前端AES加密逆向实战:Python模拟三种加密模式与完整测试脚本

1. 加密分析基础与环境准备

在当今Web应用中,前端加密已成为保护数据传输安全的常见手段。作为安全测试工程师或爬虫开发者,掌握加密逆向技术至关重要。我们将从实战角度出发,深入探讨如何分析并模拟前端AES加密。

必备工具与库安装:

pip install pycryptodome requests execjs

Chrome开发者工具是分析前端加密的核心武器,以下是关键功能速查表:

功能面板 快捷键(Mac) 主要用途
Elements Cmd+Option+C 查看DOM结构及事件绑定
Console Cmd+Option+J 执行JS代码及查看输出
Sources Cmd+Option+I 调试JS代码及设置断点
Network - 监控网络请求及查看加密参数
XHR断点 右键添加断点 在特定API请求时暂停执行

提示:分析加密时,建议开启"Preserve log"选项防止刷新丢失关键日志

2. 三种AES模式原理与Python实现

2.1 ECB模式(电子密码本)

ECB是最简单的AES模式,将明文分成固定大小的块独立加密。其显著特点是相同明文块总是生成相同密文。

Python实现示例:

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

def aes_ecb_encrypt(plaintext, key):
    cipher = AES.new(key.encode(), AES.MODE_ECB)
    padded_data = pad(plaintext.encode(), AES.block_size)
    encrypted = cipher.encrypt(padded_data)
    return base64.b64encode(encrypted).decode()

def aes_ecb_decrypt(ciphertext, key):
    cipher = AES.new(key.encode(), AES.MODE_ECB)
    decrypted = cipher.decrypt(base64.b64decode(ciphertext))
    return unpad(decrypted, AES.block_size).decode()

典型应用场景:

  • 简单参数加密
  • 不需要高安全性的临时数据保护
  • 性能要求极高的环境

2.2 CBC模式(密码块链接)

CBC模式通过引入初始化向量(IV)和前一个块的密文来增加安全性,相同明文会因IV不同而产生不同密文。

Python实现要点:

def aes_cbc_encrypt(plaintext, key, iv):
    cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode())
    padded_data = pad(plaintext.encode(), AES.block_size)
    encrypted = cipher.encrypt(padded_data)
    return base64.b64encode(encrypted).decode()

def aes_cbc_decrypt(ciphertext, key, iv):
    cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode())
    decrypted = cipher.decrypt(base64.b64decode(ciphertext))
    return unpad(decrypted, AES.block_size).decode()

关键注意事项:

  1. IV应当随机生成且不重复使用
  2. 传输时需要将IV与密文一起传递
  3. 填充方式需与前端保持一致(通常为PKCS7)

2.3 CTR模式(计数器模式)

CTR将AES转换为流密码,通过计数器生成密钥流与明文异或。支持并行计算且不需要填充。

Python实现代码:

def aes_ctr_encrypt(plaintext, key, nonce):
    cipher = AES.new(key.encode(), AES.MODE_CTR, nonce=nonce.encode())
    return base64.b64encode(cipher.encrypt(plaintext.encode())).decode()

def aes_ctr_decrypt(ciphertext, key, nonce):
    cipher = AES.new(key.encode(), AES.MODE_CTR, nonce=nonce.encode())
    return cipher.decrypt(base64.b64decode(ciphertext)).decode()

性能对比表:

模式 安全性 并行性 填充需求 典型用途
ECB 支持 需要 简单加密、性能优先
CBC 不支持 需要 通用数据加密
CTR 支持 不需要 流数据加密

3. 实战:逆向分析与代码复现

3.1 加密逻辑定位技巧

通过Chrome开发者工具分析加密流程的标准操作:

  1. 事件监听定位

    • 右键点击提交按钮 → 检查
    • 查看Event Listeners面板中的click事件
  2. 调用栈追踪

    // 在Console中设置监控
    window.addEventListener('click', function(e) {
      console.trace();
    }, true);
    
  3. XHR断点设置

    • Sources → XHR Breakpoints → 添加接口URL关键词
  4. 全局搜索关键词

    • encrypt、decrypt、key、AES、CryptoJS等

