何时以及如何在你的本地开发环境中使用 HTTPS

2,475 阅读9分钟

翻译:布兰 作者:Maud Nalpas 阅读原文:when how

在这篇文章中,关于本地主机的语句也适用于 127.0.0.1[::1],因为它们都描述了本地计算机地址,也称为“回送地址”。另外,为简单起见,未指定端口号。因此,当你看到 http://localhost 时,将其读取为 http://localhost:{PORT}http://127.0.0.1:{PORT}

如果你的生产网站使用 HTTPS,那么你会希望本地开发站点的表现也会和 HTTPS 站点一样。在大多数情况下,你可以相信 http://localhost 的行为类似于 HTTPS 站点。但是在某些情况下,你需要使用 HTTPS 在本地运行站点。

所以本文将针对 2 个问题展开:

  1. 何时需要在本地开发环境中使用 HTTPS?
  2. 如何在本地开发环境中使用 HTTPS?

何时需要在本地开发环境中使用 HTTPS

在本地开发时,默认情况下使用 http://localhostService Workers, Web 认证 API, 以及一些别的等都可以工作。然而,在以下情况下,你需要使用 HTTPS 进行本地开发:

  • 在所有浏览器中以一致的方式设置安全 cookie。你需要在本地设置一个 Secure 或 SameSite: none 或具有 __Host 前缀的 cookie。 对于所有浏览器,仅在 HTTPS上 设置安全 cookie,而不在 http://localhost 上设置安全 cookie。并且由于 SameSite: none__Host 也要求 cookie 是安全的,因此在本地开发站点上设置此类 cookie 也需要 HTTPS。

    在本地设置安全cookie时,并非所有浏览器的行为都相同! 例如,Chrome和Safari不在本地主机上设置安全cookie,但Firefox设置了。 在Chrome中,这被视为错误。

  • 调试混合内容问题。你需要在本地调试仅在 HTTPS 网站上发生的问题,而不是在 HTTP 网站上,甚至在 http://localhost 上都不会发生,例如混合内容问题

  • 使用 HTTP/2 和更高版本。

  • 使用需要 HTTPS 的第三方库或 API,比如 OAuth;

  • 你使用的不是本地主机,而是本地开发的自定义主机名,例如 mysite.example。通常,这意味着你已经覆盖了本地主机文件。在这种情况下,即使 Chrome,Edge,Safari 和 Firefox 是本地站点,默认情况下也不认为 mysite.example 是安全的。 因此,它的行为不会像 HTTPS 站点那样。

何时使用 HTTPS 进行本地开发

如何在本地开发环境中使用 HTTPS

你可能会遇到一些特殊情况,比如 http://localhost 网站的行为不像 HTTPS 网站,或者你可能只是想使用一个不是 http://localhost 网站的自定义网站名称。

推荐使用 mkcert

mkcert 地址:github.com/FiloSottile…

要将 HTTPS 与你的本地开发站点一起使用并访问 https://localhosthttps://mysite.example(自定义主机名),你需要 TLS 证书。但是浏览器不会仅仅认为任何证书有效:你的证书需要由浏览器信任的实体(称为受信任的证书颁发机构(CA))签名。

而你需要做的就是创建一份证书,并使用你的设备和浏览器在本地信任的 CA 对其进行签名。mkcert 是一个可以帮助你通过一些命令来完成此任务的工具。运作方式如下:

  • 如果你使用 HTTPS 在浏览器中打开本地运行站点,你的浏览器将检查本地开发服务器的证书;
  • 当看到证书已经由 mkcert 生成的证书颁发机构签名时,浏览器检查它是否注册为受信任的证书颁发机构;
  • mkcert 被列为受信任的权威,因此浏览器信任该证书并创建一个 HTTPS 连接。

mkcert 工作原理图

mkcert (及其类似的工具)提供了几个好处:

  • mkcert 专门用于创建符合浏览器认为有效的证书,它保持更新以满足需求和最佳实践。这就是为什么你不必使用复杂的配置或参数运行 mkcert 命令来生成正确的证书;
  • mkcert 是一个跨平台的工具,团队中的任何人都可以使用它。

mkcert 是我们推荐的用于为本地开发创建 TLS 证书的工具。你也可以查看其他选项。

许多操作系统可能包含用于产生证书的库,比如 openssl。与 mkcert 和类似的工具不同,这些库可能不能始终生成正确的证书,可能需要运行复杂的命令,而且不一定是跨平台的。

注意事项:

  • 不要导出或共享文件 rootCA-key.pem,因为当你在执行 mkcert -install 的时候,它会自动创建;攻击者获得这个文件可以为你访问的任何站点创建路径上的攻击。他们可以拦截从你的机器发送到任何网站的安全请求,比如你的银行、医疗服务提供商或社交网络。如果你想知道它被放到哪里才是安全的,可以执行 mkcert -CAROOT
  • mkcert 仅仅只是被我们用于开发环境,切不可让最终用户去使用 mkcert 命令;
  • 所有团队成员都应该独自安装和运行 mkcert 命令(不能够存储或者共享 CA 和 证书)。

