Let’s Encrypt This Site

操了我都,字体 CDN 莫名其妙被 CORS 阻挡了,搞得我数学公式都没法正常显示,你说你一个 CDN 搞什么 HTTPS ?

一直懒得搞证书,反正没有什么敏感信息(大概?)。既然今天难得因为 HTTPS 出了问题,就折腾一波好了。

按部就班。

About HTTPS

What is HTTPS

HTTPS ,比 HTTP 多了一个 S ,这个 S 是 Secure 的意思,即安全的 HTTP ,它是一个基于 TCP/IP 协议栈的协议,为 HTTP 提供了安全的 SSL/TLS 加密传输层,为浏览器提供了服务器身份验证的能力,同时也可以为服务器提供客户端认证的功能。

Why using HTTPS

HTTPS 是个好东西啊,现在这个世界,谁不把隐私当作宝一样看待呢(李彦宏:你再说一遍?)

HTTP 是一个使用明文传输数据的协议,这当然犯了信息安全的大忌。为了确保整个 HTTP 报文被加密保护,就需要在 HTTP 和下层协议 (TCP) 之间加入加密层,通常来说这一层都是 SSL/TLS , HTTPS 就是 HTTP over SSL/TLS 的缩写(好吧,它是一堆东西的缩写)。

HTTPS 能使得所有数据在经过 TCP 之后,也就是数据在 TCP 层以及其下的协议栈中均为加密状态,而我们知道在互联网上,除了两端终端设备之外,其它网络设备解码不过 TCP ,因此可以说在走出计算机之后,数据就是加密状态,数据无法被窃听(机密性)、身份无法被冒充、数据无法被篡改(完整性)。

SSL/TLS 的方案是每次建立一个 TCP 连接之后就对网站进行身份认证,换句话说,你要证明你是你。这可是个千古难题啊,俗话说得好:

在互联网上,没人知道你是一条狗。

How to HTTPS

为了解决这个问题,我们要引入密码学中的可信赖第三方来帮网站证明“我是我”,举例而言:

全体中国公民都承认并信任中华人民共和国政府是可信任的第三方身份证发行机构,这个世界上也只有中华人民共和国政府有权力、有能力发行身份证,身份证不会重复(虽然存在,不过作为例子就不用考虑那几个罕见的反例了)、无法被篡改、无法被伪造,所以我们都承认身份证作为一个人证明自己是自己的(不唯一)凭证。

同样的道理,我们也承认几个证书颁发机构 (CA, Certificate Authority) 颁发的证书的权威性,所以某个网站能出示它们的证书,浏览器就承认网站的身份。至于如何才能算是 CA ,那就是浏览器的事情了,浏览器会内置一些机构列表(或使用操作系统提供的 CA 列表,或通过用户提供等),仅仅承认列表中机构颁发的证书,并且通过浏览器版本更新来维护这个列表。

所以我作为一个网站,第一件事就是要拿到证书颁发机构给我的证书。

Browser Authentication

要是基础的对称加密和非对称加密都分不清,那这小节也没必要看了。

现实中,我们通过签名、指纹或公章来验证一份文书的真实性。在互联网上,我们通过签名算法来验证信息的真实性。

建立 TCP 连接之后,首先是客户端发起加密请求,然后由服务器主动提供自己的证书,上面有 CA 的信息, CA 的签名,还有被 CA 加密过的网站信息。

在证书中会保存 CA 的签名,该签名只能通过 CA 自己的公钥解密,客户端将解密后的内容与浏览器内置的 CA 的信息相对比即可证明该证书的颁发者的确是某个 CA 。

然后再通过解密验证 CA 确实是给当前网站颁发的证书。

验证完毕之后就可以开始和网站协商对称加密密钥流程,然后最后就使用对称加密算法来通信。

Let’s Encrypt!

前面废话这么多,主要还是为了打下知识基础啦。世界上 CA 这么多,随便找一个申请即可。然而要钱……

作为个人站长,选一个免费的可信任的 CA 即可。这里我墙裂安利 Let’s Encrypt ,该项目由 ISRG 发起并维护,这个 ISRG 又是由密歇根大学、 Mozilla 组织、电子前哨基金会发起,现在的成员除了以上三者之外,还有著名的思科、 Akamai 、美国公民自由联盟等大公司以及公益组织。

该机构颁发的证书已经被主流浏览器承认,因此大可不必担心某个浏览器打不开的情况。

Let’s Encrypt 证书获取方式非常简单。对于终端用户,它们提供了 TUI 程序指导证书获取,大部分发行版的包管理器都包含了其 TUI 程序,可以通过搜索 letsencryptcertbot 即可下载。如果包管理器中没有,可以通过 https://certbot.eff.org/ 来获取。

下文只介绍 Let’s Encrypt 证书获取方法。

certbot 为 Apache Nginx 提供了自动配置的插件,如果服务器上跑的是这俩的话直接装个插件、加个参数就好了。

Domain Authentication

Let’s Encrypt 需要验证该域名为你所有,为此 certbot 提供了多种认证方式:

  • DNS Challenge: 通过给 DNS 添加一条 TXT 记录。
  • HTTP Challenge: 通过 HTTP 请求某个 URL 下的文件,要求已经配置好 DNS 解析,证书颁发服务器会请求该域名下某个 URL 以证明域名所有权。 HTTP 方式又分两种认证方式。

DNS Challenge

DNS Challenge 需要你为自己域名某个子域的 DNS 加一条 TXT 记录,会在下文 Wildcard Domain Certificate 一并介绍。

