当前位置: 首页 > news >正文

Web Crypto API实战:AES-CBC加密逆向分析与Node.js复现

1. 项目概述:一次教科书式的AES逆向分析

最近在分析一些教育类应用的登录流程时,遇到了“升学e网通”这个案例。它的密码加密方式让我眼前一亮——这竟然是一个相当标准的AES加密实现。在如今各种魔改、混淆、自定义算法横行的逆向环境中,能遇到一个规规矩矩遵循标准协议的加密,简直像在沙漠里找到了一片绿洲。对于逆向新手来说,这无疑是一个绝佳的练手材料,因为它剥离了那些令人头疼的“黑盒”操作,让你能清晰地看到数据从明文到密文的完整、标准的转换过程。这个案例的核心,就是定位到其前端JavaScript代码中调用Web Crypto API进行AES-CBC加密的逻辑,并完整复现其加密过程,最终实现密码的模拟加密。整个过程涉及前端代码调试、加密参数提取和算法复现三个关键环节。

2. 逆向环境与目标分析

2.1 目标网站与工具准备

我们的目标是“升学e网通”的登录页面。在开始之前,需要准备好一套顺手的逆向工具链。浏览器自然是核心战场,Chrome或Edge的开发者工具是首选,其Sources面板和Network面板将是我们主要的侦察阵地。为了更高效地调试JavaScript,特别是处理经过压缩或混淆的代码,一个格式化工具(Pretty Print)是必不可少的。此外,因为涉及加密操作,我们需要一个能够执行JavaScript代码片段的环境,浏览器的Console面板就很好用,但对于更复杂的复现,也可以准备一个Node.js环境。最后,为了验证我们的复现结果,一个能够发送HTTP请求的工具如Postman或Hoppscotch也会很有帮助。

2.2 登录流程初探与加密定位

打开登录页面,输入账号密码(请务必使用测试账号),点击登录前,先打开开发者工具的Network面板,并勾选“Preserve log”。点击登录后,你会看到浏览器发起了一个登录请求。重点关注这个请求的Request Payload,你会发现密码字段(通常叫password)已经不再是明文,而是一长串看似随机的字符,这证实了密码在客户端被加密了。我们的任务就是找到生成这串字符的JavaScript代码。通常,加密函数会在点击登录按钮时被触发。我们可以在Sources面板中,对可能的按钮点击事件监听器或者表单提交事件打上断点,然后再次点击登录,让代码执行暂停在加密发生之前。另一种更直接的方法是,在Network面板中找到那个登录请求,右键点击它,选择“Copy -> Copy as cURL”,然后粘贴到一个文本编辑器中,搜索password这个关键词,看看它的值是什么样子,这能给我们一个关于密文格式(比如是否是Base64编码)的初步印象。

3. 核心加密逻辑的定位与解析

3.1 搜索与追踪加密入口点

在庞大的前端代码库中寻找加密函数,需要一些技巧。最直接的方法是在Sources面板中,对所有已加载的JavaScript文件进行全局搜索。搜索关键词可以尝试“encrypt”、“AES”、“CBC”、“password”、“crypto”等。在“升学e网通”的案例中,通过搜索“encrypt”或“AES”,我们很快就能定位到关键代码段。这些代码通常不会深度混淆,因为Web Crypto API的使用本身比较标准。找到疑似函数后,在其第一行打上断点,然后再次触发登录操作。代码执行会在此处暂停,这时我们就能进入这个函数内部,一步步观察它如何工作。

3.2 标准AES-CBC参数剖析

当断点命中后,我们进入函数内部单步执行(F11)。关键的发现就在这里:代码中明确使用了crypto.subtle.encrypt这个API。这是现代浏览器提供的用于执行底层加密操作的Web Cryptography API接口。我们需要在调试器中仔细观察传递给这个函数的参数。一个标准的AES-CBC加密调用通常如下所示:

