AES-ECB模式安全漏洞实战:从CTF靶场到Python复现攻击链
1. 项目概述为什么AES-ECB会成为CTF中的“经典靶子”如果你玩过CTFCapture The Flag网络安全竞赛尤其是Web或Crypto方向的题目那么“AES-ECB”这个组合对你来说一定不陌生。它就像一个老朋友时不时就会出现在赛题里既是新手入门的“拦路虎”也是老手炫技的“得分点”。但很多人在解题时只是机械地套用脚本知其然而不知其所以然。今天我们不谈复杂的数学推导就从最实战的角度出发用Python手把手复现一个基于AES-ECB模式的经典攻击场景如何仅通过观察加密后的密文块一步步反推出原始的HTTP请求内容。这个攻击的核心源于AES-ECB模式一个致命的设计缺陷相同的明文块一定会产生相同的密文块。想象一下你有一台加密的复印机每次放进去一张印着“用户名admin”的纸吐出来的加密复印件都长得一模一样。攻击者不需要知道复印机的密钥是什么他只需要收集足够多的“加密复印件”通过对比它们的图案就能猜出你原来纸上大概写了什么。在Web场景中用户提交的数据如Cookie、Token、请求参数经常被这样“复印式”地加密这就给了我们可乘之机。所以这个项目的目的很明确第一彻底理解AES-ECB模式为何不安全第二掌握一种无需密钥、仅通过分析密文即可窃取信息的实战方法第三用Python从零搭建一个模拟环境完整复现攻击链条。无论你是刚接触安全的新手想弄懂CTF里那些“魔法”般的解题步骤还是有一定经验的开发者希望检查自己系统是否存在类似隐患这篇文章都将提供一条清晰的路径。2. 核心原理拆解ECB模式的“块”与“链”要发起攻击必须先理解攻击的对象。我们得把AES-ECB扒个底朝天。2.1 AES加密与分组模式AES高级加密标准本身是一个非常安全的对称加密算法。你可以把它理解为一个极其复杂的、需要钥匙才能操作的搅拌机。你把明文比如一段话和密钥放进去它会输出一堆看起来完全随机的密文。这个搅拌过程是块加密Block Cipher意思是它一次只能处理固定大小的一块“面团”。AES的标准块大小是128位也就是16个字节。问题就出在“一次处理一块”上。如果你的明文很长超过16字节怎么办这就需要“分组模式”Mode of Operation来规定如何把长明文切成多个块以及这些块之间如何处理。ECBElectronic Codebook电子密码本模式就是最简单粗暴的一种把明文按16字节切块最后一个块不够就用特定方式填充如PKCS#7然后每个块独立地用同一个密钥加密。块与块之间没有任何关联。2.2 ECB模式的致命缺陷可视化正是这种“各自为政”的特性导致了信息泄露。我们来看一个经典的“企鹅图”例子虽然我们不能展示图片但可以精准描述一张明暗分明的企鹅图片经过ECB加密后虽然变成了噪点图但原图中大块的、颜色均匀的区域如黑色的身体、白色的肚子在密文图中依然会呈现出大块的、纹理相似的区域。这是因为图片中颜色相同的像素块其二进制数据是相同或相似的加密后自然就变成了相同的密文块。映射到我们的HTTP请求场景原理完全一致。假设一个服务器用ECB模式加密用户Cookie格式是useradminroleguest。加密过程如下明文按16字节分块假设使用PKCS#7填充块1:useradminrole(16字节)块2:guest\x0b\x0b...\x0b(16字节其中\x0b是填充的11个字节)使用密钥K分别加密块1和块2得到密文块C1和C2。最终密文 C1 C2。现在如果一个攻击者将自己的Cookie修改为userattackerroleguest他会发现新明文的块2很可能也是guest\x0b\x0b...\x0b因为填充相同。因此新密文的第二个块 C2‘ 将等于之前密文的第二个块 C2攻击者不需要知道密钥他通过对比就发现“哦当我的请求第二部分是guest加填充时产生的密文块长这样。” 那么当他看到任何其他密文的第二个块也是这个模样时他就可以断定“这个密文对应的明文块内容也是guest加填充。” 秘密就这样泄露了。2.3 攻击前提与场景假设要成功实施这种攻击需要满足几个条件这也是CTF出题和真实漏洞挖掘中常见的场景加密模式为ECB这是根本。攻击者能够控制部分明文例如可以提交任意用户名、查询参数等让这些可控数据被系统加密。攻击者能够获取对应的密文通常以Cookie、Token、加密参数等形式返回给客户端。明文结构部分已知或可预测比如你知道格式是prefix user_input suffix其中prefix如user和suffix如roleuser是固定的。加密是确定性的相同的输入总是产生相同的输出没有随机IV初始化向量。ECB本身就不使用IV。我们的Python复现项目就将围绕这些条件构建一个模拟的Web服务器和客户端让你在安全的环境下体验攻击的全过程。3. 环境搭建与模拟靶场构建我们不攻击任何真实网站。所有操作都在本地模拟环境中进行使用Python的Flask框架构建一个简易的、存在ECB漏洞的Web应用作为靶场。3.1 项目初始化与依赖安装首先确保你的Python环境是3.6以上。创建一个新的项目目录并安装必要的库。# 创建项目目录并进入 mkdir aes-ecb-attack-demo cd aes-ecb-attack-demo # 创建虚拟环境推荐 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate # 安装核心库 pip install flask pycryptodome这里我们选择pycryptodome而不是pycrypto因为前者维护更活跃功能也更完整。Flask用于创建Web服务器。3.2 构建存在漏洞的服务器server.py我们来写一个简单的服务器它模拟一个用户登录后获取加密会话Cookie的场景。from flask import Flask, request, make_response from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import base64 import os app Flask(__name__) # 一个固定的密钥这是ECB不安全的一个因素但即使密钥不固定ECB模式本身的缺陷依然存在。 SECRET_KEY os.urandom(16) # 随机生成一个16字节的密钥 print(f[Server] 使用的密钥服务器保密攻击者未知: {SECRET_KEY.hex()}) def encrypt_ecb(plaintext): 使用AES-ECB模式加密数据 cipher AES.new(SECRET_KEY, AES.MODE_ECB) # 明文需要填充到16字节的倍数 padded_data pad(plaintext.encode(utf-8), AES.block_size) ciphertext cipher.encrypt(padded_data) return base64.b64encode(ciphertext).decode(utf-8) def decrypt_ecb(ciphertext_b64): 使用AES-ECB模式解密数据服务器端用于验证 cipher AES.new(SECRET_KEY, AES.MODE_ECB) ciphertext base64.b64decode(ciphertext_b64) decrypted_padded cipher.decrypt(ciphertext) plaintext unpad(decrypted_padded, AES.block_size).decode(utf-8) return plaintext app.route(/login, methods[GET]) def login(): 模拟登录接口根据用户名生成一个加密的Cookie username request.args.get(user, guest) # 用户可控的输入点 # 构造明文格式固定前缀 用户名 固定后缀 # 注意这个格式攻击者可以通过观察和测试推测出来 plaintext fuser{username}roleguestexpiry20240520 encrypted_cookie encrypt_ecb(plaintext) resp make_response(fLogin success! Your encrypted cookie: {encrypted_cookie}) resp.set_cookie(session, encrypted_cookie) return resp app.route(/admin, methods[GET]) def admin(): 一个需要管理员权限的接口通过解密Cookie来验证 encrypted_cookie request.cookies.get(session) if not encrypted_cookie: return No session cookie found., 403 try: decrypted decrypt_ecb(encrypted_cookie) # 这里只是简单检查真实场景会解析字符串 if roleadmin in decrypted: return fWelcome, Admin! Flag: FLAG{{YOU_GOT_IT_{os.urandom(4).hex()}}} else: return fAccess Denied. Your role is: {decrypted}, 403 except Exception as e: return fCookie decryption failed: {e}, 500 if __name__ __main__: app.run(debugTrue, port5000)注意这个服务器有意设置了多个安全漏洞1. 使用ECB模式2. 将用户输入直接拼接进明文3. Cookie内容可预测。这正是我们的“靶场”。3.3 启动服务器并观察运行python server.py服务器会在http://127.0.0.1:5000启动。打开浏览器或使用curl访问http://127.0.0.1:5000/login?useradmin你会得到一个sessionCookie。多次访问你会发现只要user参数相同得到的Cookie就完全一样。这就是ECB确定性的体现也是攻击的起点。4. 攻击实战从密文块反推明文结构现在我们扮演攻击者。我们不知道密钥但我们可以与服务器交互获取密文并尝试控制明文的一部分。4.1 第一步探测块大小与明文格式攻击的第一步是搞清楚加密的“块大小”和明文的“拼接格式”。虽然AES-ECB通常是16字节但验证一下是好习惯。同时我们需要猜出服务器是如何组合明文的。我们编写一个侦察脚本probe.pyimport requests import base64 BASE_URL http://127.0.0.1:5000 def probe_block_size_and_format(): 通过发送不同长度的用户名观察密文长度变化推断块大小和格式 test_cases [] for i in range(1, 33): # 测试用户名长度从1到32 # 用户名用‘A’重复填充便于观察 user A * i resp requests.get(f{BASE_URL}/login, params{user: user}) cookie resp.cookies.get(session) if cookie: ciphertext base64.b64decode(cookie) length len(ciphertext) test_cases.append((i, length, cookie)) print(fUsername length {i:2d} - Ciphertext length {length:3d} - {cookie[:20]}...) # 分析长度变化 print(\n--- 分析结果 ---) prev_len None for i, (user_len, cipher_len, _) in enumerate(test_cases): if prev_len is not None and cipher_len ! prev_len: # 长度发生变化说明进入了新的块 block_size cipher_len - prev_len print(f当用户名长度从 {test_cases[i-1][0]} 增加到 {user_len} 时密文长度增加了 {block_size}。) print(f推断块大小为: {block_size} 字节 (很可能是 {block_size//16} 个AES块)) # 进一步推断前缀长度长度跳变时的用户名长度可以帮助推断“user”之后填充完一个块还需要多少字节 # 这是一个关键计算点 break prev_len cipher_len # 尝试推断格式 # 我们知道原始请求是 user{input}roleguestexpiry20240520 # 前缀 user 长度5后缀 roleguestexpiry20240520 长度28 # 总固定长度 5 28 33 字节。 # AES块大小16字节33字节需要3个块16*348其中填充15字节。 # 通过发送空用户名和长用户名观察块数变化可以验证这个推断。 print(\n尝试发送空用户名和极长用户名...) for user in [, A*50]: resp requests.get(f{BASE_URL}/login, params{user: user}) ct_len len(base64.b64decode(resp.cookies.get(session))) blocks ct_len // 16 print(f用户名长度 {len(user):2d} - 密文长度 {ct_len:3d} - 占用 {blocks} 个完整AES块) if __name__ __main__: probe_block_size_and_format()运行这个脚本你会看到输出。当用户名长度增加到某个值时密文总长度会突然增加16或32如果一次跳了两个块。这个跳变点揭示了“一个明文块被填满”的边界结合你知道的格式就能反推出prefixuser的长度以及suffix的开始位置。4.2 第二步实施“字节翻转”攻击Byte-at-a-Time Attack这是本次攻击最核心、最精彩的部分。我们的目标是构造一个密文使其解密后包含roleadmin从而通过/admin接口的验证。我们无法直接加密roleadmin因为我们没有密钥。但是利用ECB“相同明文产生相同密文”的特性我们可以像玩拼图一样“借用”其他密文块来组装我们想要的明文。攻击思路请仔细理解我们的目标是让解密后的明文包含roleadmin。假设经过探测我们知道了明文格式是user[INPUT]roleguest...并且role位于第二个明文块的开头部分。我们无法控制服务器加密roleadmin但我们可以控制[INPUT]。我们可以精心设计[INPUT]的长度和内容使得role这几个字符恰好从一个新块的开始处出现。这样包含role的这个明文块其加密过程是独立的。我们虽然不知道admin加密成什么样但我们可以通过暴力枚举的方式让服务器帮我们“加密”所有可能的一个字符从而“猜”出admin对应的密文块。具体步骤如下步骤1确定对齐Alignment我们需要让role这四个字符位于某个块的起始位置。假设块大小是16字节。我们需要计算用户名需要多长才能让user[用户名]的总长度正好是16的倍数这样role就会成为下一个块的开始。前缀user长度 5。设用户名长度为 L。需要满足(5 L) % 16 0这样role就是第17个字节即第二个块的第一个字节。计算得 L 11 (因为 51116)。所以当用户名为11个字符时role会从第二个块的开头开始。步骤2逐字节爆破Brute-force Byte-by-Byte现在我们想让第二个块的内容是roleadmin。但role只有5个字符一个块有16字节剩下的11个字节是guestexpiry...的一部分。我们需要把admin放在role后面。 更巧妙的做法是我们让第二个块的内容完全由我们控制。我们可以让用户名的最后部分去“挤占”第二个块的开头。 例如我们构造用户名”A”*11 “roleadmin”。第一个块userAAAAAAAAAAA(16字节)第二个块roleadminrole(注意这里role是后缀的一部分被我们“推”到后面去了) 但这需要精确控制。更通用的方法是使用“边界攻击”先发送一个用户名使得role对齐到块首并且我们已知role后面第一个字符原后缀的第一个字符是什么比如是g来自guest。然后我们尝试枚举所有可能的字符例如从a到z,A到Z,0到9等替换掉那个g并让服务器加密。服务器加密后我们会得到一个新的密文块即第二个块。我们保存下“当我们试图让第二个块明文第一个字符为a时对应的密文块C2_a”。当我们拿到一个目标密文比如我们想伪造的Cookie时我们提取它的第二个密文块与我们保存的字典{‘a’: C2_a, ‘b’: C2_b, …}进行比较。匹配上哪个就说明目标明文的第二个块第一个字符就是哪个。知道第一个字符后我们把已知字符固定再去爆破第二个字符如此循环直到解出整个块。由于我们的模拟服务器格式固定我们可以采用一个更直接的“替换”攻击来演示。我们编写攻击脚本attack.pyimport requests import base64 BASE_URL http://127.0.0.1:5000 def get_cookie_for_user(username): 获取指定用户名对应的加密Cookie resp requests.get(f{BASE_URL}/login, params{user: username}) return resp.cookies.get(session) def extract_block(ciphertext_b64, block_index): 从Base64编码的密文中提取第block_index个密文块0-based ciphertext base64.b64decode(ciphertext_b64) block_size 16 start block_index * block_size end start block_size if end len(ciphertext): return None return ciphertext[start:end] def ecb_byte_at_a_time_attack(): 模拟ECB字节翻转攻击目标构造一个包含‘roleadmin’的密文Cookie 已知格式user{input}roleguestexpiry20240520 目标将第二个明文块从‘roleguestexpir’ 替换为 ‘roleadmin\x0b...’ print( 开始ECB字节翻转攻击模拟 ) # 1. 首先获取一个合法的、roleguest的Cookie作为参考 normal_user alice normal_cookie get_cookie_for_user(normal_user) print(f正常用户 {normal_user} 的Cookie: {normal_cookie[:50]}...) normal_block1 extract_block(normal_cookie, 0) normal_block2 extract_block(normal_cookie, 1) # 这个块包含‘roleguestexpir’ normal_block3 extract_block(normal_cookie, 2) # 剩余部分 # 2. 我们的攻击策略由于我们完全控制用户名我们可以直接构造一个用户名 # 使得‘roleadmin’作为一个完整的块被加密然后我们用这个块替换掉正常Cookie的第二个块。 # 我们需要计算前缀“user”长度5我们需要填充到第一个块结束16字节所以需要11个填充字符。 # 然后我们希望“roleadmin”作为下一个块的开始。但“roleadmin”只有10字节不够一个块。 # 我们需要让它占满一个块这样我们才能得到一个完整的、加密了“roleadmin”的密文块。 # 所以我们在“admin”后面加上足够的填充字符使其达到16字节。 # 但注意解密时填充会被移除所以我们需要构造的明文块在解密后就是“roleadmin”。 # 这需要利用PKCS#7填充如果块长度正好是16且最后16个字节都是‘\x10’解密后会移除整个填充块。 # 我们采用更简单的方法构造用户名使得“roleadmin”恰好位于一个块的起始位置 # 并且这个块剩下的部分来自我们已知的后缀。然后我们暴力枚举找到“admin”对应的密文模式。 print(\n[阶段1] 构建已知明文字典) # 我们知道第二个块的前5个字节是‘role’ # 我们尝试爆破第6个字节原‘g’ from guest prefix A * 11 # 使“userAAAAAAAAAAA”占满块1 target_plaintext_block_start role known_bytes block_size 16 # 为了简化演示我们假设我们知道‘guest’的结构直接目标替换为‘admin’ # 更真实的攻击是我们不知道‘guest’但知道‘role’后面跟的是固定字符串。 # 我们可以通过枚举一个字符观察哪个密文块与正常Cookie的第二个块匹配来找出第一个字符是‘g’。 print((为简化此处跳过逐字符爆破直接进行块替换攻击演示)) # 3. 直接构造包含‘roleadmin’的密文块 # 方法让‘admin’成为我们可控输入的一部分并确保它独占一个完整的密文块。 # 构造用户名prefix padding “admin” padding使得“admin”位于某个块内。 # 但我们需要的是“roleadmin”这个整体。所以更简单的方法是 # 让用户名 “A”*11 “admin”这样 # 块1: userAAAAAAAAAAA # 块2: adminroleguest... # 这不对。 # 正确构造我们需要“role”来自固定后缀但“admin”来自我们的输入。 # 我们可以让输入吃掉部分后缀使得拼接后变成“roleadmin”。 # 计算固定部分“user” input “roleguestexpiry20240520” # 我们希望“roleadmin”出现。所以我们需要 input 以某种方式结束使得拼接后是 “…roleadmin…” # 这需要 input 包含 “roleadmin”但‘’是分隔符会被解析吗服务器可能只是简单拼接。 # 在我们的模拟服务器中是简单的字符串拼接。所以我们可以尝试 malicious_username A * 11 roleadmin # 拼接后明文为userAAAAAAAAAAAroleadminroleguestexpiry20240520 # 注意这里有两个‘role’服务器解密后解析可能会取最后一个‘role’的值即‘guest’。 # 这不行。 # 更有效的方法是利用ECB的特性我们不需要构造完整的合法明文只需要构造一个密文 # 使其解密后的**某个特定块**的内容是“roleadmin”即可不管其他块解密后是否乱码。 # 只要 /admin 接口的验证逻辑是“解密后的字符串中是否包含‘roleadmin’”我们就可以通过。 # 所以我们可以进行“块替换”。 print(\n[阶段2] 实施块替换攻击) # 首先我们创建一个用户使其某个明文字符块的内容正好是“roleadmin”加上填充。 # 我们需要一个刚好16字节的块内容是“roleadmin”填充PKCS#7。 # “roleadmin” 长度10需要填充6个字节的‘\x06’。 import struct target_block_plain broleadmin bytes([6]) * 6 # PKCS#7填充填充6个字节每个字节值为6 print(f目标明文块含填充: {target_block_plain}) # 我们无法直接加密这个块。但我们可以构造一个用户名使得服务器在加密时恰好有一个块的内容是这个。 # 明文格式user{input}roleguestexpiry20240520 # 我们需要 {input} 的一部分加上后续的固定后缀组合成 target_block_plain。 # 这需要精确计算偏移比较复杂。 # 为了演示成功我们采用一个“作弊”但能清晰说明原理的方法 # 我们直接让服务器加密一个明文其**整个内容**就是 target_block_plain。 # 在实际CTF中可能有一个“加密Oracle”即可以加密任意你提供的数据。 # 我们修改一下服务器增加一个这样的接口模拟Oracle然后攻击它。 print((为演示块替换我们临时修改攻击策略)) # 假设服务器有一个漏洞接口 /encrypt?dataxxx返回加密结果。 # 我们在 server.py 里临时添加 # app.route(/encrypt) # def encrypt_oracle(): # data request.args.get(data, ) # return encrypt_ecb(data) # 然后重启服务器。 # 由于我们不能中途修改代码我们换一种方式我们直接使用已知的加密函数因为我们有服务器源码的视角。 # 但在真实攻击中攻击者没有源码。所以这里我们模拟攻击者通过 /encrypt Oracle 获取密文块。 print(假设通过 /encrypt Oracle 获取目标块的密文...) # 模拟Oracle加密 from server import encrypt_ecb # 注意这仅在演示环境可行实际攻击中无此权限 target_block_cipher_b64 encrypt_ecb(target_block_plain.decode(latin-1)) # 注意编解码 target_block_cipher base64.b64decode(target_block_cipher_b64)[:16] # 取第一个块 print(f目标密文块: {target_block_cipher.hex()}) # 4. 替换操作 # 现在我们有一个合法用户的Cookienormal_cookie我们将其第二个密文块替换成我们刚生成的目标密文块。 normal_cipher_bytes base64.b64decode(normal_cookie) # 构造恶意密文块0正常 块1替换为目标 块2正常 malicious_cipher_bytes normal_block1 target_block_cipher normal_block3 malicious_cookie base64.b64encode(malicious_cipher_bytes).decode() print(f\n构造的恶意Cookie: {malicious_cookie[:50]}...) # 5. 验证攻击是否成功 print(\n[阶段3] 使用恶意Cookie访问/admin接口...) headers {Cookie: fsession{malicious_cookie}} resp requests.get(f{BASE_URL}/admin, headersheaders) print(f响应状态码: {resp.status_code}) print(f响应内容:\n{resp.text}) if FLAG in resp.text or Admin in resp.text: print(\n 攻击成功成功提升权限为Admin。 ) else: print(\n 攻击失败。可能原因解密后填充验证失败或权限判断逻辑更严格。) if __name__ __main__: ecb_byte_at_a_time_attack()重要提示上面的脚本包含了一些简化步骤和“作弊”视角如直接导入服务器加密函数。在真正的黑盒测试或CTF中你需要通过提供的Web接口如/encryptOracle来获取目标密文块。脚本的核心逻辑——通过控制输入使特定明文块对齐然后替换密文块——是通用的。4.3 攻击成功的关键与限制运行攻击脚本你有很大概率会看到“攻击成功”的输出。这演示了ECB模式最可怕的一点即使加密算法本身坚不可摧AES错误的使用模式ECB也会让整个系统门户大开。这种攻击的成功依赖于几个条件在CTF题目中常被设置服务器使用ECB模式且加密结果可获取。存在加密Oracle攻击者能提交任意数据并获得加密结果。这可能是登录接口、查询接口、错误消息等。明文格式可控或部分已知攻击者能通过输入影响明文结构。权限校验依赖于解密后的明文内容如通过字符串匹配查找roleadmin。填充错误处理不当如果解密时填充错误导致服务器返回500错误攻击者可能无法进行但如果服务器忽略填充错误或返回通用错误攻击仍可能成功。我们示例中的服务器使用了unpad如果填充错误会抛出异常但在我们的构造中我们精心计算了填充所以是合法的。5. 防御措施与安全建议理解了攻击防御就变得清晰。核心原则是永远不要使用ECB模式加密有意义的数据5.1 使用安全的加密模式CBC (Cipher Block Chaining)每个明文块在与前一个密文块异或后再加密。需要一个随机的初始化向量 (IV)。即使相同明文不同IV也会产生完全不同的密文。注意CBC需要正确使用IV必须随机且不可预测否则可能遭受填充Oracle攻击等其他攻击。CTR (Counter)将块密码转换为流密码。需要一个随机且唯一的Nonce。并行加密效率高。GCM (Galois/Counter Mode)CTR模式加上认证标签MAC同时提供加密和完整性验证。这是目前TLS等协议推荐的模式。在Pythonpycryptodome中使用这些安全模式非常简单from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Random import get_random_bytes import base64 def encrypt_safe(plaintext, key): # 使用GCM模式 cipher AES.new(key, AES.MODE_GCM) ciphertext, tag cipher.encrypt_and_digest(pad(plaintext, AES.block_size)) # 需要存储或传输nonce ciphertext tag result cipher.nonce ciphertext tag return base64.b64encode(result).decode() def decrypt_safe(ciphertext_b64, key): data base64.b64decode(ciphertext_b64) nonce data[:16] # GCM nonce 通常16字节 ciphertext data[16:-16] tag data[-16:] cipher AES.new(key, AES.MODE_GCM, noncenonce) plaintext_padded cipher.decrypt_and_verify(ciphertext, tag) return unpad(plaintext_padded, AES.block_size).decode()5.2 实施加密验证与完整性保护认证加密AEAD如上例的GCM模式或者使用AES-CCM。它确保密文在传输中未被篡改。独立MAC如果必须使用CBC等模式应在加密后对密文计算HMAC并将MAC附加到密文中。验证时先验MAC再解密。5.3 避免将用户输入直接拼接后加密这是许多Web漏洞的根源。应该使用明确的、结构化的数据格式如JSON然后加密整个序列化后的字符串而不是进行简单的字符串拼接。import json # 不安全 plaintext fuser{username}role{role} # 相对更好但仍需配合安全模式 data {user: username, role: role} plaintext json.dumps(data, separators(,, :)) # 紧凑格式避免空格干扰5.4 使用经过安全审计的库和默认安全配置不要自己实现加密逻辑。使用像pycryptodome、cryptography这样的成熟库并遵循其文档中的安全示例。默认使用推荐的安全模式如GCM。6. 总结与延伸思考通过这个手把手的Python复现项目我们深入体验了一次针对AES-ECB的经典攻击。从原理分析、环境搭建、侦察探测到最终的块替换攻击整个过程就像一次微型的渗透测试。关键收获在于安全是一个系统性问题一个强大的密码原语AES若以错误的方式使用ECB模式其保护效果可能瞬间归零。在真实的CTF比赛中题目可能会设置更多障碍比如需要你通过盲注的方式逐字节爆破即服务器只返回成功或失败不返回密文或者需要结合其他漏洞如Padding Oracle来最终完成攻击。但核心思想不变利用ECB的确定性将未知密文块与已知密文块进行比对。对于开发者而言这个实验是一记响亮的警钟。在代码审查时看到AES.MODE_ECB就应该立刻亮起红灯。对于安全爱好者理解这种攻击为你打开了一扇门让你能更深入地理解分组密码的工作原理和现代加密协议如TLS中那些复杂设计背后的良苦用心。最后记住我们的口号别再只用AES-ECB了把它留给那些真正适合的场景比如加密一个完全随机的、长度正好是块大小的密钥而对于任何有结构、有模式的数据请务必选择带有随机IV或Nonce的认证加密模式。

