奶昔论坛

标题: 加密的数据还能被篡改?用AEAD 拯救你的数据安全 [打印本页]

作者: yagamil    时间: 2025-6-6 01:57
标题: 加密的数据还能被篡改?用AEAD 拯救你的数据安全

你是不是觉得只要对数据加密后数据就一定不会被篡改了?那你就大错特错了。



一个意想不到的实验结果


首先,我们来看一个神奇的实验:


from __future__ import annotations

from cryptography.hazmat.primitives.ciphers import Cipher , algorithms , modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend

# ==================================================
# AES-CBC-256 核心加解密(非AEAD)
# ==================================================
def aes_cbc_encrypt(plain_data: bytes , key: bytes , iv: bytes) -> bytes:
    """CBC加密(需要PKCS7填充)"""
    # 添加填充
    padder = padding.PKCS7(128).padder()
    padded_data = padder.update(plain_data) + padder.finalize()

    # 加密
    cipher = Cipher(algorithms.AES(key) , modes.CBC(iv) , backend = default_backend())
    encryptor = cipher.encryptor()
    return encryptor.update(padded_data) + encryptor.finalize()

def aes_cbc_decrypt(ciphertext: bytes , key: bytes , iv: bytes) -> bytes:
    """CBC解密(无完整性验证)"""
    # 解密
    cipher = Cipher(algorithms.AES(key) , modes.CBC(iv) , backend = default_backend())
    decryptor = cipher.decryptor()
    padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()

    # 移除填充
    unpadder = padding.PKCS7(128).unpadder()
    return unpadder.update(padded_plaintext) + unpadder.finalize()

if __name__ == "__main__":
    # 固定输入值(实际使用中应为随机值)
    plaintext = b"Transfer $100 to Alice"
    key = b"12345678901234567890123456789012"  # 256-bit key,仅用作测试
    iv = b"1234567890123456"  # 16 字节 CBC IV

    print("===== AES-CBC-256 演示 =====")

    print(f'原始明文数据:{plaintext.hex()}')

    # CBC加密
    cbc_ciphertext = aes_cbc_encrypt(plaintext , key , iv)
    print(f"密文: {cbc_ciphertext.hex()}")

    # CBC解密(原始数据)
    cbc_decrypted = aes_cbc_decrypt(cbc_ciphertext , key , iv)
    print(f"对正常的密文解密后的结果: {cbc_decrypted.hex()}, 解密成功:{cbc_decrypted.hex() == plaintext.hex()}")

    # 篡改第1个字节(在真实攻击中更隐蔽)
    # 如果改末尾的字节则可能会解填充失败,会显式的抛出异常
    tampered_ciphertext = bytes([ cbc_ciphertext[ 0 ] ^ 0x01 ]) + cbc_ciphertext[ 1: ]

    # 解密被篡改的密文 - 不报错但得到错误结果
    tampered_decrypted = aes_cbc_decrypt(tampered_ciphertext , key , iv)
    print(
            f"篡改后的密文解密出的结果: {tampered_decrypted.hex()},"
            f"解密成功: {tampered_decrypted.hex() == plaintext.hex()},",
            f"解出的数据错误但解密过程无异常")

猜一猜运行代码后会不会抛出异常?


答案是:不会!!!


运行代码后我们可以得到如下的输出:
[attach]7[/attach]
看到没,在测试代码中,我们只改动了一个字节:
[attach]8[/attach]
最后解密出来的结果却大相径庭,并且结果过程没有丝毫的错误提示。
这就是非 AEAD加密模式的致命缺陷:只管加密,不防篡改。(例如AES-CBC 是典型的非 AEAD 加密算法)
[attach]9[/attach]

为什么加密不等于安全?


AES-CBC等传统加密模式统统都面临着以下的困境:



划重点:什么是AEAD?


AEAD(Authenticated Encryption with Associated Data)的出现彻底改变了游戏规则,它同时做到三件事:



AES-GCM 是典型的 AEAD 加密算法
[attach]10[/attach]
让我们再看一个实验:
from __future__ import annotations

from typing import Tuple

from cryptography.hazmat.primitives.ciphers import Cipher , algorithms , modes
from cryptography.hazmat.backends import default_backend