const encrypted = await crypto.subtle.encrypt( { name: "AES-CBC", // 算法模式 iv: ivArrayBuffer, // 初始化向量 }, keyObject, // 加密密钥 plaintextArrayBuffer // 明文数据 );

从这里,我们必须提取出三个最关键的参数:

  1. 密钥 (Key):这是加密解密的根本。它可能是一个固定的字符串(硬编码),也可能是从某个接口动态获取的,或者是通过某种算法(如PBKDF2)从用户密码派生而来。在调试器中,找到keyObject变量的来源至关重要。
  2. 初始化向量 (IV, Initialization Vector):CBC模式必需的参数,一个随机或固定的字节序列,用于确保相同的明文加密每次产生不同的密文。我们需要找到ivArrayBuffer的值。
  3. 明文数据 (Plaintext):即用户输入的原始密码。我们需要确认在加密前,密码是否经过了其他处理(比如添加了盐值salt,或者进行了特定的编码转换)。

在“升学e网通”的实例中,经过调试发现,其密钥是固定值,通常是一个字符串(如“某固定密钥”),并通过TextEncoder转换为ArrayBufferIV也是一个固定值。这正是它“标准”和“简单”的地方——所有加密要素都是静态可知的,没有动态协商或复杂变换。

注意:在调试时,对于ArrayBuffer类型的数据,直接console.log打印出来是看不到具体内容的。你需要将其转换为十六进制字符串或Base64字符串来查看。例如,在Console中执行btoa(String.fromCharCode(...new Uint8Array(ivArrayBuffer)))可以将ArrayBuffer转为Base64查看。

3.3 数据编码与传输格式确认

crypto.subtle.encrypt返回的结果也是一个ArrayBuffer。前端需要将这个二进制密文转换为字符串才能通过网络传输。常见的转换方式是Base64编码。我们需要在代码中紧接着加密函数之后,寻找类似btoaBuffer.from().toString('base64')或者引入的第三方Base64库的调用。在“升学e网通”中,加密后的ArrayBuffer被直接转换为Base64字符串,然后作为password字段的值发送。这一步的确认,保证了我们复现算法后,输出格式与目标完全一致。

4. 加密算法的完整复现

4.1 复现环境选择:Node.js

既然我们已经在前端浏览器环境中搞清了所有参数和步骤,现在就需要在一个独立的环境(比如后端或脚本)中复现这个加密过程。Node.js是一个完美选择,因为它内置了crypto模块,功能强大且与Web Crypto API有一定相似性。我们将使用Node.js的crypto模块来重写加密函数。

4.2 关键参数提取与验证

首先,将我们在浏览器调试中抓取到的关键参数记录下来:

  • 密钥明文:假设我们从代码中看到密钥字符串是“this_is_a_secret_key”
  • IV明文:假设我们看到IV是“initial_vector_iv”
  • 算法细节:AES-CBC,密钥长度可能是128、192或256位。这通常由密钥字符串的长度决定。一个16字节(128位)的密钥很常见。

这里有一个非常重要的步骤:验证这些参数的正确性。我们可以写一个简单的Node.js测试脚本,用这些参数加密一个已知的密码(比如“123456”),然后将输出的Base64密文,与我们在浏览器Network里捕获到的、用相同密码登录时产生的密文进行比对。如果一致,恭喜你,参数正确;如果不一致,就需要回头检查密钥或IV的提取是否正确,或者密码在加密前是否被做了其他处理(例如,是否在密码前后拼接了其他字符串)。

4.3 Node.js 复现代码详解

以下是一个完整的Node.js复现代码示例,包含了详细的注释:

const crypto = require('crypto'); function encryptPassword(password) { // 1. 定义固定的密钥和IV(此处为示例,需替换为实际抓取的值) const KEY_STRING = 'this_is_a_secret_key'; // 替换为实际密钥 const IV_STRING = 'initial_vector_iv'; // 替换为实际IV // 2. 处理密钥:AES-128-CBC要求密钥长度为16字节。 // 如果密钥字符串不是16字节,需要进行处理。常见方法是取MD5哈希值的前16位,或者直接使用字符串的字节表示(如果刚好16字节)。 // 假设我们的密钥字符串就是16字节,则直接使用。 const key = Buffer.from(KEY_STRING, 'utf-8'); // 将字符串转为Buffer // 如果密钥长度不足16字节,可以填充;如果超过,可以截取。这里假设KEY_STRING设计时就是16字节。 if (key.length !== 16) { console.warn(`密钥长度(${key.length}字节)非标准16字节,可能需特殊处理。`); // 一种常见做法:使用密钥字符串的MD5值作为密钥(16字节) // const key = crypto.createHash('md5').update(KEY_STRING).digest(); } // 3. 处理IV:CBC模式要求IV为16字节。 const iv = Buffer.from(IV_STRING, 'utf-8'); if (iv.length !== 16) { console.warn(`IV长度(${iv.length}字节)非标准16字节,可能需特殊处理。`); // 同样,可以截取或填充至16字节。例如,用0填充:const iv = Buffer.alloc(16, 0); iv.write(IV_STRING); } // 4. 创建Cipher对象,指定算法为'aes-128-cbc',使用上文的key和iv。 const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); // 5. 执行加密:更新(传入明文)并最终化(获取全部密文)。 let encrypted = cipher.update(password, 'utf8', 'base64'); // 输入编码utf8,输出编码base64 encrypted += cipher.final('base64'); // 完成加密,追加剩余输出 // 6. 返回Base64格式的密文。 return encrypted; } // 测试 const testPassword = 'my_password_123'; const encryptedPassword = encryptPassword(testPassword); console.log('明文密码:', testPassword); console.log('加密后(Base64):', encryptedPassword);

代码关键点解析

  • crypto.createCipheriv:这是核心函数,用于创建加密器。aes-128-cbc指定了算法和模式。如果密钥是24字节或32字节,则对应aes-192-cbcaes-256-cbc
  • cipher.updatecipher.final:这是流式加密的典型用法。update可以分块处理数据,final输出最后一块并清理资源。对于密码这种短数据,一次updatefinal即可。
  • 编码一致性update方法的第二个参数是输入编码(‘utf8’),第三个参数是输出编码(‘base64’)。这必须与前端加密时的处理方式一致。前端如果明文是字符串,通常也是UTF-8编码;输出如果是Base64,这里就选Base64。

4.4 复现结果验证与抓包比对

运行上述Node.js脚本,得到一个Base64加密字符串。然后,打开浏览器,在“升学e网通”登录页面,输入你在Node.js脚本中使用的相同测试密码(my_password_123),抓取登录请求。对比抓包数据中的password字段值,和你脚本输出的值。如果两者完全一致,那么你的逆向复现就成功了。这意味着你完全掌握了它的加密逻辑,可以在任何需要模拟登录的地方使用这个加密函数了。

5. 逆向过程中的常见问题与深度思考

5.1 典型问题排查清单

即使面对如此标准的加密,在逆向过程中也可能遇到一些坑。下面是一个快速排查表:

问题现象可能原因排查思路与解决方案
Node.js加密结果与浏览器抓包结果不一致1. 密钥/IV提取错误或编码不一致。
2. 密码明文在加密前被预处理(如加盐、拼接)。
3. AES密钥长度或模式不匹配(如误用ECB模式)。
4. 输出编码不一致(如浏览器是Base64 URL Safe)。
1.核对参数:在浏览器调试态,将密钥、IV的ArrayBuffer转为Hex或Base64,与Node.js脚本中使用的Buffer进行严格比对。
2.追踪明文:在加密函数入口打断点,查看即将被加密的明文数据到底是什么,确认是否就是原始密码字符串。
3.检查算法:确认浏览器crypto.subtle.encryptname参数确实是”AES-CBC”,并确认密钥字节数对应的AES变体(128/192/256)。
4.检查编码:对比抓包密文和Node.js输出,看是否存在+/-//_的替换,这是Base64 URL Safe的特征。
无法在代码中搜索到“encrypt”、“AES”等关键词1. 代码被重度混淆,函数名变量名被替换。
2. 加密逻辑被封装在Web Worker或异步加载的模块中。
3. 使用了非标准的加密库(如CryptoJS)。
1.格式化与搜索:使用开发者工具的格式化功能,然后搜索更底层的API名,如”subtle.encrypt””CBC”
2.网络包搜索:在Network面板,搜索所有加载的JS文件内容(Chrome支持在Network面板下直接搜索文件内容)。
3.事件监听:在登录按钮上查看事件监听器,找到对应的处理函数,逐步跟进。
密钥或IV看起来是动态的密钥或IV可能从服务器接口获取,或由前端代码动态生成。1.Network搜索:在登录前的网络请求中,寻找可能返回密钥或IV的接口。
2.代码生成逻辑:如果动态生成,需逆向生成算法(如从某个固定字符串派生)。此时,标准AES的“简单”部分可能就不复存在了。

5.2 关于“标准”加密的思考与安全启示

“升学e网通”采用标准AES-CBC加密,从逆向学习角度是福音,但从安全角度却暴露了问题。固定密钥和固定IV是安全大忌。这意味着所有用户的密码密文,在已知明文攻击下是脆弱的。一旦攻击者通过某种方式知道了密钥,所有历史通信的密码都可能被解密。更佳的做法应该是:

  1. 使用HTTPS:这是基础,确保传输过程安全。
  2. 非对称加密:前端使用公钥加密密码,后端用私钥解密。这样前端无需存储密钥。
  3. 动态密钥协商:如使用ECDH等密钥交换协议。
  4. 至少使用随机IV:即使使用对称加密,每次加密使用随机IV,能保证相同明文加密结果不同。

作为开发者,在设计系统时,应避免将对称加密的密钥硬编码在前端代码中。作为安全研究人员或逆向学习者,这个案例则清晰地展示了,“标准”不等于“安全”。逆向工程不仅能帮助我们理解逻辑、实现自动化,更能让我们从攻击者的视角审视系统脆弱点,这对于构建更健壮的系统至关重要。

5.3 从该案例延伸的逆向学习路径

掌握了这个标准AES案例后,你可以尝试挑战更复杂的场景:

  1. 魔改AES:遇到密钥不是直接使用,而是经过一个自定义函数变换;或者AES的S盒被替换。
  2. RSA加密:定位前端公钥,学习如何用Node.js的crypto模块进行RSA加密。
  3. 哈希加盐:密码不是加密,而是进行MD5(password + salt)SHA256哈希,需要找到salt的生成逻辑。
  4. 代码混淆对抗:当核心函数名被混淆成a0b1c2时,如何通过调用栈、参数类型和网络行为来定位关键函数。

每一次逆向都是一次解谜。从简单的、标准的算法入手,建立信心和方法论,再逐步攻克更复杂的堡垒,这才是技术能力提升的扎实路径。这个“升学e网通”的案例,无疑是一个近乎完美的起点。

http://www.jsqmd.com/news/1100345/

相关文章:

  • Mac系统下Jmeter接口压测实战:从环境搭建到性能分析
  • AgentScope 2.0
  • 低场MRI仿真系统设计与磁场不均匀性校正技术
  • AI 编程这事,已经开始变味了
  • 工业蒸汽流量计首选品牌:高精度与高稳定性双保障
  • 基于YOLO的目标检测论文高效改进策略:从注意力机制到工程实践
  • 计算机毕业设计之高校精品课网站
  • AVR单片机CCL与CRC模块实战:硬件逻辑与数据完整性设计
  • 别再手动移位了!用Verilog实现PRBS7并行输出(附10比特并行源码)
  • 014、NLSN非局部稀疏网络:稀疏注意力机制的高效计算与实现
  • 50元玩客云刷Armbian变身家庭服务器:保姆级TTL刷机避坑指南(附固件包)
  • 为AI Agent构建可靠邮件中枢:从协议原理到自动化实战
  • 通道轮循,杜绝支付中断
  • Visual C++运行库终极修复指南:3分钟解决所有软件启动错误
  • MoeKoe Music开源音乐客户端:重新定义二次元音乐体验的挑战与实现
  • 每天复制粘贴客户反馈?教你用个微自动汇总接口解放双手
  • ClickHouse 分布式表:从分片路由到副本同步,列式存储的分布式查询引擎
  • 工业级Modbus协议栈架构深度解析:FreeModbus V1.6主机模式技术实现全解
  • HFSS 2021R1求解器怎么选?从天线设计到SI/PI,手把手教你避开求解类型选择坑
  • 【Springboot毕设全套源码+文档】基于springboot大学生社交平台的设计与实现(丰富项目+远程调试+讲解+定制)
  • iOS激活锁绕过完全指南:使用applera1n免费解锁iPhone 6s-X设备
  • 法国公司 i-TRACING 可打破 半导体产业链 “有工具、无人才、难运维” 的 OT 网络安全僵局
  • ChatGPT数据分析避坑手册:87%用户忽略的3个合规雷区(GDPR/等保2.0/内部审计红线全标注)
  • 香橙派Zero 3主线Linux移植避坑实录:手把手搞定BL31、Crust与U-Boot编译
  • 不写代码也能用GPT-5.5 搞定数据分析?Python零基础实测
  • Flutter 动画性能优化:从 60fps 到丝滑体验的工程化调优
  • MultiFunPlayer终极指南:15分钟快速掌握设备同步神器
  • 基于AES-256的CMAC算法实现与消息认证码技术详解
  • 跟AI学一手之渲染隔离
  • Java毕设选题推荐:基于 SpringBoot 的休闲棋牌室经营管理系统的设计与实现 基于 SpringBoot 的棋牌室计时计费管理平台【附源码、mysql、文档、调试+代码讲解+全bao等】