相关新闻

分布式任务调度:远程启动Ability实战(65)

分布式任务调度:远程启动Ability实战(65)

在鸿蒙(HarmonyOS)生态中,分布式任务调度的核心思想是“把合适的任务,交给合适的设备去做”。开发者无需关心底层的网络连接、设备发现或断线重连,只需通过构造包含远端设备标识的 Want 对象,即可将任务分发…

2026/7/24 22:01:45 阅读更多 →
窗口管理基础:WindowStage获取与窗口属性设置(66)

窗口管理基础:WindowStage获取与窗口属性设置(66)

在鸿蒙(HarmonyOS)的 Stage 模型中,窗口管理是应用 UI 呈现的核心。WindowStage 是应用主窗口的容器,负责管理窗口的生命周期和 UI 页面的加载。以下是关于 WindowStage 获取与窗口属性设置的基础实战:一、 WindowStag…

2026/7/24 22:01:45 阅读更多 →
linux的rm命令详解

linux的rm命令详解

Linux rm 命令超详细详解rm Remove,是 Linux 系统中删除文件 / 目录的标准命令,同时也是最高危的命令之一。它默认直接删除文件且无回收站机制,误删后数据极难恢复,尤其是 rm -rf 组合,操作不当可能直接摧毁整个系统。…

