生产最佳实践:安全

Production Best Practices: Security

概述

Overview

术语 “production” 指的是软件生命周期中的一个阶段,此时应用或 API 通常可供其终端用户或消费者使用。相比之下,在 “development” 阶段,你仍在积极编写和测试代码,并且应用不对外开放。相应的系统环境分别称为生产环境和开发环境。

The term “production” refers to the stage in the software lifecycle when an application or API is generally available to its end-users or consumers. In contrast, in the “development” stage, you’re still actively writing and testing code, and the application is not open to external access. The corresponding system environments are known as production and development environments, respectively.

开发和生产环境的设置通常不同,要求也大相径庭。在开发中很好的东西在生产中可能是不可接受的。例如,在开发环境中,你可能需要详细记录错误以进行调试,而同样的行为在生产环境中可能会成为安全问题。在开发中,你无需担心可伸缩性、可靠性和性能,而这些问题在生产中变得至关重要。

Development and production environments are usually set up differently and have vastly different requirements. What’s fine in development may not be acceptable in production. For example, in a development environment you may want verbose logging of errors for debugging, while the same behavior can become a security concern in a production environment. And in development, you don’t need to worry about scalability, reliability, and performance, while those concerns become critical in production.

注意: If you believe you have discovered a security vulnerability in Express, please see Security Policies and Procedures.

生产中 Express 应用的安全最佳实践包括:

Security best practices for Express applications in production include:

不要使用已弃用或易受攻击的 Express 版本

Don’t use deprecated or vulnerable versions of Express

不再维护 Express 2.x 和 3.x。这些版本中的安全和性能问题不会得到修复。不要使用它们!如果你尚未升级到版本 4,请按照 迁移指南 进行操作。

Express 2.x and 3.x are no longer maintained. Security and performance issues in these versions won’t be fixed. Do not use them! If you haven’t moved to version 4, follow the migration guide.

还要确保你没有使用 安全更新页面 上列出的任何易受攻击的 Express 版本。如果是,请更新到稳定版本之一,最好是最新版本。

Also ensure you are not using any of the vulnerable Express versions listed on the Security updates page. If you are, update to one of the stable releases, preferably the latest.

使用 TLS

Use TLS

如果你的应用处理或传输敏感数据,请使用 传输层安全 (TLS) 来保护连接和数据。该技术在数据从客户端发送到服务器之前对其进行加密,从而防止一些常见(且容易)的黑客攻击。尽管 Ajax 和 POST 请求可能并不明显,并且在浏览器中看起来是 “hidden”,但它们的网络流量很容易受到 数据包嗅探中间人攻击 的影响。

If your app deals with or transmits sensitive data, use Transport Layer Security (TLS) to secure the connection and the data. This technology encrypts data before it is sent from the client to the server, thus preventing some common (and easy) hacks. Although Ajax and POST requests might not be visibly obvious and seem “hidden” in browsers, their network traffic is vulnerable to packet sniffing and man-in-the-middle attacks.

你可能熟悉安全套接字层 (SSL) 加密。TLS 只是 SSL 的下一步。换句话说,如果你以前使用过 SSL,请考虑升级到 TLS。一般来说,我们推荐 Nginx 来处理 TLS。有关在 Nginx(和其他服务器)上配置 TLS 的良好参考,请参阅 推荐的服务器配置 (Mozilla Wiki)

You may be familiar with Secure Socket Layer (SSL) encryption. TLS is simply the next progression of SSL. In other words, if you were using SSL before, consider upgrading to TLS. In general, we recommend Nginx to handle TLS. For a good reference to configure TLS on Nginx (and other servers), see Recommended Server Configurations (Mozilla Wiki).

此外,获得免费 TLS 证书的便捷工具是 让我们加密,它是 互联网安全研究组 (ISRG) 提供的免费、自动化和开放的证书颁发机构 (CA)。

Also, a handy tool to get a free TLS certificate is Let’s Encrypt, a free, automated, and open certificate authority (CA) provided by the Internet Security Research Group (ISRG).

使用 Helmet

Use Helmet

Helmet 可以通过适当设置 HTTP 标头来帮助保护你的应用免受一些众所周知的 Web 漏洞的影响。

Helmet can help protect your app from some well-known web vulnerabilities by setting HTTP headers appropriately.

