1. 初识Chrome开发者工具与前端加密
每次遇到前端加密的登录表单时,你是不是也头疼过?明明用BurpSuite抓到了数据包,却因为数据被加密成了一串乱码而无法进行后续测试。别急,今天我们就用Chrome开发者工具(F12)这把瑞士军刀,带你一步步拆解前端AES加密的黑盒子。
先说说我最近遇到的一个真实案例:某电商平台的登录接口,提交的用户名和密码都变成了类似"U2FsdGVkX1+3C7gT..."的密文。常规的爆破手段瞬间失效,但通过F12的Sources面板,不到10分钟就找到了加密逻辑。你会发现,前端加密就像魔术师的帽子——看似神秘,一旦知道机关在哪,一切都有迹可循。
开发者工具的核心三板斧:
- Elements面板:查看DOM结构和绑定的事件监听器
- Sources面板:调试JavaScript代码的作战指挥部
- Network面板:监控所有网络请求的哨兵
特别提醒新手朋友:遇到加密别慌,90%的网站用的都是标准加密库(比如CryptoJS),我们要做的只是找到密钥和调用方式。就像破解魔术不是要成为魔术师,而是找出道具的隐藏机关。
2. 定位加密函数的实战技巧
2.1 从事件监听器顺藤摸瓜
最近审计一个OA系统时,发现登录按钮绑定了三个click事件。这时候不要蛮干,教你个巧妙的方法:在Elements面板选中登录按钮,右侧Event Listeners选项卡会显示所有绑定事件。逐个点击右边的JS文件链接,就像侦探翻看嫌疑人的档案。
有个小技巧值得分享:在可疑事件上右键选择"Remove"临时移除,然后尝试提交表单。如果加密功能失效,恭喜你找到了关键入口!我在某次渗透测试中,就用这个方法快速锁定了加密函数所在的loginHandler.js文件。
2.2 全局搜索的妙用
当代码量较大时,试试Ctrl+Shift+F全局搜索这些关键词:
encrypt AES CryptoJS mode/padding(如CBC、PKCS7) key/iv(密钥和初始化向量)上周分析某金融平台时,直接搜索"CryptoJS.AES.encrypt"就定位到了加密核心代码。记住,优秀的猎人懂得使用工具——开发者工具的搜索支持正则表达式,比如用encrypt.*\(.*\)可以快速找到所有加密函数定义。
2.3 调用栈分析实战
在Network面板找到登录请求,右键选择"Replay XHR"会重新发送请求。这时立即切换到Sources面板,点击右下角的"{}"美化代码格式,然后在可疑位置打上断点。
举个例子:在某次测试中,我在Network面板发现请求体有个encryptedData字段,于是在Sources对所有包含这个字段名的JS文件设断点。当断点触发时,Call Stack面板清晰显示了加密函数的调用路径:login() -> encryptPayload() -> CryptoJS.AES.encrypt()。
3. 提取加密参数的完整流程
3.1 密钥提取的三种姿势
- 硬编码密钥:直接在代码里找到类似这样的定义
const key = "5tgbYHN&UJM8ik,";动态生成密钥:需要跟踪密钥生成函数。曾遇到一个网站用用户ID拼接时间戳再MD5哈希生成密钥,这时就要在密钥生成函数处打条件断点。
密钥协商:最复杂的情况,比如通过RSA交换AES密钥。这时要重点关注WebSocket通信或初始化接口的返回数据。
3.2 解密AES配置参数
标准的AES加密通常需要这些参数:
{ key: "1234567890abcdef", // 16/24/32字节长度 iv: "abcdef1234567890", // 16字节初始化向量 mode: CryptoJS.mode.CBC, // 常见模式有ECB/CBC/CFB/OFB padding: CryptoJS.pad.Pkcs7 // 常见填充方式 }实测中发现,80%的网站使用CBC模式+Pkcs7填充。有个快速验证的方法:在Console执行CryptoJS.AES然后按Tab键,会自动补全可用方法和属性。
4. 编写自动化测试脚本
4.1 Python实现方案
首先安装必备库:
pip install requests pycryptodome这里给出一个完整的AES加密模拟脚本:
from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 def encrypt_aes(plaintext, key, iv): cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode()) ciphertext = cipher.encrypt(pad(plaintext.encode(), AES.block_size)) return base64.b64encode(ciphertext).decode() # 从F12提取的参数 key = "5tgbYHN&UJM8ik," iv = "abcdef1234567890" username = "admin" password = "123456" encrypted_creds = encrypt_aes(f'{{"user":"{username}","pwd":"{password}"}}', key, iv) print(f"加密结果: {encrypted_creds}")4.2 直接调用JS函数
对于复杂的加密逻辑,可以用PyExecJS直接调用前端JS代码。把找到的加密函数保存为encrypt.js:
function encryptData(data, key, iv) { // 这里是前端原始加密代码 return CryptoJS.AES.encrypt(data, key, {iv: iv}).toString(); }Python调用代码:
import execjs with open('encrypt.js', 'r', encoding='utf-8') as f: ctx = execjs.compile(f.read()) encrypted = ctx.call('encryptData', '明文数据', '密钥', '初始化向量') print(encrypted)5. 高级调试技巧与坑点排查
5.1 格式化混淆代码
遇到压缩过的JS代码时,点击Sources面板左下角的{}美化按钮。但要注意:美化后的代码变量名可能仍然难以理解,这时要重点关注以下几个特征:
- 调用CryptoJS相关方法的位置
- 包含encrypt/decrypt字样的函数
- 处理key/iv等参数的逻辑分支
5.2 内存断点技巧
对于动态生成的密钥,可以在Console执行:
(function(){ let _key = ""; Object.defineProperty(window, "secretKey", { set(v){ _key=v; debugger; }, get(){ return _key; } }); })();这段代码会在密钥被设置时自动触发断点,我在分析某区块链网站时靠这个方法抓到了动态密钥。
5.3 常见报错解决方案
- Invalid key length:检查密钥是否是16/24/32字节
- Invalid IV length:IV必须是16字节
- Padding error:尝试更换padding模式(如PKCS7改为ZeroPadding)
- Unicode编码问题:确保所有字符串统一编码(通常UTF-8)
记得去年遇到一个奇葩案例:前端加密时先用Base64解码密钥字符串,再用于AES加密。这种隐藏逻辑只有通过单步调试才能发现。
6. 安全思考与防御建议
虽然我们演示了如何逆向前端加密,但要清醒认识到:前端加密不能替代HTTPS等传输层安全措施。它的主要价值在于:
- 增加自动化攻击门槛
- 防止请求被直接重放
- 避免敏感信息明文出现在日志中
对开发者的建议:
- 避免将主密钥硬编码在前端代码中
- 考虑使用Web Crypto API替代第三方加密库
- 为每个会话生成临时密钥
- 配合使用HMAC进行完整性校验
某次安全评估中发现,有个系统虽然用了AES加密,但密钥居然是"password123"。这种安全防护反而会给人虚假的安全感。