HTTPS 开发:实用指南
根据 Firefox Telemetry 的数据,76% 的网页都使用 HTTPS 加载,而且这个数字还在增长。
软件工程师迟早要处理 HTTPS,而且越早越好。继续阅读,了解为什么以及如何在开发环境中使用 HTTPS 服务 JavaScript 应用程序。
为什么在开发环境中使用 HTTPS?
首先,你到底应不应该在生产环境中使用 HTTPS 服务网站?除非你真的知道自己在做什么,否则默认答案是肯定的。HTTPS 可以在很多层面提升你的网站:安全性、性能、SEO 等等。
如何设置 HTTPS 通常在首次发布时就已解决,但这也带来了许多其他问题。流量应该从头到尾加密吗?还是加密到反向代理就足够了?证书应该如何生成?证书应该存储在哪里?HSTS又该如何呢?
开发团队应该能够尽早回答这些问题。如果做不到这一点,最终可能会像 Stack Overflow 一样浪费大量时间。
此外,将开发环境尽可能靠近生产环境可以降低错误进入生产环境的风险,并减少调试这些错误的时间。这对于端到端测试也是如此。
此外,还有一些功能仅在通过 HTTPS 提供服务的页面上有效,例如Service Workers。
但是 HTTPS 很慢!很多人认为加密很复杂,而且从某种程度上来说,为了高效,速度必然很慢。但有了现代硬件和协议,这种想法已经不再成立了。
如何为开发环境生成有效证书?
对于生产系统,获取TLS证书很容易:从Let's Encrypt生成一个或从付费提供商处购买一个。
对于开发环境来说,这似乎比较棘手,但其实并不难。
Mkcert:无需思考的 CLI
Filippo Valsorda最近发布了mkcert
一个简单的命令行工具,用于生成本地信任的开发证书。你只需运行一行命令即可:
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 环境中的端到端测试。更好的解决方案是创建您自己的本地证书颁发机构,将此自定义颁发机构添加到您的浏览器中,并从中生成证书。
这就是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的 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
)。
编辑: dev.to 的读者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 捆绑在一起,那么使用 HTTPS 服务就和使用 HTTPSwebpack-dev-server
一样简单!你可以在 Webpack 配置中使用两行代码来配置自定义 HTTPS 证书:create-react-app
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。难怪这两个库的名字都与速度有关,因为它们的设置速度很快!
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.')
});
HTTP/2 并非提供 HTTPS 服务的必要条件,可以使用 HTTP 开头的协议来提供加密内容。不过,在提供 HTTPS 服务的同时,我们可以升级 HTTP 协议。如果您想了解更多关于 HTTP/2 的优势,可以阅读这篇快速常见问题解答。
结论
现代工具能够构建对最终用户更安全、更快速且易于引导的应用程序。我希望我能够说服您从项目伊始就使用这些库和技术,因为它们的安装成本仍然很低。
本文中使用的所有示例都收集在以下仓库中:marmelab/https-on-dev。欢迎大家自由尝试,并添加您自己的 HTTPS 开发经验!
文章来源:https://dev.to/kmaschta/https-in-development-a-practical-guide-175m