一篇文章搞懂HTTPS协议
1. HTTP 协议
HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP 是一个基于 TCP/IP 通信协议来传递数据,HTTP 协议承载的数据类型包括 HTML 文件,Javascript, css, 图片,json,yaml, pdf 等等数据类型。
HTTP 是一个属于应用层协议,应用层有专门的应用服务器,例如: Nginx, Apache HTTP Server, Candy, Apache Tomcat, IIS, Weblogic、WebSphere, jetty。
用户也可以使用各种语言提供的网络库,框架实现自己的 HTTP 服务.
1.1. Http 协议存在的安全问题?
http 协议的传输是以明文的形式进行传输,从浏览器经中间的各种代理服务器,路由器,防火墙和各种网络的设备之后到达服务器,服务器收到请求再发出响应经过各种网络的设备到达浏览器。这整一个过程都是以明文的形式进行传输。
1.1.1. 通信是采用明文,内容可能被窃听
明文传输就是在通信的过程中所有的东西都是可见的毫无隐私可言,如果在中间的一些环节,有人恶意地去读取通信信息,那就可能会被窃取一些敏感的信息。而这些就包括最为常见的,用户的账户和密码,或者是银行账号和手机号,只要是在通信中出现了这样的数据就可以被取到。
另一种是会被篡改 http 协议的内容,比如,在一个页面中直接插入了一个广告或者小卡片的,而这里小广告通常不是一个静态的图片,而是一个可能带有恶意注入攻击的链接,只要点击了就可能被攻击了或者是再弹出一些乱七八糟的东西。
1.1.2. 无法验证接受报文的完整性,可能被篡改
还有一种是修改或者插入脚本,使页面在加载的时候直接跳到了其他的页面上去或者是修改了 header 使得原本用来防御注入攻击的功能失效。
1.1.3. HTTP 协议不验证通信方身份,因此可能被伪装
HTTP 协议设计的十分简单,并且不验证通信双方,也就意味着,不论是谁发送的请求,只要合法(后台没有限制访问 ip 和端口号),服务器都会接受而不确认通信方身份可能会导致以下的问题:
无法确定请求发送至目标 Web 服务器是否按照真实意图返回响应的那台服务器,有可能是已经伪装的 Web 服务器
无法确定响应返回到的客户端是否是按照真实意图接收响应的客户端,有可能是已伪装的客户端
无法确定正在通信的对方是否具有访问权限,因为某些 Web 服务器上保存着重要信息,只想发给特定用户通信的权限
无法判断请求是来自何方,出自谁手
即是无意义的请求也照单全收,无法阻止海量请求下的 DoS 攻击(Denial of Service),也就容易遭受攻击
2. HTTPS 协议
2.1. 为什么要有 HTTPS 协议?
HTTPS 是支持 TLS 加密的 HTTP。HTTPS 使用 TLS (SSL) 来加密普通的 HTTP 请求和响应,使它变得更加安全。
2.2. HTTPS 如何保证安全?
如果服务器给客户端的消息是密文的,只有服务器和客户端才能读懂,就可以保证数据的保密性。同时,在交换数据之前,验证一下对方的合法身份,就可以保证通信双方的安全。(和我们平时开发中 RSA 加签验签,加密解密的过程比较像)。HTTPS 就是利用了类似的原理来保证通信的安全性。
所以 HTTPS 保证安全通信的步骤主要分为两步:
通信前验证对方的合法身份;
将通信的报文加密,通过密文进行通信。
2.3. HTTPS 的基础 —— SSL 协议
HTTPS 实现安全通信的基础是 SSL 协议。
SSL:(Secure Socket Layer,安全套接字层),为 Netscape 所研发,用以保障在 Internet 上数据传输安全,它已被广泛地用于 Web 浏览器与服务器之间的身份认证和加密数据传输。
SSL 协议位于 TCP/IP 协议与各种应用层协议之间。SSL 协议可分为两层,SSL 记录协议(SSL Record Protocol):它建立在可靠的传输协议(如 TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL 握手协议(SSL Handshake Protocol):它建立在 SSL 记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
SSL 协议的主要功能
- 加密数据以防止数据中途被窃听;
- 认证用户和服务器,确保数据发送到正确的客户机和服务器;
- 验证数据的完整性,确保数据在传输过程中不被篡改。
下面我们就具体来看看 SSL 协议是怎么来实现上面三个功能的。
2.3.1. SSL 协议加密数据的原理
上面提到 SSL 协议会把通信的报文进行加密,那么服务器把数据加密后,客户端如何读懂这些数据呢?服务器必须要把加密的密钥(SSL 中报文加密使用了对称加密技术,比如 DES,3DES,AES 等)告诉客户端,客户端才能利用对称密钥解开密文的内容。
但是,如果服务器将这个对称密钥以明文的方式给客户端,这个密钥还是可能会被中间人截获,依然无法保证通信的安全性。
那么服务器要以密文的方式将对称密钥发给客户端的话,客户端又是如何解开这个密文的呢?下面看下 SSL 协议的工作流程。
- 客户端浏览器向服务器发送 SSL/TLS 协议的版本号、加密算法的种类、产生的随机数,以及其他需要的各种信息。
- 服务器从客户端支持的加密算法中选择一组加密算法与 Hash 算法,并且把自己的证书(包含网站地址、加密公钥、证书颁发机构等)也发送给客户端。
- 浏览器获取服务器证书后验证其合法性,验证颁发机构是否合法,验证证书中的网址是否与正在访问的地址一致,通过验证的浏览器会显示一个小锁头,否则,提示证书不受信。
- 客户端浏览器生成一串随机数(这串随机数就是后续用于加密的对称密钥)并用服务器传来的公钥加密,再使用约定好的 Hash 算法计算握手消息,发送到服务器端。
- 服务器接到握手消息后用自己的私钥解密,并用散列算法验证(这部散列算法验证很重要,可以防止中间人伪造),这样双方都有了此次通信的密钥。
- 服务器再使用密钥加密一段握手消息,返回给客户端浏览器。(这步的作用是告诉客户端,服务器已经正确收到密钥)
- 浏览器用密钥解密,并用散列算法验证,确定算法与密钥。
完成以上 7 步后双方就可以利用此次协商好的密钥进行通信。
2.3.2. 为什么需要使用 CA 证书?
CA 是 Certificate Authority 的缩写,也叫“证书授权中心”,它是负责管理和签发证书的第三方机构, CA 是整个安全机制的重要保障。
我们来看一下某度的证书,从图中可以看出证书中包含了许多关键信息,包括颁发者是谁,颁发给谁,颁发日期,有效期,公钥,证书等等信息。
大家有没有想过,如果是中间人攻击,中间人完全有可能截获服务器端发送的证书,然后将公钥替换为自己的公钥和证书,这样客户端如果不经过验证,直接使用公钥,客户端以为自己使用的是服务器发送的公钥,实际使用的是中间人伪造的公钥,这样在通信过程中,客户端发送的信息,中间人是可以使用自己的私钥解开,再用服务端的公钥加密,传递给服务端,这是完全有可能的。这样导致的后果就是,客户端发送的敏感信息在中间人面前依然是明文的。
所以 SSL 工作流程中的第三步就非常关键,客户端拿到服务器发送的证书,一定要进行验证。这个验证过程是如何工作的呢?我们再看下面这张图:
从证书的层次结构可以看出,证书的层次结构呈现链式结构,称为 SSL 证书链, 至于 SSL 证书链是如何工作的,这里不作展开讲解,我们只要知道 SSL 证书链的作用是使用 CA 颁发的根证书验证 CA 颁发的子证书。
处于 SSL 证书链条的顶部的证书可以用于验证 SSL 链条底部的证书的合法性。最顶端的证书称为根证书,而这些权威证书颁发机构的颁发的根证书是预留在操作系统中,也是预留在浏览器中,在系统升级过程中会随之更新的。使用这些根证书就可以验证 CA 颁发的或者 CA 的代理机构颁发的证书,从而可以保证证书的有效性和合法性。而中间人通过自己的私钥伪造的证书是理论上是无法通过证书验证这一环节的。
linux 根证书位置:/etc/pki/tls/certs
所以只有 CA 颁发的证书才能 SSL 验证通过,这就是为什么需要使用 CA 证书的原因。 当然在日常开发过程中,开发者自己的颁发的证书要想通过证书验证这一关,需要将自己颁发的根证书放到系统的或者浏览器的受信任的证书列表中。
2.3.3. CA 证书签发过程是怎样的?
PKI(Public Key Infrastructure)公钥基础设施,由密钥管理(发放,吊销),密钥合法性验证,认证机构,密钥发放等组成。为数据完整性,隐私性,数据身份认证等提供基础服务。
RA(Registration Authority)数字证书登记机构,给申请数字证书者发放证书,发放前对申请者进行审合登记,它是 CA 的延伸机构。
- 首先证书申请人向证书登记机构提交证书签名申请 csr 文件(certificate signing request)
- CA 通过线上、线下等多种手段验证申请者提供信息的真实性,如:组织是否存在、企业是否合法,是否拥有域名的所有权等;信息审核通过,CA 会向申请者签发认证文件 — 证书 cer 文件(cer 为 certificate 的简写)。
- 证书 = 公钥 + 申请者与颁发者信息 + 签名
- 证书申请人获得到证书 cer 文件后,连同创建 csr 文件时使用的密钥(key)文件部署到 web 服务器。部署成功后,服务器就变成支持 https 的服务器了。
- 当客户端发起 http 请求的时候,web 服务器会将证书发送给客户端,用于交换通信密钥,详见 SSL 协议加密数据的原理章节。
2.3.4. 证书签名申请(CSR)是如何制作的?
- 证书申请人需要使用非对称加密方式生产公私钥对,公钥为 pub 文件,私钥为 key 文件
- 申请人使用私钥将公钥及网站信息(域名/组织 etc.)生成 csr 文件证书签名申请
3. 使用 ACME 快速申请证书
ACME (Automatic Certificate Management Environment) 协议主要用于自动化 TLS/SSL 证书的获取、配置和管理。它简化了证书的管理过程,提高了网站的安全性和可维护性。
ACME 的主要作用:
- 自动化证书申请:用户自动生成证书请求并发送给证书颁发机构(CA)。
- 自动化证书验证:CA 自动验证域名所有权(通过 HTTP-01、DNS-01 或 TLS-ALPN-01 挑战)。
- 自动化证书颁发:验证通过后,CA 自动颁发证书并发送给客户端。
- 自动化证书更新:在证书即将过期时,自动监控并更新证书。
- 简化管理:减少人工操作的错误和维护成本,简化大规模证书管理。
3.1. ACME 的实现
ACME 协议有许多实现,它们提供了不同的功能和适应性,以满足各种需求。以下是一些著名的 ACME 实现:
- Let’s Encrypt (Certbot)
- acme.sh
- dehydrated
- Caddy
- lego
- Win-ACME
- acmetool
- cert-manager
3.2. ACME 工作流程
详细解释:
- Generate Key Pair:客户端生成一个新的密钥对。
- Register Account:客户端向 CA 注册一个新的 ACME 账号,并使用生成的密钥对进行签名。
- Apply for Certificate:客户端创建一个证书签名请求(CSR),并向 CA 提交申请。
- Validation Challenge:CA 通过 ACME 协议执行验证挑战(Validation Challenge),验证域名所有权。
- Issue Certificate:验证通过后,CA 向客户端颁发证书。
- Deploy Certificate:客户端接收证书并将其部署到服务器上。
- Certificate Renewal:在证书即将到期时,客户端会自动执行更新流程。
3.3. 安装 acme.sh
一键脚本安装
1 |
|
或者从 github 安装
1 | git clone https://github.com/acmesh-official/acme.sh.git |
3.4. 申请证书
3.4.1. 通过 http 方式申请证书
只需要指定域名, 并指定域名所在的网站根目录. acme.sh 会全自动的生成验证文件, 并放到网站的根目录, 然后自动完成验证. 最后会聪明的删除验证文件. 整个过程没有任何副作用.
1 | # 生成 RSA 证书: |
如果没有安装 web 服务器,acme.sh 也自带一个建议的 web 服务用于证书申请,前提是需要安装 socat ,80 端口没有被占用。使用方法:
1 |
|
3.4.2. 通过 DNS 方式申请证书
这种方式的好处是,你不需要任何服务器,不需要任何公网 ip,只需要 dns 的解析记录即可完成验证,而且可申请泛域名证书。坏处是,需要配合 DNS 解析服务商的 API 使用,否则 acme.sh 将无法自动更新证书,每次都需要手动再次重新解析验证域名所有权。
目前支持 DNSPod、CloudXNS、Aliyun、jdcloud、 CloudFlare、 GoDaddy、 Azure、AWS 等国内外大多数主流 DNS 服务提供商。详细清单详见: https://github.com/Neilpang/acme.sh/wiki/dnsapi
阿里云 DNS 申请方式
1 | export Ali_Key="abcd" |
Ali_Key 和 Ali_Secret 需要从阿里云 RAM 访问控制中获取。
Namesilo 申请方式
1 | export Namesilo_Key="xxxxxxxxxx" |
如果一切顺利,证书将会被颁发,并保存在~/.acme.sh目录下
1 |
|
可以使用如下命令查看证书内容
1 | openssl x509 -in TEST.CRT -text -noout |
3.5. 安装证书
证书生成以后, 接下来需要把证书安装到真正需要用它的地方。acme.sh 默认生成的证书都放在安装目录下: ~/.acme.sh/ ,但是官方不推荐直接使用该目录下的证书文件。
正确的使用方法是使用 –installcert 命令,并指定目标位置, 然后证书文件会被复制到相应的位置。
1 | acme.sh --installcert -d www.example.com \ |
–reloadcmd 参数用于让 web 服务器重新加载新的证书文件,例子中使用的是 nginx 服务器,您也可以定义成其它服务器。
–ecc 参数用于 ECC 证书,rsa 证书请不要添加此参数,下同。
3.6. 更新 SSL 证书
Let’s Encrypt 的证书有效期为 3 个月,每 3 个月得重新申请证书。通过 acme.sh 可以自动管理 SSL 证书的申请。通过上面步骤的安装后 acme.sh 会定期自动更新 SSL 证书。
当然,acme.sh 也提供手动更新方式。
1 |
|
3.7. 取消 SSL 证书的自动续期
有时候你可能需要移除特定域名的自动申请,这时候可以使用下面的命令让 acme.sh 取消对特定域名的自动续期。当然已申请的证书仍然有效,不会失效。
1 | acme.sh --remove -d www.example.com --ecc |
4. 参考文档
一篇文章搞懂HTTPS协议