HTTPS开发中:实用指南

发布于:2021-02-02 14:15:20

0

254

0

devops webdev javascript react

根据FirefoxTelemetry,76%的网页加载了HTTPS,而且这个数字还在增长。

软件工程师迟早要处理HTTPS,而且越早越好。继续阅读,了解为什么以及如何在开发环境中使用HTTPS为JavaScript应用程序提供服务。

  

为什么在开发环境中使用HTTPS?

首先,你应该通过HTTPS服务于一个生产中的网站吗?除非您真的知道自己在做什么,否则默认答案是“是”。它在很多方面改善了你的网站:安全性,性能,搜索引擎优化,等等。

如何设置HTTPS通常在第一个版本中讨论,并带来许多其他问题。通信应该从端到端加密,还是加密到反向代理就足够了?如何生成证书?它应该存放在哪里?那HSTS呢?

开发团队应该能够尽早回答这些问题。如果没有做到这一点,您可能会像堆栈溢出一样浪费大量时间。

此外,拥有一个与生产环境尽可能接近的开发环境,可以减少bug到达生产环境的风险,而且还可以减少调试这些bug的时间。端到端测试也是如此。

此外,还有一些功能只能在HTTPS服务的页面上工作,例如服务工作者。

但是HTTPS很慢!许多人认为加密很复杂,在某种程度上必须很慢才能有效。但随着现代硬件和协议的发展,这不再是事实。

如何为开发环境生成有效的证书?

对于生产系统,很容易获得TLS证书:从生成一个,让我们加密或从付费提供商购买一个。

对于开发环境,这似乎比较棘手,但并不难。

Mkcert: 简单的 CLI

菲利波·瓦尔索达(Filippo Valsorda)最近发布mkcert了一个简单的CLI,可以生成本地信任的开发证书。您只需要运行一个单行命令:

mkcert -install
mkcert example.com


完全受支持的证书将在您运行命令的位置提供,即位于./example.com-key.pem。

使用OpenSSL手动安装

mkcert除非您必须与您的同事或通过本地环境以外的其他系统共享同一证书,否则您应该满足您的所有需求。在这种情况下,您可以借助生成自己的证书openssl。  

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt


证书(server.crt)及其密钥(server.key)将是有效的但具有自签名。任何证书颁发机构都不知道该证书。但是所有浏览器都要求知名的证书颁发机构来验证证书,以接受加密的连接。对于自签名证书,他们无法对其进行验证,因此显示烦人的警告:

 

您可以接受这种不便之处,并在每次警告出现时手动忽略它。但这非常麻烦,并且可能会阻止CI环境中的e2e测试。更好的解决方案是创建您自己的本地证书颁发机构,将此自定义证书颁发机构添加到浏览器并从中生成证书。

这就是mkcert您的内幕,但是如果您想自己做,我写了一个要帮助您的要点:Kmaschta / 205a67e42421e779edd3530a0efe5945。

来自反向代理或第三方应用程序的HTTPS

通常,最终用户不会直接访问应用程序服务器。而是由负载平衡器或反向代理处理用户请求,该负载均衡器或反向代理将请求分布在后端,存储缓存,防止有害请求,等等。看到这些代理同时充当解密请求和加密响应的角色并不少见。

在开发环境中,我们也可以使用反向代理!

通过Traefik和Docker Compose进行加密

Traefik是反向代理,对开发人员来说具有很多优势。其中,配置简单,并且带有GUI。另外,在docker hub上有一个官方的docker镜像。

因此,让我们在docker-compose.yml仅提供静态文件的假设应用程序内部使用它:

version: '3.4'

services:
   reverse-proxy:
       image: traefik # The official Traefik docker image
       command: --docker --api # Enables the web UI and tells Traefik to listen to docker
       ports:
           - '3000:443'  # Proxy entrypoint
           - '8000:8080' # Dashboard
       volumes:
           - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
           - ./certs/server.crt:/sslcerts/server.crt
           - ./certs/server.key:/sslcerts/server.key
           - ./traefik.toml:/traefik.toml # Traefik configuration file (see below)
       labels:
           - 'traefik.enable=false'
       depends_on:
           - static-files
   static-files:
       image: halverneus/static-file-server
       volumes:
           - ./static:/web
       labels:
           - 'traefik.enable=true'
           - 'traefik.frontend.rule=Host:localhost'
           - 'traefik.port=8080'
           - 'traefik.protocol=http'
       ports:
           - 8080:8080

在此示例中,我们的静态文件服务器侦听端口8080并以HTTP提供文件。此配置告诉Traefik处理HTTPS请求https://localhost并代理每个请求,http://localhost:8080以提供静态文件。  

我们还必须添加一个traefik.toml以配置Traefik入口点:

debug = false

logLevel = "ERROR"
defaultEntryPoints = ["https","http"]

