如何在Node.js中使用免费SSL/TLS证书?在本教程中,我们将通过一个实际示例,向您介绍如何将Let's Encrypt生成的免费ssl证书添加到您的Express.js服务器。
2025年的网站已经没有理由不使用HTTPS。访客对此有期待,谷歌百度将其作为排名因素,并且浏览器厂商会乐于点名批评那些不使用HTTPS的网站。
在本教程中,我将通过一个实际示例,向您介绍如何将Let's Encrypt生成的免费ssl证书添加到您的Express.js服务器。
然而,仅仅使用HTTPS保护我们的网站和应用程序还不够。我们还应该要求与我们通信的服务器建立加密连接。我们将看到,即使SSL/TLS层默认未启用,也存在激活该层的可能性。
注意: 如果您正在寻找关于在将NGINX配置为Node应用程序的反向代理时,如何使用NGINX设置SSL的说明,请查看我们的快速提示“使用Node.js配置NGINX和SSL”。
让我们首先简要回顾一下HTTPS的当前状态。
HTTPS
模块通过安全通道与客户端进行通信。这需要生成SSL证书,可以使用Let's Encrypt免费生成。Helmet
实现。HTTP/2规范于2015年5月以RFC 7540的形式发布,这意味着它目前已成为标准的一部分。这是一个重要的里程碑。现在我们都可以升级服务器以使用HTTP/2。其中最重要的一点是它向后兼容HTTP 1.1,并提供了选择其他协议的协商机制。虽然该标准没有强制加密,但目前还没有浏览器支持未加密的HTTP/2。这为HTTPS带来了又一次提升。我们最终将实现HTTPS的无处不在!
我们的技术栈实际上是什么样的?从在浏览器中运行的网站(在应用程序级别)的角度来看,我们必须遍历以下层才能到达IP级别:
HTTPS只不过是SSL/TLS之上的HTTP协议。因此,HTTP的所有规则仍然适用。这层附加协议实际上给我们带来了什么?它有诸多优势:我们可以通过密钥和证书进行身份验证;由于连接采用非对称加密,因此可以保证一定的隐私和机密性;此外,由于传输过程中数据无法更改,因此数据完整性也得到了保护。
最常见的误解之一是使用SSL/TLS计算成本高昂,并且会降低服务器速度。现在这显然已经过时了。我们也不需要任何带有加密单元的专用硬件。即使对于Google来说,SSL/TLS层也只占CPU负载的不到1%,而HTTPS的网络开销相比HTTP也低于2%。总而言之,为了一点开销而放弃HTTPS是没有意义的。
正如Ilya Grigorik所说,只有一个性能问题:
TLS 只有一个性能问题:它的使用范围不够广泛。其他方面都可以优化:https://t.co/1kH8qh89Eg
— 伊利亚·格里戈里克 (@igrigorik) 2014 年 2 月 20 日
最新版本是TLS 1.3。TLS是SSL的后继者,其最新版本是SSL 3.0。从SSL到TLS的变更阻碍了互操作性,但基本流程保持不变。我们拥有三种不同的加密通道:第一种是用于证书链的公钥基础设施;第二种是用于密钥交换的公钥加密;最后,第三种是对称加密,这里我们使用加密技术进行数据传输。
TLS 1.3在一些重要操作中使用哈希算法。理论上,可以使用任何哈希算法,但强烈建议使用SHA2或更强大的算法。SHA1长期以来一直是标准,但最近已被淘汰。
HTTPS也越来越受到客户的关注。隐私和安全问题一直存在,但随着在线可访问数据和服务的不断增长,人们的担忧也越来越强烈。对于那些尚未实现HTTPS的网站,有一个实用的浏览器扩展程序——EFF的HTTPS Everywhere——可以加密我们与大多数网站的通信。
该插件的创建者意识到许多网站仅部分支持HTTPS。该插件允许我们针对这些仅部分支持HTTPS的网站重写请求。或者,我们也可以完全阻止HTTP(参见上图)。
证书的验证过程包括验证证书签名和有效期。我们还需要验证它是否链接到受信任的根证书。最后,我们需要检查它是否已被撤销。世界上有一些专门的、值得信任的机构来颁发证书。如果其中一个机构被盗用,该机构颁发的所有其他证书都将被撤销。
HTTPS握手的序列图如下所示。首先从客户端进行初始化,然后发送包含证书和密钥交换的消息。服务器发送其完整的数据包后,客户端可以开始密钥交换和密码规范传输。至此,客户端操作完成。最后,服务器确认密码规范选择并结束握手。
整个序列独立于HTTP触发。如果我们决定使用HTTPS,则仅套接字处理会有所改变。客户端仍在发出HTTP请求,但套接字将执行前面描述的握手并加密内容(标头和正文)。
那么我们需要做什么才能使SSL/TLS与Express.js服务器一起工作?
默认情况下,Node.js通过HTTP提供内容。但为了通过安全通道与客户端进行通信,我们还必须使用 HTTPS
模块。这是一个内置模块,其用法与 HTTP
模块非常相似:
JavaScript
const https = require("https"),
fs = require("fs");
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
const app = express();
app.use((req, res) => {
res.writeHead(200);
res.end("hello world\n");
});
app.listen(8000);
https.createServer(options, app).listen(8080);
暂时忽略 /srv/www/keys/my-site-key.pem
和 /srv/www/keys/chain.pem
文件。这些是我们接下来需要生成的SSL证书。这部分正是Let's Encrypt带来的改变。以前,我们必须生成一对私钥/公钥,将其发送给可信机构,付款,然后可能还要等待一段时间才能获得SSL证书。现在,Let's Encrypt可以立即免费生成并验证您的证书!
Certbot
TLS规范要求证书由受信任的证书颁发机构(CA)签名。CA确保证书持有者的身份与其声称的身份相符。因此,当您在浏览器中看到绿色锁图标(或URL左侧任何其他绿色符号)时,意味着您正在与之通信的服务器确实与其声称的身份相符。topssl.cn上看到绿色锁图标,则几乎可以肯定您正在与网络k通信,并且没有其他人可以看到您的通信——或者更确切地说,没有其他人可以读取它。
值得注意的是,此证书不一定需要由Let's Encrypt等机构验证。还有其他付费服务。理论上,您可以自行签名,但这样一来(由于您不是受信任的CA),访问您网站的用户可能会看到一条巨大的警告信息,提示他们如何恢复安全。
在下面的示例中,我们将使用Certbot,它用于使用Let's Encrypt生成和管理证书。
在Certbot网站上,您可以找到如何在几乎所有操作系统/服务器组合上安装Certbot的说明。您应该选择适合您的选项。
部署Node应用程序的常见组合是在最新的LTS Ubuntu上使用NGINX,我将在这里使用它:
Bash
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
Webroot
Webroot是一个Certbot插件。除了Certbot的默认功能(自动生成公钥/私钥对并为其生成SSL证书)外,它还会将证书复制到您的webroot文件夹,并通过将一些验证码放入名为 .well-known
的隐藏临时目录中来验证您的服务器。为了省去手动执行某些步骤的麻烦,我们将使用此插件。该插件默认随Certbot安装。为了生成和验证我们的证书,我们将运行以下命令:
Bash
certbot certonly --webroot -w /var/www/example/ -d www.example.com -d example.com
您可能必须以 sudo
身份运行此命令,因为它将尝试写入 /var/log/letsencrypt
。
您还会被要求提供电子邮件地址。建议您输入一个经常使用的真实地址,因为如果您的证书即将到期,您会收到通知。Let's Encrypt颁发的免费证书每三个月到期一次。幸运的是,续订非常简单,只需运行一个简单的命令即可,我们可以将其分配给 cron
作业,从此无需担心证书过期。此外,续订SSL证书是一种良好的安全措施,因为它可以减少攻击者破解加密的时间。有时开发人员甚至会将此cron设置成每天运行,这完全没问题,甚至值得推荐。
请记住,您必须在 -d
(表示域名)标志下指定的域名解析到的服务器上运行此命令,也就是您的生产服务器。即使您的本地 hosts
文件中有DNS解析,此命令也无法正常工作,因为该域名将从外部进行验证。因此,如果您在本地执行此操作,则很可能会失败,除非您从本地计算机打开一个到外部世界的端口,并让该命令运行在解析到您计算机的域名后面。这种情况极不可能发生。
最后但同样重要的一点是,运行此命令后,输出将包含您的私钥和证书文件的路径。将这些值复制到前面的代码片段中——cert
属性用于证书,key
属性用于密钥:
JavaScript
// ...
const options = {
key: fs.readFileSync("/var/www/example/sslcert/privkey.pem"),
cert: fs.readFileSync("/var/www/example/sslcert/fullchain.pem") // these paths might differ for you, make sure to copy from the certbot output
};
// ...
HTTP严格传输安全 (HSTS)
您是否遇到过这样的情况:网站从HTTP切换到HTTPS后,仍然有一些重定向残留在HTTP上?HTTP严格传输安全(HSTS)是一种网络安全策略机制,用于缓解协议降级攻击和Cookie劫持。
HSTS有效地强制客户端(访问服务器的浏览器)通过HTTPS引导所有流量——这是一种“安全或完全不安全”的理念!
Express JS默认不允许我们添加此标头,因此我们将使用 Helmet
,这是一个允许我们执行此操作的Node模块。通过运行以下命令安装 Helmet
:
Bash
npm install helmet
然后我们只需将其作为中间件添加到我们的Express服务器中:
JavaScript
const https = require("https"),
fs = require("fs"),
helmet = require("helmet");
const options = {
key: fs.readFileSync("/srv/www/keys/my-site-key.pem"),
cert: fs.readFileSync("/srv/www/keys/chain.pem")
};
const app = express();
app.use(helmet()); // Add Helmet as a middleware
app.use((req, res) => {
res.writeHead(200);
res.end("hello world\n");
});
app.listen(8000);
https.createServer(options, app).listen(8080);
Diffie–Hellman 强参数
为了省去一些复杂的数学运算,我们直接切入正题。简单来说,加密使用两个不同的密钥:一个是从证书颁发机构获得的证书,另一个是由服务器生成的用于密钥交换的密钥。密钥交换的默认密钥(也称为Diffie-Hellman密钥交换,简称DH)使用的密钥比证书密钥“短”。为了解决这个问题,我们将生成一个强DH密钥,并将其提供给我们的安全服务器使用。
为了生成更长的密钥(2048位),您需要 openssl
,它可能已默认安装。如果您不确定,请运行 openssl -v
。如果找不到该命令,请运行 sudo apt install openssl
(或访问其下载页面)进行安装:
Bash
openssl dhparam -out /var/www/example/sslcert/dh-strong.pem 2048
然后将文件的路径复制到我们的配置中:
JavaScript
// ...
const options = {
key: fs.readFileSync("/var/www/example/sslcert/privkey.pem"),
cert: fs.readFileSync("/var/www/example/sslcert/fullchain.pem"), // these paths might differ for you, make sure to copy from the certbot output
dhparam: fs.readFileSync("/var/www/example/sslcert/dh-strong.pem")
};
// ...
在现在及未来,我们没有理由拒绝HTTPS。未来的行业方向清晰可见:HTTPS将无处不在!
SSL/TLS在Node.js中的重要性是什么?
SSL/TLS在Node.js中至关重要,原因有很多。首先,它在客户端和服务器之间提供安全连接,确保两者之间传输的所有数据均经过加密,不会被第三方拦截。这对于处理敏感用户数据(例如登录凭据或支付信息)的应用程序尤为重要。其次,SSL/TLS有助于与用户建立信任。当用户看到网站使用SSL/TLS保护时,他们可以确信自己的数据得到了安全处理。最后,使用SSL/TLS还可以带来SEO优势,因为搜索引擎通常会优先考虑使用安全连接的网站。
如何为Node.js生成自签名证书?
可以使用OpenSSL为Node.js生成自签名证书。首先,您需要在系统上安装OpenSSL。安装完成后,您可以使用以下命令生成自签名证书:
Bash
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
这将创建一个新的RSA密钥对和一个有效期为365天的证书。-keyout
选项指定要写入新创建的私钥的文件名,-out
选项指定证书的输出文件名。
如何将创建的SSL/TLS证书与我的Node.js服务器一起使用?
生成SSL/TLS证书后,您可以将其添加到服务器配置中,以便与Node.js服务器一起使用。这可以通过 https
模块的 createServer
方法来实现,该方法接受一个选项对象和一个请求监听器。选项对象应该包含您的证书和私钥,如下所示:
JavaScript
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
这将创建一个使用您的SSL/TLS证书的新HTTPS服务器。
SSL和TLS之间有什么区别?
SSL(安全套接字层)和TLS(传输层安全性)都是旨在通过网络提供安全通信的加密协议。虽然它们经常互换使用,但它们之间存在一些关键区别。SSL是两种协议中较早的一种,由于已知的安全漏洞,已被大量弃用。TLS是SSL的后继者,提供了更高的安全性和性能。通常,当人们提到SSL/TLS时,他们通常指的是TLS。
如何在Node.js中将HTTP流量重定向到HTTPS?
将HTTP流量重定向到HTTPS可以通过创建一个HTTP服务器来实现,该服务器监听传入连接并将其重定向到HTTPS服务器。可以使用 http
模块的 createServer
方法完成此操作,如下所示:
JavaScript
const http = require('http');
http.createServer((req, res) => {
res.writeHead(301, { 'Location': 'https://' + req.headers['host'] + req.url });
res.end();
}).listen(80);
这将创建一个新的HTTP服务器,该服务器监听端口80,并将所有传入连接重定向到等效的HTTPS URL。
如何验证我的Node.js服务器的SSL/TLS配置?
您可以使用在线工具(例如SSL Labs的SSL服务器测试)来验证Node.js服务器的SSL/TLS配置。此工具将对任何SSL Web服务器的配置进行深入分析,并提供包含服务器证书、协议支持、密码强度等信息的详细报告。
如果我在Node.js中遇到SSL/TLS错误该怎么办?
如果您在Node.js中遇到SSL/TLS错误,第一步是尝试理解错误消息。错误消息通常会提供一些线索,帮助您判断问题所在。常见问题包括证书问题(例如证书已过期或不受信任)或服务器配置问题。如果您无法自行解决问题,请考虑向Node.js社区或专业人士寻求帮助。
如何更新我的Node.js服务器上的SSL/TLS证书?
更新Node.js服务器上的SSL/TLS证书需要生成新证书(或从证书颁发机构获取证书),然后更新服务器配置以使用新证书。新证书到位后,您需要重新启动服务器才能使更改生效。
我可以使用Let's Encrypt为我的Node.js服务器获取免费的SSL/TLS证书吗?
是的,Let's Encrypt是一个免费、自动化且开放的证书颁发机构,可用于获取SSL/TLS证书。他们提供了一个名为Certbot的工具,可以自动执行证书的获取和安装过程。安装后,Certbot还可以在证书到期前自动续订。
什么是完美前向保密性以及如何在Node.js中启用它?
完美前向保密(PFS)是安全通信协议的一种特性,它允许长期密钥的泄露不会危及过去的会话密钥。这意味着即使服务器的私钥被泄露,过去的通信仍然是安全的。在Node.js中,可以通过将服务器配置为使用一组支持PFS的密码套件(例如ECDHE或DHE密钥交换算法提供的密码套件)来启用PFS。
推荐申请,价格随时改变!
{{item}}
{{item.title}}
{{items.productName}}
¥{{items.priceOne.price}}/年
{{item.title}}