概述

术语“生产”表示软件生命周期中应用程序或 API 可供最终用户或使用者正常使用的阶段。而在“开发”阶段中,您仍然积极编写和测试代码,应用程序并未对外部开发访问。对应的系统环境分别被称为生产开发环境。
开发和生产环境的设置通常不同,需求也天差地别。在开发环境中没问题的做法在生产环境中可能就无法接受。例如,在开发环境中,可能需要对错误进行详细日志记录以便进行调试,而同一行为在生产环境中可能会成为安全问题。在开发环境中,无需担心可扩展性、可靠性和性能,而这些方面在生产环境中就是关键问题。
本文讨论了部署到生产环境的 Express 应用程序的一些安全最佳实践。

请勿使用不推荐或者存在漏洞的 Express 版本

Express 2.x 和 3.x 不再得到维护。不会纠正这些版本中的安全问题和性能问题。请勿使用这些版本!如果您尚未迁移到 V4,请按照迁移指南进行迁移。
还请确保您未使用安全性更新页面中列出的任何存在漏洞的 Express 版本。如果在使用,请更新到某个稳定发行版,首选为最新版本。

使用 TLS

如果应用程序处理或传输敏感数据,请使用传输层安全性 (TLS) 来保护连接和数据。这种技术用于加密数据,然后将其从客户机发送到服务器,以防止某些常见的(而且容易的)黑客攻击。虽然 Ajax 和 POST 请求可能不是很明显,似乎“隐藏”在浏览器中,但是其网络流量很容易受到包嗅探攻击和中间人攻击
您可能很熟悉安全套接字层 (SSL) 加密。TLS 就是下一代的 SSL。换言之,如果您以前使用 SSL,请考虑升级到 TLS。一般而言,我们建议使用 Nginx 来处理 TLS。要获取在 Nginx(和其他服务器)上配置 TLS 的优秀参考信息,请参阅 Recommended Server Configurations (Mozilla Wiki)。
此外,可以使用一种方便的 Let’s Encrypt 工具来获取免费的 TLS 证书,这是由因特网安全研究组 (ISRG) 提供的免费、自动化的开放式认证中心 (CA)。

使用 Helmet

Helmet 通过适当地设置 HTTP 头,帮助您保护应用程序避免一些众所周知的 Web 漏洞。
Helmet 实际上只使用以下九个较小中间件函数的集合,这些功能用于设置与安全相关的 HTTP 头:

  • csp 用于设置 Content-Security-Policy 头,帮助抵御跨站点脚本编制攻击和其他跨站点注入攻击。
  • hidePoweredBy 用于移除 X-Powered-By 头。
  • hpkp 用于添加公用密钥固定头,防止以伪造证书进行的中间人攻击。
  • hsts 用于设置 Strict-Transport-Security 头,实施安全的服务器连接 (HTTP over SSL/TLS)。
  • ieNoOpen 用于为 IE8+ 设置 X-Download-Options
  • noCache 用于设置 Cache-Control 和 Pragma 头,以禁用客户端高速缓存。
  • noSniff 用于设置 X-Content-Type-Options,以防止攻击者以 MIME 方式嗅探浏览器发出的响应中声明的 content-type。
  • frameguard 用于设置 X-Frame-Options 头,提供 clickjacking 保护。
  • xssFilter 用于设置 X-XSS-Protection,在最新的 Web 浏览器中启用跨站点脚本编制 (XSS) 过滤器。

像安装其他模块一样安装 Helmet:

  1. $ npm install --save helmet

然后将其用于您的代码:

  1. ...
  2. var helmet = require('helmet');
  3. app.use(helmet());
  4. ...

至少禁用 X-Powered-By 头

如果不希望使用 Helmet,那么至少应禁用 X-Powered-By 头。攻击者可能会使用该头(缺省情况下已启用)来检测运行 Express 的应用程序,然后发动针对特定目标的攻击。
所以,最佳实践是使用 app.disable() 方法禁用此头:

  1. app.disable('x-powered-by');

如果使用 helmet.js,它会为您执行此功能。

安全地使用 cookie

要确保 cookie 不会打开应用程序使其暴露在风险之中,请勿使用缺省会话 cookie 名称并相应地设置 cookie 安全选项。
有两个主要的中间件 cookie 会话模块:

  • express-session,用于替换 Express 3.x 内置的 express.session 中间件。
  • cookie-session,用于替换 Express 3.x 内置的 express.cookieSession 中间件。