[entryPoints]
 [entryPoints.http]
 address = ":80"
   [entryPoints.http.redirect]
   entryPoint = "https"
 [entryPoints.https]
 address = ":443"
 [entryPoints.https.tls]
     [[entryPoints.https.tls.certificates]]
     certFile = "/sslcerts/server.crt"
     keyFile = "/sslcerts/server.key"

在这里,我们有两个入口点:http和https,分别侦听端口80和443。第一个重定向到HTTPS,第二个通过指定的TLS证书配置为加密请求。

通过Nginx从Docker Compose加密

显然,我们可以使用流行的Nginx反向代理来做完全相同的事情。由于Nginx本身也可以直接提供静态文件,因此设置更加简单。同样,第一步是docker-compose.yml:

version: '3'

services:
   web:
       image: nginx:alpine
       volumes:
           - ./static:/var/www
           - ./default.conf:/etc/nginx/conf.d/default.conf
           - ../../certs/server.crt:/etc/nginx/conf.d/server.crt
           - ../../certs/server.key:/etc/nginx/conf.d/server.key
       ports:
           - "3000:443"

Nginx配置位于default.conf:

server {
   listen 80 default_server;
   listen [::]:80 default_server;
   return 301 https://$server_name$request_uri;
}

server {
   listen 443 ssl;

   server_name ~.;

   ssl_certificate /etc/nginx/conf.d/server.crt;
   ssl_certificate_key /etc/nginx/conf.d/server.key;

   location / {
       root /var/www;
   }

   ## If the static server was another docker service,
   ## It is possible to forward requests to its port:
   # location / {
   #     proxy_set_header Host $host;
   #     proxy_set_header X-Real-IP $remote_addr;
   #     proxy_pass http://web:3000/;
   # }
}

直接从应用程序提供HTTPS服务

有时,安全性要求需要端到端加密,或者在开发环境中使用反向代理似乎显得过大。大多数时候,有可能直接从您的日常开发环境中提供HTTPS服务。

让我们以一个常见的堆栈为例:一个带有Express API的带有REST API的React应用程序。

使用Create React App或Webpack Dev Server

您的平均React应用程序由引导create-react-app。这个很棒的工具带有许多内置功能,可以开箱即用地处理HTTPS。为此,您只需要HTTPS=true在启动应用程序时指定一个环境变量:

HTTPS=true npm run start
# or
HTTPS=true yarn start

此命令将通过https://localhost:3000而不是http://localhost:3000自动生成的证书为您的应用提供服务。但这是一个自签名证书,因此开发人员体验很差。

如果您想使用自己的HTTPS证书(使用浏览器信任的权限签名),create-react-app则在不弹出应用程序(npm run eject)的情况下不允许您对其进行配置。

编辑:面向读者的Zwerge发现了一个聪明的解决方法,可以即时替换默认的HTTPS证书:

 "scripts": {
   "prestart": "(cat ../../certs/server.crt ../../certs/server.key > ./node_modules/webpack-dev-server/ssl/server.pem) || :",
   "start": "react-scripts start",
 },

幸运的是,如果您确实弹出CRA,或者您的项目与webpack捆绑在一起,webpack-dev-server则就像create-react-app提供HTTPS一样简单!可以在Webpack配置中使用两行来配置自定义HTTPS证书:

const fs = require('fs');
const path = require('path');

module.exports = {
   mode: 'production',
   // ...
   devServer: {
       https: {
           key: fs.readFileSync(path.resolve(__dirname, '../../certs/server.key')),
           cert: fs.readFileSync(path.resolve(__dirname, '../../certs/server.crt')),
       },
       port: 3000,
   },
};

下次运行时webpack-dev-server,它将处理对的HTTPS请求https://localhost:3000。

 

使用Express和SPDY加密的HTTP / 2

现在我们有了通过HTTPS服务的应用程序的前端部分,我们必须对后端进行相同的操作。

为此,让我们使用express和spdy。难怪这两个库的名称为何与SPEED有关,这是因为它们设置起来很快!

const fs = require('fs');
const path = require('path');
const express = require('express');
const spdy = require('spdy');

const CERTS_ROOT = '../../certs/';

const app = express();

app.use(express.static('static'));

const config = {
   cert: fs.readFileSync(path.resolve(CERTS_ROOT, 'server.crt')),
   key: fs.readFileSync(path.resolve(CERTS_ROOT, 'server.key')),
};

spdy.createServer(config, app).listen(3000, (err) => {
   if (err) {
       console.error('An error occured', error);
       return;
   }

   console.log('Server listening on https://localhost:3000.')
});

使用HTTPS不需要HTTP / 2,可以使用名称为HTTP的加密内容来服务,但是当我们使用HTTPS时,可以升级HTTP协议。如果您想进一步了解HTTP / 2的优势,可以阅读此快速FAQ。

结论

现代化的工具允许构建对于最终用户而言更安全,更快速,并且现在易于引导的应用程序。我希望我说服您从项目开始时就开始使用这些库和技术,因为它们的安装成本仍然很低。