设置

  1. 安装 mkcert(仅需一次):按照说明在你的操作系统上安装 mkcert:

    brew install mkcert
    
  2. 将 mkcert 添加到本地根 CA 中,在终端运行如下命令:

    mkcert -install
    

    这将生成一个本地证书颁发机构(CA)。你的 mkcert 生成的本地 CA 在你的设备上仅受本地信任。

  3. 为你的站点生成一个由 mkcert 签名的证书。在终端中,导航到站点的根目录或者你希望证书位于的任何目录,然后执行:

    mkcert localhost
    

    如果你使用一个自定义的主机名,比如 mysite.example,运行:

    mkcert mysite.example
    

    上面的命令做了两件事:

    • 为指定的主机名生成证书;
    • 让 mkcert (在步骤2中添加为本地 CA)签署此证书。

    到这步为止,你的证书已经就绪,并由浏览器本地信任的证书颁发机构签名。你差不多完成了,但是你的服务器还不知道你的证书!

  4. 配置你的服务器。现在需要告诉服务器使用 HTTPS (因为开发服务器默认使用 HTTP)并使用刚刚创建的 TLS 证书。如何做到这一点完全取决于你的服务器:

    • 使用 Node 处理:使用 server.js 替换 {PATH/TO/CERTIFICATE...}{PORT}

      const https = require('https');
      const fs = require('fs');
      const options = {
          key: fs.readFileSync('{PATH/TO/CERTIFICATE-KEY-FILENAME}.pem'),
          cert: fs.readFileSync('{PATH/TO/CERTIFICATE-FILENAME}.pem'),
      };
      https
          .createServer(options, function (req, res) {
              // server code
          })
          .listen({PORT});
      
    • 使用 http-server。按照以下方式启动服务器替换 {PATH/TO/CERTIFICATE...}

      // - s 使用 HTTPS 运行服务器,-c 设置证书,-k 设置密钥
      http-server -S -C {PATH/TO/CERTIFICATE-FILENAME}.pem -K {PATH/TO/CERTIFICATE-KEY-FILENAME}.pem
      
    • 使用 React 开发服务器。按如下方式编辑 package.json,并替换 {PATH/TO/CERTIFICATE... }

      "scripts": {
          "start": "HTTPS=true SSL_CRT_FILE={PATH/TO/CERTIFICATE-FILENAME}.pem SSL_KEY_FILE={PATH/TO/CERTIFICATE-KEY-FILENAME}.pem react-scripts start"
      

      例如,如果你已经创建了位于站点根目录中的 localhost 证书,如下所示:

      |-- my-react-app
          |-- package.json
          |-- localhost.pem
          |-- localhost-key.pem
          |--...
      

      那么你的开始脚本应该是这样的:

      "scripts": {
          "start": "HTTPS=true SSL_CRT_FILE=localhost.pem SSL_KEY_FILE=localhost-key.pem react-scripts start"
      
  5. OK,你已经完成配置了。在你的浏览器中打开 https://localhost 或者 https://mysite.example 你使用 HTTPS 在本地运行你的网站。你不会看到任何浏览器警告,因为你的浏览器将 mkcert 信任为本地证书颁发机构。

自签名证书

你还可以决定不使用像 mkcert 这样的本地证书颁发机构,而是自己签署证书。

不过,得小心这种方法的几个缺陷:

  • 浏览器不信任你作为证书颁发机构,它们会显示你需要手动绕过的警告。在 Chrome 中,你可以使用这个标志 #allow-insecure-localhost,自动绕过这个警告;
  • 如果你在不安全的网络中工作,这是不安全的;
  • 自签名证书的行为方式与受信任证书不完全相同;
  • 这并不一定比使用像 mkcert 这样的本地 CA 更容易或更快;
  • 如果不在浏览器上下文中使用此技术,则可能需要禁用服务器的证书验证。忽略在生产中重新启用它将是危险的。

当使用自签名证书时,会显示警告浏览器

为什么浏览器不相信自签名证书

如果你在浏览器中使用 HTTPS 打开本地运行站点,你的浏览器将检查本地开发服务器的证书。当它看到证书是你自己签署的时候,它会检查你是否注册为受信任的证书颁发机构。因为你不是,所以你的浏览器不能信任证书; 它会显示一个警告,告诉你你的连接不安全。你可能会自行承担风险ーー如果你这样做,将创建一个 HTTPS 连接。

为什么浏览器不相信自签名证书

由普通证书颁发机构签署的证书

你还可以找到基于拥有一个实际的证书颁发机构(而不是本地的证书颁发机构)来签署证书的技术。

如果你正在考虑使用这些技巧,请记住以下几点:

  • 与使用像 mkcert 这样的本地 CA 技术相比,你需要做更多的安装工作;
  • 你需要使用你控制的有效域名。这意味着你不能够使用实际的证书颁发机构:
    • localhost 以及其他的保留域名,比如 example 或者 test;
    • 任何你不能控制的域名;
    • 无效的顶级域名

反向代理

使用 HTTPS 访问本地运行站点的另一个选择是使用反向代理,比如 ngrok。需要考虑的几点:

  • 任何人都可以访问你的本地开发网站,只要你与他们分享一个由反向代理创建的 URL。这可以是非常方便的时候,你的项目演示给客户!如果你的项目很敏感,这也可能是一个缺点;
  • 你可能需要考虑价格;
  • 浏览器中新的安全措施可能会影响这些工具的工作方式。