从JS文件泄露到数据解密:一次RSA私钥暴露的实战复盘

📅 2026/6/29 11:06:38
从JS文件泄露到数据解密:一次RSA私钥暴露的实战复盘
1. 事件背景与问题发现那天我正在对一个移动应用进行常规安全测试发现所有请求和响应数据都被加密了。初步判断是AES加密但尝试了各种常见测试方法都无功而返。就在准备放弃时我习惯性地翻查了前端JS文件突然在datalk-crypto.js中发现了异常——这里竟然硬编码了完整的RSA公私钥对更令人惊讶的是这个文件还包含了RSA和AES的完整加密实现而且关键函数都没有做代码混淆。继续分析发现应用使用AES加密业务数据endata字段而AES密钥ranStr是通过RSA加密后传输的enkey字段。这意味着只要获取RSA私钥就能解密出AES密钥进而解密所有业务数据。2. 技术原理分析2.1 混合加密机制解析这套系统采用了典型的RSAAES混合加密方案RSA非对称加密用于安全传输AES密钥公钥加密enkey RSA_Encrypt(ranStr)私钥解密ranStr RSA_Decrypt(enkey)AES对称加密用于业务数据加密数据加密endata AES_Encrypt(rawData, ranStr)数据解密rawData AES_Decrypt(endata, ranStr)2.2 关键漏洞点问题出在以下设计缺陷私钥硬编码私钥直接存储在客户端JS文件中缺乏密钥轮换所有会话使用相同的RSA密钥对无混淆保护加密算法实现可被直接分析密钥传输设计AES密钥通过RSA加密但私钥可获取3. 漏洞利用实战3.1 提取RSA私钥首先从JS文件中提取完整的PEM格式私钥const privateKey MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJGBH7NDV2mU5ro88K0CEJ75gXVxxPzHGUHzTdpVt9b8bWPfjAJv9c1MQ9qvOHIkVCylmVfrbEwGCIArUx3osMPkQEWulH9MaxS2PDShazmYzqwcV4DGzJAWK4ePmpFXFrxk63jIFLM6Lj5Quq1H7ePTZAEHIU98OQ0VttNX3zhAgMBAAECgYBj04YPNB1tt5XQomyxFeCXYTD7hZGTp3lhsO5x5Ctb2RWn1sA1DFA95j6GQsN7HS/qOGa208SnXUJki/VLkbPJLaVY8EpBvtbx/MjmH7SWqH1jZeN6pTA4HEw56fRDY/VyjxOrEPE9zNRK4sYoyv8rrstPD1aaf2bTZ4ShnXFjQJBANX2BHLyFliL9sYUIvYmMfG/5LG0A3RUU0itNItrIB/4ck2y5IQxLwkr6YAVfPtFuA9xiLGIhs7mbO2BFS7XnDcCQQCuF9IR24jo6jiDkbc7huePNyq6xZZFSkgBoTQQrP8xGyVIZR391wjBgWeuCbD9ylNobFF9TJM20gZN03Po5OnAkBp/VoM2vHI4WTYkMcU6qzZFfcjNIp1iQRMv0iFPAcI71koNlNPTNIBGBiuk3Z7PvlD5TkSwRAO1ETnjodA0hwXAkEAggf8uaXVztISwAK4ceJ68mfXEjphCMyLWQZFcSBBO0yjJYhdUnWJdgdrby9wlPIpAXkB5ZTMqycl5Nv2KT/BQJBALpCgJAbtdupZFhXglYJJ6ZnwNosCEwRlmczOGKdoF2x88t6k37aOybiBFq750R0zDKxwoafulvPh6pQuF3wI;3.2 解密AES密钥使用JSEncrypt库进行RSA解密// 在浏览器控制台执行 const encrypt new JSEncrypt(); encrypt.setPrivateKey(privateKey); const decryptedKey encrypt.decrypt(JmwLTbfe1HyBAUBwdwLeaWQZGPqqdZ24ot1kSNgEpCXwz8eGtXpa5ci0qq7hM7WNgxxyntE4Qb1C6F4RKAPulRGKs2DWyVh0Qa4aUhPhBzod/AXGEIzaeh/NL2WDmrXNr8GoPQSEisPpbeQEuWNHFJKQiBfcGaJhXYTmVDEU); console.log(AES Key:, decryptedKey);3.3 解密业务数据获得AES密钥后使用在线工具或crypto-js解密数据// 使用CryptoJS解密示例 const decryptedData CryptoJS.AES.decrypt( encryptedEndata, decryptedKey, { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ).toString(CryptoJS.enc.Utf8);4. 完整攻击链还原信息收集分析应用网络请求发现加密数据源码审计查找前端加密相关JS文件密钥提取从未混淆的JS中获取硬编码私钥流量解密拦截获取enkeyRSA加密的AES密钥用私钥解密enkey得到ranStr用ranStr解密endata获取原始数据数据篡改可选修改数据后用ranStr重新加密用公钥加密ranStr替换原enkey5. 防御方案建议5.1 前端加密最佳实践避免硬编码密钥使用动态密钥分发机制考虑使用Web Crypto API增强代码保护// 混淆后的密钥处理示例 const keyParts [ MIICdwIBADANBgkqhkiG, 9w0BAQEFAASCAmEwg, // ...其他分片... ]; const privateKey keyParts.reverse().map(p atob(p)).join();传输层优化每个会话使用临时RSA密钥对实现完善的密钥轮换机制5.2 服务端检测方案# 示例检测异常解密请求 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend def detect_privatekey_leak(requests): suspicious_patterns [ MII, BEGIN RSA PRIVATE KEY, Proc-Type: 4,ENCRYPTED ] return any(pattern in req.body for pattern in suspicious_patterns)6. 深度防护策略6.1 多层加密体系建议采用以下架构传输层TLS 1.3 证书固定应用层临时密钥交换ECDHE每会话独立AES密钥数据层敏感字段单独加密6.2 实时监控方案// 前端密钥使用监控 const originalDecrypt JSEncrypt.prototype.decrypt; JSEncrypt.prototype.decrypt function(message) { logKeyUsage(this.getPrivateKey()); return originalDecrypt.call(this, message); };这个案例让我深刻体会到安全是一个系统工程。前端加密若实施不当反而会制造虚假的安全感。在实际项目中必须建立从代码审计到运行时监控的完整防护体系才能真正保护数据安全。