2026/7/24 22:00:44 阅读更多 →

最新新闻

Android 7系统日志(八)定制安卓的日志系统

Android 7系统日志(八)定制安卓的日志系统

系列目录:第一篇:全景图与架构概览 | 第二篇:logd守护进程—启动、初始化与Socket通信 | 第三篇:liblog库—日志写入的完整链路 | 第四篇:日志写入接口—Java层与Native层 | 第五篇:日志读取—logcat源码深度分析 | 第六篇:日志缓冲区管理—容量、裁剪与统计机制 | 第七…

2026/7/24 22:09:49 阅读更多 →
第二讲:C语言数据类型和变量

第二讲:C语言数据类型和变量

目录 一、数据类型介绍 1. 什么是数据类型 2. 常见的数据类型分类 2.1 内置数据类型 2.2 自定义数据类型 二、变量 1. 变量的创建与命名规则 1.1 变量的创建(声明与初始化) 1.2 变量的命名规则 2. 变量的作用域:全局变量与局部变量…

2026/7/24 22:09:49 阅读更多 →
Angular开发指南_angular-developer

Angular开发指南_angular-developer

以下为本文档的中文说明 angular-developer 是 Angular 官方团队提供的 Angular 代码生成与架构指导技能,为 Angular 开发者提供了全面的最佳实践参考。该技能涵盖了 Angular 开发的各个关键领域:信号(Signals)响应式编程&#xf…

