Yakit实战:逆向前端加密与签名机制,构建自动化安全测试工具链

发布时间:2026/7/28 8:14:56
Yakit实战:逆向前端加密与签名机制,构建自动化安全测试工具链 1. 项目概述一次从“黑盒”到“白盒”的实战演练最近在安全测试圈子里Yakit 这款工具的热度持续走高它集成了 Burp Suite 的代理、扫描器、漏洞利用等多种功能于一体并且深度整合了 Yaklang 脚本语言让安全测试的自动化能力上了一个台阶。但工具再强大也得看谁来用、怎么用。很多朋友在实战中尤其是面对那些做了前端加密和签名校验的 Web 应用时依然会感到无从下手。你可能会遇到这种情况用 Burp Suite 或者 Yakit 抓到一个登录请求发现password字段是一长串看不懂的密文直接改包重放服务器直接返回“签名错误”或“请求非法”。这时候常规的 SQL 注入、XSS 测试手段好像都失效了测试陷入了僵局。这正是“前端加密与签名绕过”这个经典攻防场景的核心。它不再是简单的参数篡改而是上升到了对应用安全机制的理解和对抗层面。本次实战我们就以 Yakit 为核心构建一个完整的、从原理分析到自动化工具链搭建的实战流程。目标很明确当我们面对一个做了前端 RSA AES 加密并且携带了动态签名的请求时如何快速定位加密逻辑、逆向关键参数、并最终实现请求的自动化重放与漏洞探测。这不仅仅是学会用某个功能更是掌握一套应对复杂前端防护的通用方法论。整个流程会涉及静态代码分析、动态调试、Yak 脚本编写等多个环节适合有一定 Web 安全基础和工具使用经验的朋友。如果你刚入门跟着做一遍你会对 Web 前端与后端的交互、加密算法的应用、以及安全测试的深度有一个全新的认识。如果你已经是老手那么本次构建的自动化工具链或许能为你枯燥的重放测试节省大量时间。2. 核心原理拆解前端加密与签名的“盾”与“矛”在开始动手之前我们必须先理解对手。前端加密与签名本质上是开发者为 API 接口穿上的一层“盔甲”。这层盔甲通常由两部分构成内容加密和请求验签。2.1 内容加密保护传输中的敏感数据最常见的是RSA AES 混合加密模式。为什么是混合因为两种算法各有优劣。RSA非对称加密公钥加密私钥解密。优点是安全无需共享密钥缺点是速度慢不适合加密大量数据。AES对称加密加密和解密使用同一个密钥。优点是速度快适合加密数据体缺点是密钥分发存在风险。因此一个高效的混合模式通常是前端生成一个随机的AES 密钥通常叫sessionKey或encryptKey。前端使用这个 AES 密钥对实际的请求数据如{“username”: “admin”, “password”: “123456”}进行加密得到密文 A。前端使用从服务器获取的RSA 公钥对刚刚生成的 AES 密钥进行加密得到密文 B。前端将密文 A加密的数据和密文 B加密的 AES 密钥一起发送给服务器。服务器用自己的 RSA 私钥解密密文 B得到 AES 密钥再用这个 AES 密钥解密密文 A得到原始数据。这样既利用了 AES 的高效来加密大量数据又利用了 RSA 的安全性来传输关键的 AES 密钥。在 Burp 或 Yakit 中你看到的可能就是两个长长的、看似随机的 Base64 字符串。2.2 请求签名防止请求被篡改或重放加密保证了数据内容不被窥探但无法防止攻击者截获整个请求包密文并直接重放Replay Attack。签名就是为了解决这个问题。 签名通常是一个基于特定算法生成的字符串比如HMAC-SHA256。它的生成过程一般如下将请求的部分或全部元素按一定规则拼接成一个字符串。这些元素可能包括请求路径 (/api/login)排序后的请求参数 (a1b2)请求体可能是加密后的密文一个时间戳 (timestamp)一个随机数 (nonce)使用一个密钥这个密钥可能固定也可能由前端动态生成或从服务器获取对这个拼接字符串进行 HMAC 计算。将计算结果通常转为十六进制或 Base64放在请求头中比如X-Signature: xxxxxx。服务器收到请求后会以同样的规则和密钥服务器端存有或能计算得出重新生成签名并与请求中的签名比对。如果一致说明请求在传输过程中未被篡改且时间戳、随机数等可以有效抵御重放攻击。那么我们的“矛”指向哪里作为测试者我们的目标不是破解 RSA 或 AES 算法这在计算上不可行而是定位前端加密/签名代码找到生成密文和签名的 JavaScript 代码。提取关键参数拿到 RSA 公钥、AES 密钥生成逻辑、签名密钥和拼接规则。模拟或劫持流程让我们的测试工具Yakit能够按照应用的规则对我们想要测试的 Payload 进行“合法”的加密和签名从而绕过校验。3. 环境准备与目标分析工欲善其事必先利其器。我们首先需要搭建一个包含前端加密签名的靶场环境并准备好分析工具。3.1 靶场环境搭建为了高度模拟真实场景我们不会使用现成的、加密逻辑过于简单的靶场。推荐以下两种方式自建简易靶场使用 Flask 或 Node.js 快速写一个 demo。后端提供/getPublicKey接口返回 RSA 公钥/login接口实现前述的 RSAAES 解密和签名验证。前端用 JavaScript 实现对应的加密和签名。这种方式最灵活你能完全控制逻辑。使用修改版开源靶场找一个如Pikachu这样的漏洞练习平台在其登录模块上手动添加前端加密和签名逻辑。这比完全自建稍省事且具备其他漏洞测试功能。这里以自建 Flask 靶场为例给出核心思路后端生成 RSA 密钥对将公钥通过接口暴露。定义签名验证中间件检查每个 POST 请求的X-Signature头。/login接口先验证签名再解密数据最后进行简单的用户名密码判断。3.2 分析工具链配置我们的核心工具是Yakit但需要其他工具辅助分析。Yakit确保安装最新版并开启 MITM 代理功能。这是我们的操作中枢。浏览器开发者工具Chrome 或 Edge 的 DevTools。关键面板Network (网络)记录所有请求查看原始请求/响应头、预览响应体。Sources (源代码)查看、搜索和调试前端 JavaScript 文件。Console (控制台)执行 JavaScript 代码测试函数。调试代理将浏览器代理设置为 Yakit 的监听地址如127.0.0.1:8083并安装 Yakit 的 CA 证书到浏览器受信任的根证书颁发机构以确保能解密 HTTPS 流量。注意在实战中目标网站可能使用了反调试技术如无限 debugger、代码混淆。这时需要用到“停用断点”、“条件断点”或“Overrides”功能来绕过。对于高度混淆的代码可以尝试使用AST抽象语法树反混淆工具进行初步还原但这属于进阶内容。4. 动态抓包与加密逻辑定位一切就绪我们开始正面接触目标。4.1 初始请求观察用配置好代理的浏览器访问靶场登录页打开开发者工具的 Network 面板勾选Preserve log。在输入框输入账号密码如 admin/123456点击登录。 在 Yakit 的 “MITM 交互式劫持” 模块或 “HTTP History” 中你应该能看到至少两个关键请求GET /api/getPublicKey响应体里可能是一个 JSON包含publicKey字段值是一串 PEM 格式的 RSA 公钥。立即保存这个公钥。POST /api/login这就是我们的主目标。查看请求体很可能不再是明文的username和password而是变成了类似这样的结构{ encryptedData: U2FsdGVkX1/...很长一串Base64, encryptedKey: OaRr7BZ0P...另一串Base64 }同时请求头里可能会有一个X-Signature: sha256xxxxxx或X-Sign: xxxxx。4.2 关键 JavaScript 文件定位现在需要找到生成这些参数的 JavaScript 代码。在 Network 面板中对/api/login这个请求右键选择Initiator或Request call stack这可以帮你追踪是哪个 JS 文件发起了这个请求。通常这会指向一个包含login、submit、encrypt等字眼的 JS 文件。点击这个 JS 文件在 Sources 面板中打开它。如果代码未被压缩你可以直接搜索关键词如encrypt、RSA、AES、CryptoJS、sign、hmac。如果代码被压缩单行变量名短可以使用 Chrome 的{}Pretty-print按钮进行格式化然后再搜索。常见的库和函数CryptoJS.AES.encrypt/CryptoJS.AES.decryptJSEncrypt一个纯 JavaScript 的 RSA 库window.crypto.subtle.encryptWeb Crypto API自定义的generateSignature或sign函数。4.3 调试与关键参数提取找到疑似加密的函数后在其所在行号上点击设置断点。回到登录页面再次点击登录代码执行会在断点处暂停。调试关键步骤Step Into (F11)进入函数内部。观察变量在 Scope 或 Watch 面板中查看此时各个变量的值。你可能会看到明文密码password。生成的随机 AES 密钥key或sessionKey。调用JSEncrypt.setPublicKey(publicKey)设置的公钥。用于生成签名的密钥secret和拼接的原始字符串rawString。提取关键逻辑不要只记值要记下算法和流程。比如AES 密钥是 16 位还是 32 位模式是 CBC 还是 ECBIV 向量是什么RSA 加密时对 AES 密钥做了哪些处理比如 Padding 模式签名时拼接了哪些字段顺序如何使用的哈希算法是 SHA256 还是 MD5实操心得很多时候加密函数会被封装在一个通用的request函数里所有 API 请求都会经过它。找到这个入口函数并分析其参数结构比追踪单个登录按钮的点击事件更高效。另外可以尝试在 Console 中直接调用你找到的加密函数传入测试数据看输出是否与抓包一致这是验证你分析正确性的好方法。5. 构建 Yakit 自动化工具链分析清楚逻辑后我们就可以在 Yakit 中构建自动化测试能力了。Yakit 的强大之处在于其内置的 Yaklang 引擎让我们可以编写脚本来实现复杂逻辑。5.1 编写 Yak 脚本实现加密/签名我们假设分析出的逻辑如下前端生成 16 字节随机数作为 AES-128-CBC 的密钥aesKey和 IV。用aesKey加密 JSON 格式的请求数据。用固定的 RSA 公钥加密aesKey。签名sign HMAC-SHA256(“/api/login” timestamp encryptedData, “fixedSecret”)。对应的 Yak 脚本核心函数如下// 导入必要的库 import ( crypto/aes crypto/cipher crypto/hmac crypto/rand crypto/rsa crypto/sha256 encoding/base64 encoding/hex encoding/json time crypto/x509 encoding/pem ) // 1. AES-CBC 加密 func aesEncrypt(data, key, iv []byte) (string, error) { block, err : aes.NewCipher(key) if err ! nil { return , err } padLen : aes.BlockSize - len(data)%aes.BlockSize padding : make([]byte, padLen) for i : range padding { padding[i] byte(padLen) } data append(data, padding...) ciphertext : make([]byte, len(data)) mode : cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(ciphertext, data) return base64.StdEncoding.EncodeToString(ciphertext), nil } // 2. RSA 加密 (使用 PEM 格式公钥) func rsaEncrypt(data []byte, pubKeyPEM string) (string, error) { block, _ : pem.Decode([]byte(pubKeyPEM)) if block nil { return , error(failed to parse PEM block) } pubInterface, err : x509.ParsePKIXPublicKey(block.Bytes) if err ! nil { return , err } pubKey : pubInterface.(*rsa.PublicKey) encrypted, err : rsa.EncryptPKCS1v15(rand.Reader, pubKey, data) if err ! nil { return , err } return base64.StdEncoding.EncodeToString(encrypted), nil } // 3. 生成 HMAC-SHA256 签名 func generateSignature(path, timestamp, encryptedData, secret string) string { rawString : path timestamp encryptedData h : hmac.New(sha256.New, []byte(secret)) h.Write([]byte(rawString)) return hex.EncodeToString(h.Sum(nil)) } // 主函数生成一个完整的加密请求体 func generateEncryptedRequest(username, password, rsaPubKey string) (map[string]string, error) { // 生成随机 AES Key 和 IV (各16字节) aesKey : make([]byte, 16) rand.Read(aesKey) iv : make([]byte, 16) rand.Read(iv) // 准备明文数据 plainData : map[string]string{ username: username, password: password, } jsonData, _ : json.Marshal(plainData) // AES 加密数据 encryptedData, err : aesEncrypt(jsonData, aesKey, iv) if err ! nil { return nil, err } // RSA 加密 AES Key encryptedKey, err : rsaEncrypt(aesKey, rsaPubKey) if err ! nil { return nil, err } // 生成签名 timestamp : string(time.Now().Unix()) secret : fixedSecretKey123456 // 从JS分析中获取 signature : generateSignature(/api/login, timestamp, encryptedData, secret) // 组装最终请求 requestBody : make(map[string]string) requestBody[encryptedData] encryptedData requestBody[encryptedKey] encryptedKey // 通常 timestamp 和 signature 放在 Header 里 // 这里为了演示也放在 body requestBody[timestamp] timestamp requestBody[signature] signature return requestBody, nil }5.2 集成到 Yakit 的 Web FuzzerYakit 的 “Web Fuzzer” 模块是我们的主战场。我们不能直接修改密文但可以在请求发出前用脚本实时生成合法的加密请求。创建新的 Fuzzer 标签页在 Yakit 中打开 Web Fuzzer。配置原始请求从 HTTP History 中将/api/login的请求包右键发送到 Fuzzer。这会得到一个基础模板。使用 Yak 脚本作为 Payload 处理器在 Fuzzer 的Payloads标签页添加你的测试字典比如 SQL 注入 Payload 列表。关键步骤切换到Payload Processing或Codec部分。Yakit 允许你为 Payload 编写编码/解码脚本。这里我们需要的是一个“生成器”。我们可以编写一个 Yak 脚本它接收一个原始 Payload例如admin OR 11然后调用上面写的generateEncryptedRequest函数用这个 Payload 作为password字段的值生成全新的encryptedData,encryptedKey,signature等。这个脚本的输出不是简单的字符串替换而是一个完整的、加密后的 JSON 请求体。更新请求模板在 Fuzzer 的请求模板中将{{payload}}或原始的加密参数位置替换为脚本处理器的输出占位符具体语法参考 Yakit 文档可能是{{yak(...)}}或{{base64(...)}}结合脚本。发送测试运行 FuzzerYakit 会为每一个 Payload 动态生成一个合法的、带有正确签名的加密请求然后发送给服务器。这样SQL 注入的测试载荷就被成功地“包装”进了应用认可的安全信封里。5.3 构建自动化扫描插件对于更复杂的测试场景如遍历目录、检测多种漏洞我们可以将整个逻辑封装成一个 Yakit 插件。创建新插件在 Yakit 的 “插件仓库” 中选择 “创建本地插件”。定义插件信息给插件起名如Bypass-Frontend-Encryption。编写插件主逻辑插件可以接收一个目标 URL 和 RSA 公钥作为输入参数。插件内部集成上述的加密、签名函数。利用 Yakit 的扫描引擎如http请求库、crawler爬虫自动爬取或接收用户指定的 API 接口。对于每个需要测试的 POST 接口自动按照分析出的规则对测试 Payload 进行加密和签名然后发送请求并根据响应判断是否存在漏洞如 SQL 注入的延时、报错、布尔盲注特征。发布与使用保存插件后它就会出现在你的插件列表中。以后遇到类似的前端加密场景你只需要配置好目标 URL 和公钥运行这个插件它就能自动进行漏洞扫描极大提升效率。6. 实战案例绕过加密实现 SQL 注入测试让我们用一个简化的流程串联起所有步骤。场景靶场登录接口/api/login使用了上述 RSAAESHMAC 的防护。目标测试password字段是否存在 SQL 注入。步骤分析通过浏览器调试确认了加密逻辑和签名算法。发现签名密钥secret硬编码在 JS 中为“this_is_a_secret_key”。编写 Yak 函数将第 5.1 节的代码保存为一个 Yak 脚本文件encrypt_tools.yak。准备 Payload准备一个经典的 SQL 注入测试字典如payloads.txt内容包含 OR 11admin--sleep(5)#等。配置 Web Fuzzer原始请求POST /api/login HTTP/1.1 ... \n\n {encryptedData:old,encryptedKey:old}Payloads加载payloads.txt。Payload Processor编写一个简短的 Yak 脚本调用encrypt_tools.yak中的generateEncryptedRequest函数。假设函数返回一个 JSON 字符串{encryptedData:..., encryptedKey:..., timestamp:..., signature:...}。请求模板修改将整个请求体替换为{{yak(processor_script)}}其中processor_script的输出就是上面那个 JSON 字符串。同时记得在请求头中更新Content-LengthYakit 通常会自动处理并添加X-Signature: {{signature_from_script}}。发送与判断启动 Fuzzer观察每个请求的响应状态码、长度、时间和内容。如果某个 Payload如admin--的响应与其他明显不同例如返回了登录成功的消息而其他返回密码错误则强烈怀疑存在 SQL 注入。进一步可以测试时间盲注 Payloadadmin AND sleep(5)--观察响应时间是否显著延迟。通过这个流程我们成功地将明文的 SQL 注入 Payload无缝地“注入”到了前端加密的流程中实现了真正的漏洞测试。7. 进阶技巧与疑难问题排查在实际操作中你肯定会遇到比示例更复杂的情况。这里分享一些进阶技巧和常见问题的排查思路。7.1 应对动态密钥与签名每次请求的 AES 密钥都不同这是正常且更安全的设计。我们的脚本需要在每次请求时都重新生成随机 AES 密钥。这并不影响自动化因为生成随机数是瞬间完成的。签名密钥动态获取有些应用会在登录前通过一个/api/getToken之类的接口返回一个临时的secret用于本次会话的签名。解决方案是在 Yak 脚本中先发起一个 GET 请求获取这个 token/secret然后再用这个 secret 去生成签名。Yaklang 的http库可以轻松实现这个链式请求。签名包含其他动态参数除了timestamp可能还有nonce随机数。我们的脚本也需要动态生成这些值并确保它们被正确地拼接到签名字符串中。7.2 处理代码混淆与反调试格式化与搜索优先使用 Chrome 的代码美化功能。Hook 关键函数如果代码混淆严重直接搜索关键词无效。可以尝试在 Console 中 Hook 关键函数。例如在发送请求前执行XMLHttpRequest.prototype.open (function(open) { return function(method, url) { console.trace(Requesting:, method, url); return open.apply(this, arguments); }; })(XMLHttpRequest.prototype.open);这会在任何 XHR 请求发起时打印堆栈帮你快速定位发起请求的代码区域。使用 Overrides在 Sources 面板的 Overrides 选项卡你可以用本地修改过的、已解混淆的 JS 文件替换线上文件便于调试。7.3 Yakit 脚本调试技巧die与log.info在 Yak 脚本中大量使用log.info(“变量值: %v”, variable)来输出中间结果这是调试的基础。分段测试不要试图一次性写完整个复杂的加密函数。先单独测试 AES 加密确保输出的密文能和前端加密结果匹配再测试 RSA 加密最后测试签名。每一步都验证通过后再组合。利用 Codec 功能Yakit 的 Codec 功能不仅可以用于 Fuzzer也可以单独作为加密解密工具使用方便你快速验证算法。7.4 常见问题排查清单问题现象可能原因排查思路服务器返回“解密失败”AES 加密参数错误Key, IV, Mode, Padding1. 确认 Key 和 IV 的字节长度和值是否正确。2. 确认加密模式CBC/ECB和填充模式PKCS7/ZeroPadding。3. 对比前端加密相同明文输出的密文是否与你脚本输出一致。服务器返回“签名错误”签名算法或拼接规则错误1. 确认 HMAC 的哈希算法SHA256? MD5?。2.逐字符核对签名原始字符串的拼接顺序、字段分隔符是否有或\n。3. 确认签名密钥是否正确。服务器返回“请求过期”时间戳格式或校验问题1. 确认时间戳是秒还是毫秒。2. 确认服务器是否有时间漂移容忍窗口如±5分钟。3. 检查签名是否包含了时间戳。Yakit Fuzzer 发送失败Payload 处理器脚本输出格式错误1. 检查脚本输出是否是有效的字符串或 JSON。2. 在 Fuzzer 中先使用“单次测试”功能查看脚本处理后的实际请求包。3. 检查请求头Content-Type是否与 body 格式匹配如application/json。构建这样一套自动化工具链的初期投入确实需要一些时间但一旦完成它将成为你面对同类目标时的“破甲利器”。更重要的是这个过程极大地锻炼了你逆向分析前端逻辑、理解加密算法应用和编写自动化测试脚本的能力这些才是安全测试工程师的核心价值所在。工具链的意义不在于替代思考而在于将重复、繁琐的劳动自动化让你能更专注于漏洞逻辑本身的分析与挖掘。