# ==================================================
# AES-GCM-256 核心加解密(AEAD模式)
# ==================================================
def aes_gcm_encrypt(plain_data: bytes , key: bytes , nonce: bytes , ad: bytes) -> Tuple[ bytes , bytes ]:
    """GCM加密(返回密文和认证标签)"""
    cipher = Cipher(algorithms.AES(key) , modes.GCM(nonce) , backend = default_backend())
    encryptor = cipher.encryptor()

    # 添加关联数据认证
    if ad:
        encryptor.authenticate_additional_data(ad)

    # 加密并获取认证标签
    ciphertext = encryptor.update(plain_data) + encryptor.finalize()
    return ciphertext , encryptor.tag

def aes_gcm_decrypt(ciphertext: bytes , tag: bytes , key: bytes , nonce: bytes , ad: bytes) -> bytes:
    """GCM解密(带完整性验证)"""
    cipher = Cipher(algorithms.AES(key) , modes.GCM(nonce , tag) , backend = default_backend())
    decryptor = cipher.decryptor()

    # 添加关联数据认证
    if ad:
        decryptor.authenticate_additional_data(ad)

    # 解密(如果验证失败会抛出异常)
    return decryptor.update(ciphertext) + decryptor.finalize()

if __name__ == "__main__":
    # 固定输入值(实际使用中应为随机值)
    plaintext = b"Secret message! AEAD is awesome!"
    key = b"12345678901234567890123456789012"  # 256-bit key 仅用作测试
    nonce = b"123456789012"  # GCM 12 字节 nonce
    ad = b"Additional data"  # GCM关联数据

    print("\n===== AES-GCM-256 演示 =====")

    print(f'原始明文数据: {plaintext.hex()}')

    # GCM加密
    gcm_ciphertext , tag = aes_gcm_encrypt(plaintext , key , nonce , ad)
    print(f"密文数据: {gcm_ciphertext.hex()}")
    print(f"认证标签: {tag.hex()}")

    # GCM解密(原始数据)
    gcm_decrypted = aes_gcm_decrypt(gcm_ciphertext , tag , key , nonce , ad)
    print(f"正常的密文解密结果: {gcm_decrypted.decode()}, 解密成功:{gcm_decrypted.hex() == plaintext.hex()}")

    # 尝试篡改密文的第一个字节
    tampered_gcm_ciphertext = bytes([ gcm_ciphertext[ 0 ] ^ 0x01 ]) + gcm_ciphertext[ 1: ]

    # 解密篡改后的密文 - 应该失败
    try:
        aes_gcm_decrypt(tampered_gcm_ciphertext , tag , key , nonce , ad)
        print("解密成功 (不应该发生)")
    except Exception as e:
        print(f"篡改测试: {e} 解密失败,AEAD检测到密文篡改!")

    # 尝试篡改关联数据
    try:
        aes_gcm_decrypt(gcm_ciphertext , tag , key , nonce , b"tampered_ad")
        print("解密成功 (不应该发生)")
    except Exception as e:
        print(f"AD篡改测试: {e} 解密失败,AEAD检测到AD篡改!")

运行代码后我们得到以下结果:
[attach]11[/attach]

我们可以清楚的看到,对于正确的密文,可以正确解密出明文;而对于被篡改后的密文,则会捕获解密失败的异常。


这就是 AEAD 的特殊功效:



  1. 认证标签(Tag):数据的"数字指纹",由密钥+密文生成,哪怕改动1个比特,标签就会失效

  2. 关联数据(AD):若伪造关联数据,则解密立即失败

  3. Nonce防御重放攻击:每次加密使用唯一Nonce(编号)防重放攻击


当前支持AEAD的加密算法


[attach]12[/attach]
目前超过90%的HTTPS流量使用AES-GCM或ChaCha20-Poly1305,而TLS 1.3更是强制使用AEAD技术!
安全不是产品,而是持续的过程。
真正的安全不是相信数据未被篡改,而是能证明它未被篡改。

虽然AEAD提供终极防护,但如果实现不当(如密钥泄漏或随机数重用),仍然可能导致各种数据风险,因此,AEAD不是银弹,因为黑客从不攻击算法本身,而是寻找人犯错留下的缝隙。




欢迎光临 奶昔论坛 (https://bbs.moecloud.cn/) Powered by Discuz! X3.5