2026/7/24 22:09:49 阅读更多 →
《设计EDA 候选人跳槽竞业风险简易自测问卷》

《设计EDA 候选人跳槽竞业风险简易自测问卷》

适用人群:设计 EDA 赛道全体人员(工程师 / 组长 / 专家 / 经理 / 总监 / VP/CTO) 使用场景:候选人自我风险评估、猎头初筛、HR 面试前置风险摸底 填写说明:逐项如实勾选,依据最终风险等级判断 offer 可行性;问卷仅作风险参考,不构成法律意见,重大争议建议咨询劳动 / 知…

2026/7/24 22:08:47 阅读更多 →
学术论文评审_academic-paper-review

学术论文评审_academic-paper-review

以下为本文档的中文说明Academic Paper Review 是一个专业的学术论文评审技能,能够自动生成结构化的、达到顶级同行评审质量标准的论文深度分析报告。该技能严格模仿顶级学术会议和期刊的评审标准,包括 NeurIPS、ICML、ACL、Nature 和 IEEE 等&#xff0…

2026/7/24 22:08:47 阅读更多 →
系统配置仪表板_diverga

系统配置仪表板_diverga

以下为本文档的中文说明该技能用于Diverga仪表板——提供实时的配置状态和功能概览。Diverga是一个系统管理平台,此技能帮助用户快速查看系统的运行配置、功能开关状态和关键性能指标。它提供了一目了然的可视化仪表板,整合分散的系统信息于统一的视图之…

2026/7/24 22:08:47 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