我先说个结论:AES本身只是一套严谨的数学变换,真正让你在项目中翻车或者被攻击者捡漏的,往往不是AES算法本身,而是它外面套的那层“工作模式”。我见过太多团队把“用了AES-256”当成安全万金油,结果ECB模式一上线,密文里的轮廓图直接被画出来;也见过有人因为CBC模式的填充预言机漏洞,被攻击者逐个字节把明文猜出来。这篇文章我就把从ECB到GCM的常见AES工作模式完整拆一遍,讲清楚每种模式的原理、优缺点、典型坑,以及最后到底该怎么选型。
适合读这篇文章的,是那些需要在接口加密、数据库敏感字段加密、文件加密等场景里做技术选型的开发者和安全工程师。我会尽量把每个模式都放到“实际使用”的语境里讲,而不是只贴公式和流程图。你看完之后,至少能做到:拿到一个加密需求,能判断出该用哪种模式、IV怎么生成、密钥怎么管理、认证标签怎么处理。
1. 先搞清楚分组密码的模式到底在解决什么问题
1.1 AES算法加密的是“一块”,而不是“一段”
AES作为一个分组密码算法,核心能力其实很单一:把固定长度的输入块变成固定长度的输出块。AES-128、AES-192、AES-256分别对应128位、192位、256位密钥,但无论密钥多长,处理的数据块长度永远是128位,也就是16字节。
这里就出现了一个最基础的问题:你要加密的数据几乎不可能刚好是16字节的整数倍。哪怕你是加密一个短字符串“hello world”,也就11字节,距离16字节还差5个字节。所以你需要某种规则来决定:数据怎么分组、最后一组不足16字节怎么补齐、组与组之间怎么产生关联。这套规则就是工作模式。
从另一个角度看,如果只是简单地把每一块独立用AES加密,然后拼接起来,这种“最简单”的做法恰恰隐藏着巨大的安全问题——块与块之间的重复模式会被完整保留下来。这就是ECB模式的问题根源。
1.2 工作模式要解决的三个核心问题
我在做实际项目时,会习惯把工作模式的需求抽象成三个问题,这样看每种模式就特别清晰:
第一,数据分组与填充规则。多长的数据分一组,最后一组怎么处理。最常用的是PKCS#7填充,缺几个字节就补几个字节的相同数值,缺5字节就补5个0x05。
第二,块与块之间的关联方式。每块密文是否依赖于前面块的状态。如果有依赖,相同明文块在不同位置就会产生不同密文块,这能有效掩盖数据中的统计特征。
第三,是否提供完整性保护。普通模式只保证机密性,攻击者对密文做的任何篡改,你解密后根本无法感知。认证加密模式则会在加密之外附加一个认证标签,确保数据没有被改过。
理解了这三个问题,你再看ECB、CBC、CTR、GCM这些模式,就清楚它们在设计上的差异到底在哪里了。
2. 逐模式详解:从最不安全的ECB到推荐首选的GCM
2.1 ECB模式:千万不要在生产环境用
ECB全称Electronic Codebook,电子密码本模式。它的工作方式非常直白:把明文按16字节切开,每一块独立用同一个密钥做AES加密,最后把密文块按顺序拼起来。
因为每块加密之间没有任何关联,所以它有两个致命问题。第一个问题,相同明文块必然产生相同密文块。如果你加密一张图片,图片中大面积的相同颜色区域会被加密成完全相同的密文块,你把这些密文块重新拼成图像,居然还能看出原始图像的轮廓。这就是经典的“企鹅图”演示,把密文图像渲染出来,还能认清图案的轮廓。第二个问题,攻击者可以重排、删除、复制密文块,解密方根本察觉不到。因为没有块间校验,也没有认证机制,数据完整性完全失控。
我早期帮某团队做安全评审时,看到他们把用户手机号字段用AES-256/ECB直接加密存到数据库。那一刻我意识到这不是个例——很多开发者选择ECB就是因为它简单,不需要IV,不需要处理初始化向量,加解密代码寥寥几行。但ECB的问题不是理论层面的“可能被攻击”,而是现实层面“一定会泄漏信息结构”。任何包含规律性的业务数据,比如身份证号、手机号、枚举状态字段,用ECB加密后都能通过相同密文块反推明文间的相等关系。
所以在AES工作模式选型中,ECB只适合一种场景:加解密随机数据,且这些随机数据之间不需要任何关联保护。实际上我建议你干脆忘记这个模式,所有使用AES的地方默认先排除ECB。
2.2 CBC模式:经典但需要小心处理填充与IV
CBC全称Cipher Block Chaining,密码分组链接模式。它在ECB的基础上引入了块间链接机制:加密每一块明文之前,先将这一块明文与上一块的密文做异或,然后再用AES加密。第一块没有“上一块密文”,所以用一个随机初始向量IV充当这个角色。
这个链式依赖带来的好处是,相同明文块在不同位置、不同上下文中会产生完全不同的密文块,统计特征被抹掉了。于是CBC成为很长一段时间里最主流的加密模式,SSL/TLS早期版本、很多文件加密工具都用它。
但CBC有它需要小心对待的坑。第一个坑是IV必须是随机且不可预测的。IV不需要保密,但每次加密都要重新生成,绝不能固定复用。如果IV固定,那么第一条消息的第一块就和第二条消息的第一块共享同一个“起点”,开头相同的消息会暴露相同的前缀密文。而且CBC的IV如果被攻击者控制,还会衍生出更复杂的比特翻转攻击。
第二个坑是填充 oracle 攻击。因为CBC需要填充,解密时如果填充格式不正确,解密端通常会返回一个错误。攻击者可以向解密端不断发送篡改过的密文,根据是否触发填充错误来逐字节推测明文。这个攻击在现实中已经多次被利用,最著名的就是针对早期SSL/TLS的BEAST攻击和padding oracle攻击。解决办法说起来简单但执行上非常严格:一定要用认证机制保护密文,确保密文没被改过;解密错误信息要统一口径,不能区分“填充错误”和“认证失败”。
第三个坑是解密端无法并行的性能特征。CBC加密是串行的,因为每一块都要等前一块的密文结果;解密是可以并行的,因为解密时只需要密文块和前一块密文。所以CBC在高吞吐加密场景下会有瓶颈,但如果你主要做解密,性能反而还行。
在实践中用CBC,我会给一组明确的参数建议:使用AES-256-CBC,IV用16字节完全随机数,密钥单独管理,填充用PKCS#7,同时在密文上叠加HMAC做认证,并且要求“先加密后MAC”的顺序。这样用CBC是安全的,但代码复杂度实际上已经不低了。
2.3 CTR与OFB/CFB:走向流密码化
CTR全称Counter Mode,计数器模式。它不再对明文做链式异或,而是先构造一个计数器块,把计数器块用AES加密,得到一串伪随机密钥流,再把密钥流与明文做异或得到密文。
用一句话概括CTR的本质:把AES变成一个伪随机数生成器,然后用这个伪随机序列去掩盖明文。这个过程跟流密码很像。由于每个块的加密只依赖于“密钥+计数器值”,块与块之间没有依赖,所以CTR可以完全并行化,加密解密速度都非常快。
CTR的安全性核心在计数器构造上。我比较推荐的方式是:用一个随机nonce(比如12字节随机数)作为高96位,低32位作为初始计数器值。每次加密第一个块时计数器为某个起始值,之后逐块递增。这里最大的雷是nonce复用:同一个密钥下,只要nonce相同,生成的密钥流就相同。两个不同消息用了相同的密钥和nonce,攻击者把两段密文异或,就直接能恢复出两段明文的异或值,如果再结合明文中的已知结构,很可能完整推出明文。这个事不是理论风险,业内真实发生过多起因nonce复用导致大规模数据泄漏的事件。
OFB(Output Feedback,输出反馈模式)和CFB(Cipher Feedback,密码反馈模式)算是CTR的“前辈”。OFB是通过把AES的输出再反馈回输入,不断生成密钥流;CFB是把上一块密文反馈到加密输入,生成看似随机的内容再与当前块明文异或。这两个模式今天使用场景都比较少,主要因为CTR在同样能做成流密码的同时,还有更好的并行性和更简单的结构。如果你在维护老系统时会碰到它们,新项目里我不建议再选。
2.4 GCM模式:加密同时解决完整性,现代应用的首选
GCM全称Galois/Counter Mode,伽罗瓦计数器模式。它本质上是在CTR模式的基础上增加了一层GHASH认证机制,所以它同时提供机密性和完整性保护,属于认证加密模式家族(AEAD)的一员。
GCM的工作方式可以这样理解:加密部分用的就是CTR的计数器逻辑,把明文异或上密钥流得到密文。认证部分则把所有输入——包括额外认证数据AAD和密文——都送进一个基于伽罗瓦域运算的GHASH函数,生成一个固定长度的认证标签。解密时,接收方先重新计算认证标签,如果和附带的标签不一致,直接拒绝解密结果。
这里有个关键机制需要讲清楚:GCM的认证覆盖了AAD、密文和nonce,所以如果有人篡改了密文中的任何一个bit,或者把两条消息的AAD换掉,认证标签都会对不上。换句话说,GCM把“数据完整性”和“来源可信性”一起解决了。
GCM的巨大优势可以用一个实际项目需求来说明:假设你要设计一个API的响应加密方案,需要加密响应体,还要确保响应体在传输过程中没有被中间人篡改。如果选CBC+HMAC,你得写两套逻辑,一套负责加密,一套负责HMAC计算,还要仔细设计“先加密后MAC”的组装顺序,防止攻击者把密文和标签拆开重组。而GCM一个接口就同时搞定:encrypt(key, nonce, plaintext, aad) -> ciphertext + tag。这就是为什么在TLS 1.3中,GCM系列成为主流的加密套件选择。
不过GCM也不是没有坑。最重要的一条还是nonce唯一性:GCM对nonce复用极其敏感,同一个密钥下只要nonce重复一次,攻击者就可能恢复出用于认证的哈希密钥,从而伪造任意密文——这比CTR的nonce复用后果更严重。因此在实现GCM时,随机生成12字节nonce通常可以接受,但如果加密量大到接近几十GB级别,我建议改用计数器式的nonce管理,确保每个nonce严格唯一。另外,认证标签长度建议使用12字节以上,部分场景使用16字节,千万不要为了省流量把tag截断到4字节或8字节,那会把安全强度直线拉低。
GCM里还有个点很多人会忽略:AAD(Additional Authenticated Data,额外认证数据)。AAD是“要认证但不加密”的数据,比如请求头、版本号、消息类型。AAD非常适合把协议元数据纳入完整性保护。举个我实际用过的例子:在加密日志字段时,我把日志级别和时间戳放进AAD,这样日志级别被篡改后,解密会直接失败,这种设计比把元数据混进明文再统一解密要优雅得多。
2.5 CCM与OCB:特殊场景下的备选
CCM全称Counter with CBC-MAC,是一个比较早期的认证加密模式,在WiFi的WPA2中广泛使用。它把CTR加密和CBC-MAC认证组合在一起,代码实现比较复杂,性能也一般,但胜在专利友好、标准化程度高。如果你的项目必须兼容老设备或者某些硬件加密模块,CCM可能是不得不选的选项。需要注意CCM对nonce长度限制比较严格,通常nonce是7到13字节,实际选型时要仔细看硬件支持。
OCB全称Offset Codebook Mode,是目前学术界评价非常高的认证加密模式,只需要一次AES运算就能同时完成加密和认证,性能非常优秀,设计优雅。但OCB最大的问题是专利历史包袱,早期专利问题导致很多开源项目回避它。虽然部分专利已过期,但如果你在大公司做选型,法务那一关大概率会卡住。所以OCB我一般只在内部工具或非商业项目里用,生产环境还是以GCM为主。
3. 到底怎么选:解密一条从需求到方案的决策路径
3.1 七步选型法:我给你一个可以直接照着走的判断流程
网上很多文章讲AES工作模式选择时,都是从安全性理论出发,给一堆比较表格,最后说“推荐GCM”。这种结论没错,但太粗糙。真实项目里,你还要考虑性能、兼容性、硬件支持、密钥轮换方案、代码维护成本。所以我把自己的选型逻辑压缩成一个七步判断流程,你照着走基本不会出大错。
第一步,确认需求里是否有“认证”要求。这个很关键:如果数据被篡改会造成严重后果,那就必须选认证加密模式,GCM排第一位,CCM作为第二选项。
第二步,判断数据长度分布。如果数据长度都很短,且是随机token之类,CTR模式加独立MAC也可以;如果数据长度不均、包含大量结构化文本,CBC和ECB的填充机制会引入额外长度开销,GCM这类流密码化模式就没有填充开销。
第三步,确认高性能并行场景是否存在。比如网关服务需要同时加密大量请求响应,CTR/GCM因为可以并行处理,性能远优于CBC。
第四步,检查运行环境硬件是否支持AES-NI指令集和GCM专用指令。现代x86和ARM芯片基本都支持AES-NI,但部分老设备、嵌入式环境可能只支持CBC硬件加速。如果你的部署环境不支持GCM的硬件加速,纯软件实现GCM的GHASH部分会有明显成本。
第五步,评估密钥管理和nonce管理的成本。你是否能做到每次加密都生成唯一nonce?如果做不到,CBC+随机IV可能更稳妥,因为CBC的IV重复虽然危险,但GCM的nonce复用后果更致命。
第六步,确认通信协议自身的完整性保护能力。如果你已经有TLS或者MAC层保护,只想在应用层做字段加密,那么用CTR或CBC就够了,不需要每层都叠加认证,避免重复开销。
第七步,写个快速验证demo,测一下加解密速度和密文长度,确认对你业务没有负面影响。
这个流程本质上是在平衡三个东西:安全性、易用性、性能。多数情况下你会得到“选GCM”的结论,但至少你知道了为什么是GCM,也知道了什么情况下不该选GCM。
3.2 场景化选型建议:接口加密、数据存储、大规模日志
我挑几个最常见的场景,给你一套可直接套用的经验值。
第一,API请求响应加密。这通常是短消息、结构已知、需要防篡改。我推荐AES-256-GCM,nonce用12字节随机数,AAD放入API版本号和消息类型。开头加一些版本字段,方便将来密钥轮换。这个组合可以同时提供机密性、完整性、防重放的前提条件。
第二,数据库敏感字段加密存储。比如手机号、身份证号、银行卡号。这里问题在于:你需要不需要对密文做模糊查询?如果需要用密文做精确匹配,那无论什么模式下,相同明文都会产生相同密文,这本身又会泄漏信息。我见过一个折中方案:用字段的HMAC作为检索索引列,用GCM加密作为存储列。查询时先用HMAC定位,再解密展示。这个方案比直接用ECB做确定性加密安全得多。如果确实需要确定性加密,你必须接受同明文同密文的泄漏风险,那我会建议用AES-SIV(RFC 5297)这类专用确定性认证加密模式,而不是ECB。
第三,大规模日志加密。日志的特点是海量、追加写、很少读回。这里的核心矛盾是性能与完整性。用GCM虽然安全,但每一条日志都要算GHASH,CPU开销不低。更合理的方式是:用AES-CTR加密日志内容,再按批计算HMAC,比如每100条日志做一个HMAC头。这样单条追加性能非常快,批量认证也足够安全。需要注意的是,CTR模式下的nonce管理必须用持久化的计数器,不能每次随机生成,否则重启后可能复用nonce。
第四,文件加密。文件通常很大,且会随机读取部分内容。CBC模式因为要求整块链式解密,随机读取不方便;CTR/GCM因为每块独立生成密钥流,随机读取某一范围的明文非常自然。如果你想给加密文件增加完整性保护,GCM是理想选择;如果只做机密性,CTR也可以。有一点要注意:文件加密场景里nonce/IV要写进文件头,同时文件格式里最好留有明文元数据区和加密数据区的区分,将来做版本升级不至于把文件格式锁死。
3.3 密钥与nonce管理:比选模式更重要的工程问题
模式选错了可以被发现、可以改,但密钥和nonce管理如果出了问题,整个加密体系形同虚设。我在aes工作模式选择的话题下,每次都要强调这层:模式只是密码学结构,密钥管理才是安全的根基。
密钥管理的第一条原则是“一应用一密钥”,不同业务模块用完全不同的密钥,避免梭哈式的单密钥风险。第二条是密钥轮换周期化,建议至少每90天轮换一次,泄漏后能快速失效。第三条是密钥存储要独立,不能把密钥和密文放在同一个数据库里,更不能把AES密钥硬编码在代码仓库中。常规做法是用专门的密钥管理服务或在启动时从安全环境变量注入。
再说nonce/IV管理。对于CBC,IV必须随机且不可预测,用加密安全随机数生成器生成16字节。对于GCM,推荐12字节随机nonce,但如果单密钥加密消息数量巨大,建议换成分层的计数器方案,如“固定前缀+计数器”。对于CTR,计数器必须持久化,进程重启后要继续从上次的值递增,不能退回过去。只要记住一件事:在同一个密钥生命周期内,绝对不要出现两个相同nonce/IV。
AAD的使用也很值得讲。GCM的AAD是不加密但参与认证的数据,非常适合放协议元数据。例如我在一个支付回调加密方案里,把回调事件类型放进了AAD。这样攻击者即使能篡改事件类型字段,解密认证时也会立刻失败。这个技巧在CBC+HMAC方案里也有对应实践:把元数据放进HMAC的“关联数据”部分。
4. 我在实际项目里踩过的坑,和你必须避开的雷
4.1 真实案例复盘:ECB泄漏用户手机号前缀
某团队早期做用户数据加密存储,选型时图省事用了AES-128/ECB。上线几个月后,安全团队做风险评估,发现一个非常直观的问题:数据库里相同手机号前七位的记录,密文前缀大量重复。因为手机号本身规律性强,ECB模式下前7位相同意味着第一个明密文块相同,因而密文前缀出现显著的重复模式。攻击者只要拿到密文库,再配合一份常见手机号前缀分布,完全不需要爆破密钥就能推断出用户手机号的大致归属地甚至号码段。
复盘结论就是:ECB永远不能用于结构化数据加密。唯一的例外是你确定数据是完全随机的,比如加密一个随机生成的token,且不需要防重排攻击。我记得后来的修复方案是:全部数据改用AES-256-GCM,密钥轮换,密文格式加了版本前缀,兼容新旧数据的平滑迁移。
4.2 真实案例复盘:CBC的填充oracle漏洞如何被利用
另一个案例是某内部系统采用AES-CBC加密接口参数,但没有做任何认证。安全测试时发现,向解密接口发送畸形密文,让它故意改变最后一个字节,服务端返回的错误信息里能区分“fill error”和“MAC mismatch”(当然它根本没有MAC)。利用这个差异,攻击者通过上千次请求就可以逐字节还原出明文。
这个漏洞的本质不是CBC模式本身,而是缺少认证。后来我给他们的修复建议非常直接:要么把CBC换成GCM,要么在CBC基础上增加HMAC-SHA256,严格采用“先加密后MAC”的顺序,同时将解密失败的错误统一返回同一提示。从那以后,我对“加密方案必须包含完整性保护”这个原则执行得特别严格。
4.3 常见问题速查表:日常开发和排障时直接翻这里
下面这组问题,是我在技术咨询和代码评审中反复遇到的,整理成速查表方便你对照:
| 问题 | 原因分析 | 解决方案 |
|---|---|---|
| 解密时报“Invalid tag” | GCM认证标签不匹配,可能是密文被篡改、AAD不一致、密钥不匹配 | 检查AAD是否一致;确认密文是否被截断或修改;打印tag长度核对 |
| 相同明文加密后密文完全一致 | 使用了ECB模式,或者CBC/GCM的IV/nonce固定 | 检查模式配置;确保每次加密重新生成随机IV/nonce;检查代码是否误用了静态IV |
| 解密出来前半段正确,后半段乱码 | CBC模式解密第一块需要正确的IV,后半段乱码大概率是密文有块错位 | 核对IV是否一致;检查密文是否多删或少加了数据;检查填充是否被破坏 |
| 程序重启后CTR加解密结果对不上 | CTR的计数器没有持久化,重启后从0开始 | 持久化计数器到数据库或文件;或改用随机nonce模式并记录在密文头部 |
| GCM加密速度比预期慢很多 | 硬件不支持AES-NI/GCM指令,GHASH部分纯软件计算 | 检查部署环境CPU标志位;考虑换用支持AES-NI的实例;或改用CBC+HMAC但在性能可接受时优先GCM |
| 加密后的密文长度比明文长很多 | 分块模式存在填充开销,或者存储时把base64结果当成了原始密文 | 检查填充规则;确认是否需要base64编码;GCM耗时几乎不增加长度但tag会额外占用 |
4.4 自查清单:加密代码上线前,问自己这八个问题
我每次给自己项目做加密方案评审时,都会拿一份固定清单过一遍。这份清单包含八项:确认没有使用ECB模式;确认IV/nonce是每次重新生成的,且生成方式基于加密安全随机数;确认使用了认证机制,优先GCM;确认认证标签长度不低于12字节;确认解密失败时错误信息统一;确认密钥存储与密文存储分离;确认代码仓库中没有硬编码密钥;确认密钥有轮换机制和应急预案。
只要这八项全部通过,加密方案在实际项目中的安全风险就已经降到很低的水平。剩下的是持续性的密钥管理和审计。
5. 性能与工程实践:除了安全性,你还得关注这些事
5.1 不同模式的性能特征对比
把AES工作模式选型落到工程层面,性能是一个躲不开的话题。我直接用性能特征把模式分成三类。
第一类是串行加密模式,代表是CBC和CFB。CBC加密必须等前一块密文出来才能继续,所以单线程下吞吐有限。虽然可以对整个消息拆分成多个段用不同IV并行处理,但分段本身需要额外设计。实际项目中如果每秒要加密几百个5KB大小的JSON,CBC在低端虚拟机上CPU占用率会明显偏高。
第二类是并行加密模式,代表是CTR和GCM。CTR天然支持每个计数器块独立加密,可以流水线或SIMD优化。GCM在支持AES-NI和PCLMULQDQ指令的CPU上,GHASH部分的性能损耗很小。我自己实测过,在一台普通云服务器上,AES-256-GCM加密10MB数据,耗时大约只有AES-256-CBC的60%到70%,这个差距在高吞吐场景里非常可观。
第三类是流密码化模式,OFB和CFB,它们的性能特征介于上述两者之间,但今天使用场景很少。
在真实压测中还会发现一个有趣现象:数据量小的时候,不同模式的差距反而不明显,瓶颈经常在加解密库的函数调用和内存分配上。所以如果你的业务请求都是几百字节的短消息,性能不是选型首要因素,安全性和完整性才是。
5.2 密文格式设计:版本前缀、nonce与认证标签的布局
加密方案的工程化,其中一个容易被忽略但又特别重要的点是密文格式设计。我在所有自研加密方案里都会要求格式带版本号。一个典型的GCM密文存储格式可以这样设计:第1字节为方案版本号,第2到第13字节为12字节nonce,第14到第29字节为16字节认证标签,第30字节开始为密文主体。也可以选择把tag放在密文后面。
为什么版本号这么重要?因为加密方案一定会迭代。今天你用AES-256-GCM,明年可能因为合规要求换成带关联数据的某种新模式,或者需要切换密钥长度。如果你的密文没有版本号,迁移时根本无法区分新旧格式,解密代码会变得极其脆弱。我见过一个团队因为没有版本号,在做密钥轮换时把新旧数据全部解密失败,最终只能强制用户重新上传敏感信息。
nonce长度和tag长度的设计也需要规范。AES-GCM在RFC 8452等标准里推荐12字节nonce和16字节tag。有些实现允许tag长度在4到16字节之间动态选择,但为了安全,固定使用12或16字节会减少配置错误。
5.3 多语言环境下的代码提示:用对参数,别被看似相同的接口迷惑
AES-GCM在不同语言、不同加密库里的接口差异往往是坑。比如很多语言里,GCM接口的“authTag”参数必须单独管理,如果你加密时把tag丢了,解密无论如何都会失败。还有一种常见错误:把nonce误当成“salt”,创建密钥时把随机值混进密钥派生过程,造成每次加密结果都不同但无法解密。
我在实际指导中会建议每种语言都写极简的加解密自测用例,在本地先跑通“加密->解密->比对原文”的闭环。这个习惯可以帮你提前发现参数位置、字节序、编码格式三类问题。永远不要在一个大型系统里第一次实现某种模式时,就直接拿生产数据去验证。
6. 最后的实操建议和我的个人体会
在我做过的加密方案评审和实际研发里,最稳妥的默认选择就是AES-256-GCM。它同时解决机密性和完整性,接口清晰,硬件支持广泛,TL协议也验证了它的可靠性。如果你不确定项目该选什么模式,选GCM基本不会犯错。
但我也要提醒一句:模式选对只是起步。你还需要认真做好密钥生命周期管理、nonce唯一性维护、密文格式版本化、错误信息统一化、安全审计常态化。这几个方面加在一起,才算是拿AES做对了事。
最后分享一个我自己的习惯:每次接手一个新项目,我会花半小时把加密相关的代码全部看一遍,核对几个关键点——是否出现过“Zero IV”这种致命写法;是否有静态随机因子;是否有硬编码密钥;解密失败时错误信息是否会把内部状态带出去。很多时候,安全问题就藏在最不起眼的参数里。希望这篇文章能帮你少踩几个我当年踩过的坑。