Helmet 是几个较小的中间件函数的集合,它们设置与安全相关的 HTTP 响应标头。一些例子包括:

Helmet is a collection of several smaller middleware functions that set security-related HTTP response headers. Some examples include:

Helmet 还包含其他几个中间件函数,你可以阅读有关 在其文档网站上 的内容。

Helmet includes several other middleware functions which you can read about at its documentation website.

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

Install Helmet like any other module:

$ npm install --save helmet

然后在你的代码中使用它:

Then to use it in your code:

// ...

const helmet = require('helmet')
app.use(helmet())

// ...

减少指纹

Reduce Fingerprinting

它可以帮助提供额外的安全层以减少服务器指纹识别。虽然本身不是安全问题,但改善 Web 服务器整体状况的一种方法是采取措施降低对服务器上使用的软件进行指纹识别的能力。服务器软件可以通过 kwirks 识别它们如何响应特定请求。

It can help to provide an extra layer of obsecurity to reduce server fingerprinting. Though not a security issue itself, a method to improve the overall posture of a web server is to take measures to reduce the ability to fingerprint the software being used on the server. Server software can be fingerprinted by kwirks in how they respond to specific requests.

默认情况下,Express.js 发送 X-Powered-By 响应标头横幅。这可以使用 app.disable() 方法禁用:

By default, Express.js sends the X-Powered-By response header banner. This can be disabled using the app.disable() method:

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

注意: Disabling the X-Powered-By header does not prevent a sophisticated attacker from determining that an app is running Express. It may discourage a casual exploit, but there are other ways to determine an app is running Express.

Express.js 还发送它自己的格式化 404 Not Found 消息和自己的格式化程序错误响应消息。这些可以由 添加你自己的未找到处理程序编写自己的错误处理程序 更改:

Express.js also sends it’s own formatted 404 Not Found messages and own formatter error response messages. These can be changed by adding your own not found handler and writing your own error handler:

// last app.use calls right before app.listen():

// custom 404
app.use((req, res, next) => {
  res.status(404).send("Sorry can't find that!")
})

// custom error handler
app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

安全地使用 cookie

Use cookies securely

为确保 cookie 不会打开你的应用进行攻击,请不要使用默认会话 cookie 名称并适当设置 cookie 安全选项。

To ensure cookies don’t open your app to exploits, don’t use the default session cookie name and set cookie security options appropriately.

有两个主要的中间件 cookie 会话模块:

There are two main middleware cookie session modules:

这两个模块之间的主要区别在于它们如何保存 cookie 会话数据。express-session 中间件在服务器上存储会话数据;它只将会话 ID 保存在 cookie 本身中,而不是会话数据。默认情况下,它使用内存存储,并非为生产环境设计。在生产中,你需要设置一个可扩展的会话存储;见 兼容会话存储 名单。

The main difference between these two modules is how they save cookie session data. The express-session middleware stores session data on the server; it only saves the session ID in the cookie itself, not session data. By default, it uses in-memory storage and is not designed for a production environment. In production, you’ll need to set up a scalable session-store; see the list of compatible session stores.

相比之下,cookie-session 中间件实现了 cookie 支持的存储:它将整个会话序列化为 cookie,而不仅仅是会话键。仅当会话数据相对较小且易于编码为原始值(而不是对象)时才使用它。虽然浏览器应该支持每个 cookie 至少 4096 字节,但为确保不超过限制,每个域的大小不要超过 4093 字节。另外,请注意 cookie 数据将对客户端可见,因此如果有任何理由保证其安全或隐蔽,那么 express-session 可能是更好的选择。

In contrast, cookie-session middleware implements cookie-backed storage: it serializes the entire session to the cookie, rather than just a session key. Only use it when session data is relatively small and easily encoded as primitive values (rather than objects). Although browsers are supposed to support at least 4096 bytes per cookie, to ensure you don’t exceed the limit, don’t exceed a size of 4093 bytes per domain. Also, be aware that the cookie data will be visible to the client, so if there is any reason to keep it secure or obscure, then express-session may be a better choice.

Don’t use the default session cookie name

使用默认会话 cookie 名称会使你的应用容易受到攻击。提出的安全问题类似于 X-Powered-By:潜在的攻击者可以使用它来对服务器进行指纹识别并相应地进行攻击。