3.2 混淆JS处理方案

当遇到复杂混淆代码时,execjs是直接调用原始JS函数的利器:

import execjs

def call_js_encrypt(js_path, func_name, plaintext):
    with open(js_path, 'r', encoding='utf-8') as f:
        js_code = f.read()
    
    ctx = execjs.compile(js_code)
    return ctx.call(func_name, plaintext)

# 示例:调用混淆后的加密函数
encrypted = call_js_encrypt('obfuscated.js', 'window._0xad3b', 'password123')

常见混淆特征处理技巧:

  • 动态函数名:通过调用栈分析实际执行函数
  • 字符串拆分:在Console中执行拼接后的字符串
  • 环境检测:补全缺失的浏览器API对象

4. 完整测试脚本开发

4.1 会话维持与请求头处理

模拟真实浏览器行为的完整请求示例:

import requests
from urllib.parse import urlparse

class EncryptedRequest:
    def __init__(self, base_url):
        self.session = requests.Session()
        self.base_url = base_url
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
            'Accept': 'application/json, text/javascript, */*',
            'X-Requested-With': 'XMLHttpRequest'
        }
    
    def get_csrf_token(self):
        response = self.session.get(
            f'{self.base_url}/login',
            headers=self.headers
        )
        return response.cookies.get('csrftoken') or \
               response.text.split('csrfToken":"')[1].split('"')[0]
    
    def encrypted_login(self, username, password):
        csrf_token = self.get_csrf_token()
        encrypted_pwd = aes_cbc_encrypt(password, '16bytekey12345678', 'randomiv12345678')
        
        payload = {
            'username': username,
            'password': encrypted_pwd,
            'csrf_token': csrf_token
        }
        
        response = self.session.post(
            f'{self.base_url}/api/login',
            json=payload,
            headers={**self.headers, 'Referer': self.base_url}
        )
        
        return response.json()

4.2 错误处理与重试机制

健壮的加密请求应包含以下保护措施:

from tenacity import retry, stop_after_attempt, wait_exponential

class SafeEncryptedRequest(EncryptedRequest):
    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=2, max=10)
    )
    def safe_request(self, method, endpoint, **kwargs):
        try:
            url = f'{self.base_url}{endpoint}'
            response = self.session.request(
                method, url, 
                headers=self.headers,
                timeout=10,
                **kwargs
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.SSLError:
            self.session.verify = False
            return self.safe_request(method, endpoint, **kwargs)
        except Exception as e:
            print(f'Request failed: {str(e)}')
            raise

5. 高级技巧与优化策略

5.1 动态密钥处理方案

应对密钥动态生成的三种策略:

  1. Hook密钥生成函数

    // 在Console中覆盖原始函数
    let originalKeyGen = window.generateKey;
    window.generateKey = function() {
        let key = originalKeyGen();
        console.log('Generated Key:', key);
        return key;
    }
    
  2. 内存断点监控

    • 在Memory面板对密钥变量设置访问断点
  3. 请求拦截代理

    from mitmproxy import http
    
    def response(flow: http.HTTPFlow):
        if 'key' in flow.response.text:
            print(f'Found key in {flow.request.url}')
    

5.2 性能优化方案

加密请求的性能对比数据(测试1000次):

方案 总耗时(ms) 内存占用(MB)
纯Python AES 420 15
execjs调用 2100 45
多线程(4线程) 180 22
异步IO 150 18

优化建议代码:

import asyncio
from aiohttp import ClientSession

async def async_encrypted_request(url, data):
    async with ClientSession() as session:
        encrypted = aes_ctr_encrypt(data, key, nonce)
        async with session.post(url, json={'data': encrypted}) as resp:
            return await resp.json()

# 批量处理示例
async def batch_requests(urls):
    tasks = []
    async with ClientSession() as session:
        for url in urls:
            task = async_encrypted_request(url, "test")
            tasks.append(task)
        return await asyncio.gather(*tasks)

在实际项目中,根据目标网站的防护强度不同,可能需要组合使用多种技术手段。保持代码的模块化设计非常重要,这样可以在发现新的防护机制时快速调整特定模块而不影响整体流程。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