这两个模块之间的主要差异是它们保存 cookie 会话数据的方式。express-session 中间件将会话数据存储在服务器上;它仅将会话标识(而非会话数据)保存在 cookie 中。缺省情况下,它使用内存中存储,并不旨在用于生产环境。在生产环境中,需要设置可扩展的会话存储;请参阅兼容的会话存储列表。
相反,cookie-session 中间件实现 cookie 支持的存储:它将整个会话序列化到 cookie 中,而不只是会话键。仅当会话数据相对较小,且易于编码为原语值(而不是对象)时,才使用此中间件。尽管假设浏览器支持每个 cookie 至少 4096 字节以确保不会超过限制,但是每个域的大小不会超过 4093 字节。另外请注意,客户机可以访问 cookie 数据,所以,如果有任何理由将其保持安全或隐藏状态,那么 express-session 可能是更好的选择。

请勿使用缺省会话 cookie 名称

使用缺省会话 cookie 名称会使应用程序对攻击敞开大门。X-Powered-By 之类的值会造成问题:潜在攻击者可以利用这样的值对服务器采集“指纹”,并相应地确定攻击目标。
为避免此问题,请使用通用 cookie 名称;例如,使用 express-session 中间件:

  1. var session = require('express-session');
  2. app.set('trust proxy', 1) // trust first proxy
  3. app.use( session({
  4. secret : 's3Cur3',
  5. name : 'sessionId',
  6. })
  7. );

设置 cookie 安全性选项

设置以下 cookie 选项来增强安全性:

  • secure - 确保浏览器只通过 HTTPS 发送 cookie。
  • httpOnly - 确保 cookie 只通过 HTTP(S)(而不是客户机 JavaScript)发送,这有助于防御跨站点脚本编制攻击。
  • domain - 表示 cookie 的域;用于和请求 URL 的服务器的域进行比较。如果匹配,那么接下来检查路径属性。
  • path - 表示 cookie 的路径;用于和请求路径进行比较。如果路径和域都匹配,那么在请求中发送 cookie。
  • expires - 用于为持久性 cookie 设置到期日期。

以下是使用 cookie-session 中间件的示例:

  1. var session = require('cookie-session');
  2. var express = require('express');
  3. var app = express();
  4. var expiryDate = new Date( Date.now() + 60 * 60 * 1000 ); // 1 hour
  5. app.use(session({
  6. name: 'session',
  7. keys: ['key1', 'key2'],
  8. cookie: { secure: true,
  9. httpOnly: true,
  10. domain: 'example.com',
  11. path: 'foo/bar',
  12. expires: expiryDate
  13. }
  14. })
  15. );

确保依赖项的安全

在管理应用程序的依赖项方面,npm 功能非常强大,而且使用方便。但是,您使用的软件包可能包含严重的安全漏洞,也可能会对应用程序产生影响。应用程序的安全取决于依赖项中“最弱”的一环。
可以使用以下的任一或全部两种工具,帮助确保所使用的第三方软件包的安全性:nsprequireSafe。这两种工具的功能大体相同。
nsp 是一种命令行工具,用于检查 Node 安全项目漏洞数据库,确定应用程序是否使用具有已知漏洞的软件包。可通过以下命令安装该工具:

  1. $ npm i nsp -g

使用以下命令将 npm-shrinkwrap.json 文件提交至 nodesecurity.io 以进行验证:

  1. $ nsp audit-shrinkwrap

使用以下命令将 package.json 文件提交至 nodesecurity.io 以进行验证:

  1. $ nsp audit-package

以下示例说明如何使用 requireSafe 来审计 Node 模块:

  1. $ npm install -g requiresafe
  2. $ cd your-app
  3. $ requiresafe check

其他注意事项

以下是来自非常出色的 Node.js 安全核对表的一些进一步建议。请参阅此博客帖子以了解关于这些建议的所有详细信息:

  • 实施速率限制,防止针对认证的暴力攻击。实现这一点的一种方式是使用 StrongLoop API 来强制实施速率限制策略。或者,可以使用诸如 express-limiter 的中间件,但是这样做需要对代码作些修改。
  • 使用 csurf 中间件来防御跨站点请求伪造 (CSRF)。
  • 始终过滤和净化用户输入,防御跨站点脚本编制 (XSS) 和命令注入攻击。
  • 使用参数化查询或预编译的语句来防御 SQL 注入攻击。
  • 使用开源的 sqlmap 工具来检测应用程序中的 SQL 注入漏洞。
  • 使用 nmapsslyze 工具来测试 SSL 密码、密钥和重新协商的配置以及证书的有效性。
  • 使用 safe-regex 来确保正则表达式不易受到正则表达式拒绝服务攻击。

    避免其他已知的漏洞

    关注 Node 安全项目公告,这可能会影响 Express 或应用程序使用的其他模块。一般而言,Node 安全项目是有关 Node 安全性的知识和工具的出色资源。
    最后说明一点,和任何其他 Web 应用程序一样,Express 应用程序也容易受到各种基于 Web 的攻击。请熟悉已知的 Web 漏洞并采取相应的预防措施。