Using the default session cookie name can open your app to attacks. The security issue posed is similar to X-Powered-By: a potential attacker can use it to fingerprint the server and target attacks accordingly.

为避免此问题,请使用通用的 cookie 名称;例如使用 express-session 中间件:

To avoid this problem, use generic cookie names; for example using express-session middleware:

const session = require('express-session')
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 's3Cur3',
  name: 'sessionId'
}))

Set cookie security options

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

Set the following cookie options to enhance security:

下面是一个使用 cookie-session 中间件的例子:

Here is an example using cookie-session middleware:

const session = require('cookie-session')
const express = require('express')
const app = express()

const expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
  name: 'session',
  keys: ['key1', 'key2'],
  cookie: {
    secure: true,
    httpOnly: true,
    domain: 'example.com',
    path: 'foo/bar',
    expires: expiryDate
  }
}))

防止针对授权的暴力攻击

Prevent brute-force attacks against authorization

确保登录端点受到保护,以使私有数据更加安全。

Make sure login endpoints are protected to make private data more secure.

一种简单而强大的技术是使用两个指标来阻止授权尝试:

A simple and powerful technique is to block authorization attempts using two metrics:

  1. 第一个是同一用户名和 IP 地址连续尝试失败的次数。

    The first is number of consecutive failed attempts by the same user name and IP address.

  2. 第二个是在很长一段时间内来自 IP 地址的失败尝试次数。例如,如果某 IP 地址在一天内尝试 100 次失败,则阻止该 IP 地址。

    The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day.

rate-limiter-flexible 包提供了使这项技术简单快捷的工具。你可以找到 文档中的暴力保护示例

rate-limiter-flexible package provides tools to make this technique easy and fast. You can find an example of brute-force protection in the documentation

确保你的依赖是安全的

Ensure your dependencies are secure

使用 npm 管理应用的依赖非常强大且方便。但是你使用的软件包可能包含严重的安全漏洞,这些漏洞也会影响你的应用。你的应用的安全性仅与依赖中的 “最脆弱的链接” 一样强。

Using npm to manage your application’s dependencies is powerful and convenient. But the packages that you use may contain critical security vulnerabilities that could also affect your application. The security of your app is only as strong as the “weakest link” in your dependencies.

从 npm@6 开始,npm 会自动审查每个安装请求。你也可以使用 ‘npm audit’ 来分析你的依赖树。

Since npm@6, npm automatically reviews every install request. Also you can use ‘npm audit’ to analyze your dependency tree.

$ npm audit

如果你想保持更安全,请考虑 Snyk

If you want to stay more secure, consider Snyk.

Snyk 提供 命令行工具Github 整合,用于根据 Snyk 的开源漏洞数据库 检查你的应用是否存在依赖中的任何已知漏洞。按如下方式安装 CLI:

Snyk offers both a command-line tool and a Github integration that checks your application against Snyk’s open source vulnerability database for any known vulnerabilities in your dependencies. Install the CLI as follows:

$ npm install -g snyk
$ cd your-app

使用此命令测试你的应用是否存在漏洞:

Use this command to test your application for vulnerabilities:

$ snyk test

使用此命令打开一个向导,引导你完成应用更新或补丁以修复所发现漏洞的过程:

Use this command to open a wizard that walks you through the process of applying updates or patches to fix the vulnerabilities that were found:

$ snyk wizard

避免其他已知漏洞

Avoid other known vulnerabilities

请留意可能影响 Express 或你的应用使用的其他模块的 Node 安全项目Snyk 公告。总的来说,这些数据库是有关 Node 安全的知识和工具的极好资源。

Keep an eye out for Node Security Project or Snyk advisories that may affect Express or other modules that your app uses. In general, these databases are excellent resources for knowledge and tools about Node security.

最后,Express 应用 - 像任何其他网络应用一样 - 可能容易受到各种基于 Web 的攻击。熟悉已知的 网络漏洞 并采取预防措施避免它们。

Finally, Express apps - like any other web apps - can be vulnerable to a variety of web-based attacks. Familiarize yourself with known web vulnerabilities and take precautions to avoid them.

其他注意事项

Additional considerations

以下是优秀的 Node.js 安全检查表 的一些进一步建议。有关这些建议的所有详细信息,请参阅该博客文章:

Here are some further recommendations from the excellent Node.js Security Checklist. Refer to that blog post for all the details on these recommendations: