MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

HTTPS协议的工作原理与安全机制

2023-07-301.3k 阅读

HTTPS协议简介

在深入探讨HTTPS协议的工作原理与安全机制之前,我们先来对HTTPS有一个基本的认识。HTTPS,即超文本传输安全协议(Hyper - Text Transfer Protocol Secure),是在HTTP的基础上加入了SSL/TLS协议来提供加密传输和身份认证功能,确保数据在传输过程中的安全性和完整性。

传统的HTTP协议以明文形式传输数据,这就使得数据在传输过程中容易被窃取、篡改或伪造。例如,用户在使用HTTP协议登录网站时,用户名和密码等敏感信息会以明文形式在网络中传输,攻击者只要在数据传输的路径上进行监听,就能够获取到这些信息。而HTTPS通过加密机制,将数据进行加密后再传输,即使攻击者监听到数据,也无法直接获取其内容。

加密技术基础

为了更好地理解HTTPS协议的工作原理,我们需要先了解一些基本的加密技术。

对称加密

对称加密是指加密和解密使用相同密钥的加密方式。假设发送方A要向接收方B发送数据,A和B事先共享一个密钥K。A使用密钥K对数据进行加密得到密文C,然后将密文C发送给B。B收到密文C后,使用相同的密钥K对其进行解密,从而得到原始数据。

对称加密的优点是加密和解密速度快,适合大量数据的加密。例如,在视频流或文件传输中,如果使用非对称加密,由于其运算速度较慢,可能会导致传输效率低下,而对称加密则能很好地满足这种场景下对速度的要求。然而,对称加密也存在一个明显的缺点,即密钥的管理和分发比较困难。如果A和B要进行通信,他们必须通过安全的方式共享密钥K。在网络环境中,如何安全地传输这个密钥是一个挑战。如果密钥在传输过程中被窃取,那么攻击者就可以轻易地解密所有使用该密钥加密的数据。

以下是使用Python的cryptography库进行对称加密的简单示例:

from cryptography.fernet import Fernet

# 生成密钥
key = Fernet.generate_key()
cipher_suite = Fernet(key)

# 要加密的数据
data = b"Hello, World!"

# 加密数据
encrypted_data = cipher_suite.encrypt(data)
print("加密后的数据:", encrypted_data)

# 解密数据
decrypted_data = cipher_suite.decrypt(encrypted_data)
print("解密后的数据:", decrypted_data)

非对称加密

非对称加密使用一对密钥,即公钥和私钥。公钥可以公开给任何人,而私钥则由所有者妥善保管。发送方使用接收方的公钥对数据进行加密,接收方使用自己的私钥对数据进行解密。

例如,假设A要向B发送数据。B首先生成一对公钥和私钥,然后将公钥公开给A。A使用B的公钥对数据进行加密,得到密文C,并将密文C发送给B。B收到密文C后,使用自己的私钥对其进行解密,从而得到原始数据。

非对称加密解决了对称加密中密钥分发的问题。由于公钥可以公开分发,不需要担心公钥在传输过程中被窃取。即使攻击者获取了公钥,也无法通过公钥推导出私钥,因为从公钥计算出私钥在计算上是不可行的(基于一些数学难题,如大整数分解问题)。然而,非对称加密的运算速度较慢,不适合对大量数据进行加密。例如,在传输大文件时,如果直接使用非对称加密,加密和解密过程会消耗大量的时间和计算资源。

以下是使用Python的cryptography库进行非对称加密的示例:

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes

# 生成私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)

# 生成公钥
public_key = private_key.public_key()

# 将私钥序列化为PEM格式
private_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

# 将公钥序列化为PEM格式
public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 要加密的数据
data = b"Hello, Asymmetric Encryption"

# 使用公钥加密
encrypted = public_key.encrypt(
    data,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# 使用私钥解密
decrypted = private_key.decrypt(
    encrypted,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

print("原始数据:", data)
print("加密后的数据:", encrypted)
print("解密后的数据:", decrypted)

哈希函数

哈希函数是一种将任意长度的数据映射为固定长度的哈希值的函数。哈希函数具有以下几个重要特性:

  1. 确定性:相同的输入数据总是产生相同的哈希值。例如,对于字符串“Hello”,无论何时使用同一个哈希函数进行计算,得到的哈希值都是一样的。
  2. 单向性:从哈希值很难反向推导出原始数据。这意味着即使知道了某个数据的哈希值,也无法通过哈希值还原出原始数据。
  3. 敏感性:输入数据的微小变化会导致哈希值的巨大变化。比如,字符串“Hello”和“Hello ”(末尾多了一个空格),它们的哈希值会完全不同。

哈希函数在HTTPS协议中主要用于验证数据的完整性。例如,发送方在发送数据时,会计算数据的哈希值,并将哈希值随数据一起发送。接收方收到数据后,重新计算数据的哈希值,并与发送方发送的哈希值进行比较。如果两个哈希值相同,则说明数据在传输过程中没有被篡改;如果不同,则说明数据可能被篡改了。

常见的哈希函数有MD5、SHA - 1、SHA - 256等。虽然MD5和SHA - 1曾经被广泛使用,但由于它们存在一些安全漏洞(如MD5已被证明可以通过碰撞攻击伪造相同的哈希值),现在在安全敏感的场景中,一般推荐使用SHA - 256等更安全的哈希函数。

以下是使用Python的hashlib库计算SHA - 256哈希值的示例:

import hashlib

data = b"Hello, Hash Function"
hash_object = hashlib.sha256(data)
hash_value = hash_object.hexdigest()
print("SHA - 256哈希值:", hash_value)

HTTPS协议的工作原理

HTTPS协议的工作过程可以分为以下几个主要步骤:

客户端发起请求

当用户在浏览器中输入一个HTTPS网址并回车后,浏览器(客户端)会向服务器发起一个HTTPS连接请求。这个请求包含了客户端支持的SSL/TLS协议版本、加密算法列表、压缩算法列表等信息。例如,客户端可能会表示自己支持TLS 1.3协议,支持的加密算法有AES - 256 - GCM、ChaCha20 - Poly1305等。

服务器响应

服务器收到客户端的请求后,会从客户端提供的信息中选择一个双方都支持的SSL/TLS协议版本和加密算法。然后,服务器会将自己的数字证书发送给客户端。数字证书包含了服务器的公钥、服务器的域名、证书颁发机构(CA)的信息等。

例如,假设服务器的域名为example.com,它的数字证书中会包含example.com的相关信息以及由某个权威CA机构颁发的签名。客户端收到服务器的数字证书后,首先会验证证书的合法性。它会检查证书是否由受信任的CA颁发,证书是否过期,证书中的域名是否与当前访问的域名一致等。如果证书验证通过,客户端就可以从证书中提取出服务器的公钥。

密钥交换

在验证服务器证书通过后,客户端会生成一个随机数,称为“预主密钥”(Pre - Master Secret)。然后,客户端使用从服务器证书中提取的公钥对预主密钥进行加密,并将加密后的预主密钥发送给服务器。

服务器收到加密后的预主密钥后,使用自己的私钥进行解密,得到预主密钥。此时,客户端和服务器都拥有了预主密钥。接下来,双方会根据预主密钥以及之前协商好的加密算法,计算出会话密钥(Session Key)。会话密钥用于后续数据传输过程中的加密和解密。

例如,假设使用的加密算法是TLS 1.3中的HKDF(HMAC - based Key Derivation Function),客户端和服务器会使用预主密钥、双方的随机数等信息,通过HKDF计算出会话密钥。由于双方使用相同的计算方法和相同的输入信息,所以计算出的会话密钥是一致的。

数据传输

在完成密钥交换后,客户端和服务器之间就可以使用会话密钥进行加密通信了。客户端将请求数据使用会话密钥进行加密,然后发送给服务器。服务器收到加密数据后,使用会话密钥进行解密,处理请求,并将响应数据同样使用会话密钥加密后发送给客户端。客户端收到响应数据后,解密并显示给用户。

例如,假设用户在浏览器中请求一个网页,浏览器将请求的URL等信息使用会话密钥加密后发送给服务器。服务器解密请求,获取URL,查找对应的网页内容,然后将网页内容加密后返回给浏览器。浏览器解密网页内容并显示在用户界面上。

连接关闭

当数据传输完成后,客户端和服务器可以通过发送关闭信号来结束HTTPS连接。例如,客户端可以发送一个“FIN”(Finish)包,表示自己已经没有数据要发送了,服务器收到后可以回复一个确认包,并关闭自己的发送通道。然后,服务器也可以发送一个“FIN”包,客户端回复确认包,从而完全关闭连接。

HTTPS协议的安全机制

HTTPS协议通过多种安全机制来确保数据的安全性和完整性。

数据加密

如前文所述,HTTPS协议使用对称加密算法对数据进行加密传输。在握手阶段协商好加密算法和会话密钥后,客户端和服务器之间传输的所有数据都使用会话密钥进行加密。这使得攻击者即使在网络中截获了数据,由于没有会话密钥,也无法解密获取数据的内容。

例如,在电子商务网站中,用户的订单信息、支付信息等敏感数据在传输过程中都被加密。即使攻击者监听到了网络流量,看到的也只是一堆密文,无法获取其中的实际信息。

身份认证

HTTPS协议通过数字证书实现服务器的身份认证。当客户端向服务器发起请求时,服务器会将自己的数字证书发送给客户端。客户端通过验证证书的合法性,确保自己连接的是真实的服务器,而不是中间人攻击者伪造的服务器。

数字证书由受信任的证书颁发机构(CA)颁发。CA在颁发证书之前,会对服务器的身份进行严格验证,确保证书中的域名与实际服务器的域名一致,服务器拥有对应的私钥等。例如,当用户访问银行网站时,浏览器会验证银行服务器的数字证书。如果证书是由权威CA颁发且验证通过,浏览器会显示安全的标识(如绿色的锁图标),表示用户连接的是真实的银行服务器。否则,浏览器会提示用户证书存在问题,可能存在安全风险。

此外,在一些场景下,也可以实现客户端的身份认证。例如,企业内部网络中,服务器可以要求客户端提供数字证书进行身份验证,以确保只有授权的客户端能够访问服务器资源。

数据完整性验证

HTTPS协议使用哈希函数来验证数据的完整性。在数据传输过程中,发送方会计算数据的哈希值,并将哈希值随数据一起发送。接收方收到数据后,重新计算数据的哈希值,并与发送方发送的哈希值进行比较。如果两个哈希值相同,则说明数据在传输过程中没有被篡改;如果不同,则说明数据可能被篡改了。

例如,在文件下载场景中,服务器在发送文件时,会同时发送文件的哈希值(如SHA - 256哈希值)。客户端下载完文件后,计算文件的哈希值,并与服务器发送的哈希值进行对比。如果一致,则说明文件下载完整且未被篡改;如果不一致,则说明文件可能在下载过程中出现了错误或被篡改,客户端可以选择重新下载。

代码示例:实现一个简单的HTTPS服务器

下面我们使用Python的http.server模块和ssl模块来实现一个简单的HTTPS服务器。

import http.server
import ssl

# 定义一个简单的HTTP请求处理类
class MyRequestHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content - type', 'text/html')
        self.end_headers()
        self.wfile.write(b'Hello, HTTPS Server!')

# 创建HTTP服务器实例
server_address = ('localhost', 443)
httpd = http.server.HTTPServer(server_address, MyRequestHandler)

# 使用SSL/TLS进行加密
httpd.socket = ssl.wrap_socket(httpd.socket,
                               server_side=True,
                               certfile='server.crt',
                               keyfile='server.key',
                               ssl_version=ssl.PROTOCOL_TLSv1_2)

print('HTTPS server running on https://localhost:443')
httpd.serve_forever()

在上述代码中,我们首先定义了一个简单的HTTP请求处理类MyRequestHandler,它处理GET请求并返回一个简单的“Hello, HTTPS Server!”响应。然后,我们创建了一个HTTP服务器实例,并使用ssl.wrap_socket方法将其包装为HTTPS服务器。这里需要提供服务器的证书文件(server.crt)和私钥文件(server.key)。在实际应用中,需要使用由合法CA机构颁发的证书,或者自己生成自签名证书(但自签名证书在浏览器中可能会提示不安全)。

总结

HTTPS协议通过加密技术、身份认证和数据完整性验证等多种安全机制,为网络通信提供了可靠的安全保障。理解HTTPS协议的工作原理和安全机制,对于后端开发人员来说至关重要。在实际开发中,正确配置和使用HTTPS,能够有效保护用户数据的安全,提升应用的安全性和可信度。通过本文的介绍,希望读者对HTTPS协议有了更深入的理解,并能够在实际项目中更好地应用这一重要的网络安全技术。同时,随着网络安全技术的不断发展,HTTPS协议也在不断演进,开发人员需要持续关注相关技术动态,以确保应用的安全性与时俱进。