HTTP Challenge

  • standalone 方式会通过自行建立一个 web server 来验证,只要 DNS 配置好,服务能建立就可以。当然这对于已经使用了 web server 的站长来说需要 web server 下线一段时间以便让出端口。
  • webroot 方式则会通过在已有服务器的 web root 下写入一个隐藏文件来认证。对于 location 解析不是通过直接访问 web root 下某个文件的站点配置,可以通过关闭 web server 使用 standalone ,或者配置一下 location 解析特例来实现认证,特例是 /.well-known/acme-challenge/ 目录。以下是 Nginx 和 Apache 的配置,自行选择修改:
location ^~ /.well-known/acme-challenge/ {
  default_type "text/plain";
  root /your/webroot/;
}

location = /.well-known/acme-challenge/ {
  return 404;
}
Alias /.well-known/acme-challenge/ "/your/webroot/.well-known/acme-challenge/"
<Directory "/your/webroot/">
  AllowOverride None
  Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
  Require method GET POST OPTIONS
</Directory>

For Nginx/Apache

记得去下一个 python-certbot-nginxpython-certbot-apachepip 里就有。然后

certbot --nginx
certbot --apache

即可一键领取证书。

General method

对于更一般的情况, certbot 的使用也是很方便的,一般来说直接

certbot certonly

之后按步骤输入你的配置就可以了。

当然也可以一行流

certbot certonly \
  --webroot \
  --webroot-path /your/webroot \
  --domain xr1s.me \
  --domain www.xr1s.me

或者用:

certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com

即可。

Wildcard Domain Certificate

没错,这才是重点! 2018 年 3 月 14 日, Let’s Encrypt 终于支持域名通配符了。通配域名认证只能通过 DNS 方式获取,需要为 DNS 添加 TXT 记录,至于 TXT 记录的内容,还是需要在命令行获取,由于升级了协议,获取证书的接口也换了,需要手动指定、手动安装:

certbot certonly \
  --manual \
  --server https://acme-v02.api.letsencrypt.org/directory \
  --preferred-challenges dns \
  --domain xr1s.me \
  --domain '*.xr1s.me'

接下来会在命令行中回答输入邮箱(用于通知证书过期)、是否接受协议、是否订阅邮件、IP 是否和域名绑定,根据自己的情况作选择。再之后客户端会提供一条 TXT 记录信息,要求修改 DNS 记录。按照要求去提供域名 DNS 服务的地方加一条即可。

DNS 刷新需要一段时间,稍微等会儿刷新之后就可以继续了。 DNS 这玩意儿谁都可以查询,等自己都查询得到就可以回车等 Congratulation 了。(应该没人用 nslookup 了吧)

nslookup -q=TXT _acme-challenge.xr1s.me
dig _acme-challenge.xr1s.me TXT

完成验证后, certbot 会在 /etc/letsencrypt/live/xr1s.me/ 生成一份 PEM 文件,这是证书文件,好好保管。

openssl x509 -in /etc/letsencrypt/live/xr1s.me/fullchain.pem -noout -text

可以输出证书的具体内容。

这一步之后站点的身份已经被 CA 承认了,接下来就需要和浏览器交互了,开始配置 web server 。

Auto Renewal

哎,这个要交给定时软件来,比如 crontab ,比如 Systemd Timers 。

顺便一提,这玩意儿不支持通配符域名,通配符域名必须要手动重装一次(希望我重装的时候我还记得怎么装)。

不难写,不写了。

Web Server Configuration

For Nginx

指定端口和证书位置即可。

listen 443 ssl default_server;
ssl on;
ssl_certificate /etc/letsencrypt/live/xr1s.me/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xr1s.me/privkey.pem;

For Apache

LoadModule ssl_module modules/mod_ssl.so

Listen 443
<VirtualHost *:443>
  ServerName www.example.com
  SSLEngine on
  SSLCertificateFile "/path/to/www.example.com.cert"
  SSLCertificateKeyFile "/path/to/www.example.com.key"
</VirtualHost>

Force HTTPS

强制客户端使用 HTTPS 的常见方案有不少方法,比较常见的是修改 web server 配置重定向、手动加入 HSTS 头、申请 HSTS Preloading List 。

Web server 配置重定向在每次访问 HTTP 时都需要先向服务器请求一次,浪费带宽和 CPU ,而且由于第一个请求并没有加密,可能会造成安全问题。不过为了兼容老浏览器,还是要写一下的。

Web Server Redirection

For Nginx

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

For Apache

<VirtualHost *:80>
  <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
  </IfModule>
</VirtualHost>

HSTS Header

所谓 HSTS ,是 HTTP Strict-Transport-Security 的缩写,这是一个 HTTP 头,包含该头的响应通知浏览器只能使用 HTTPS 访问,任何 HTTP 访问都会被浏览器拒绝。其参数有:

  • max-age=<expire-time> ,浏览器在收到该响应头之后的 <expire-time> 秒内对该域名的访问均应使用 HTTPS 。
  • includeSubDomains ,可选,适用于所有子域名。
  • preload ,可选,用于 HSTS Preloading List 。

只要在 web server 中配置返回该头即可。

For Nginx

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

For Apache

Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"

HSTS Preloading List

这是一个 Google 维护的列表,支持该列表的浏览器在第一次加载该列表中的域名时就会直接采用 HTTPS ,从根本上解决了第一次需要使用 HTTP 来告知网站需要 HTTPS 的尴尬情况。

HSTS Preloading List Submission 申请加入该列表,申请条件很简单:

  • 有一个合法的证书。
  • 如果监听着 80 端口,需要强制重定向 HTTP 到 HTTPS 。
  • 所有子域名均支持 HTTPS 。
  • 无条件提供 HSTS 响应头:
    • max-age 至少为一年 (31536000) 。
    • includeSubDomains 必须存在。
    • preload 必须存在。

一个合法的 HSTS 头例子如下:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

References

发表评论

电子邮件地址不会被公开。 必填项已用*标注