爬虫加密解密算法特征收集 - Python 实战
在爬虫开发过程中,我们经常会遇到各种加密机制的保护。目标网站为了防止数据被轻易抓取,通常会采用 JavaScript 加密、参数签名、数据加密等手段。本文将系统梳理爬虫中常见的加密解密算法特征,并提供 Python 破解实战方案。
一、常见加密类型识别
1.1 编码类加密
Base64 编码
特征识别:
- 字符串只包含
A-Z、a-z、0-9、+、/、= - 长度通常是 4 的倍数
- 结尾可能有 0-2 个
=填充符
常见形式:
javascript
// 标准 Base64
"SGVsbG8gV29ybGQ="
// URL 安全的 Base64(替换 +/ 为 -_)
"SGVsbG8gV29ybGQ"
// 去除填充符的 Base64
"SGVsbG8gV29ybGQ"Python 破解:
python
import base64
# 标准 Base64 解码
data = "SGVsbG8gV29ybGQ="
decoded = base64.b64decode(data).decode('utf-8')
print(decoded) # Hello World
# URL 安全 Base64
url_safe = "SGVsbG8gV29ybGQ"
decoded = base64.urlsafe_b64decode(url_safe + '=' * (4 - len(url_safe) % 4))URL 编码
特征识别:
%后跟两位十六进制字符- 空格编码为
%20或+ - 中文字符通常被编码
Python 破解:
python
from urllib.parse import unquote, quote
encoded = "%E4%BD%A0%E5%A5%BD" # 你好
decoded = unquote(encoded)
print(decoded) # 你好1.2 哈希类加密
MD5 加密
特征识别:
- 固定 32 位十六进制字符串
- 只包含
0-9和a-f - 相同输入始终产生相同输出
- 不可逆(但可通过彩虹表破解)
常见形式:
5d41402abc4b2a76b9719d911017c592 # "hello" 的 MD5Python 识别与破解:
python
import hashlib
import re
def is_md5(text):
"""判断是否为 MD5 格式"""
return bool(re.match(r'^[a-f0-9]{32}$', text))
# 生成 MD5 对比
text = "hello"
md5_hash = hashlib.md5(text.encode()).hexdigest()
print(f"{text} -> {md5_hash}")
# 简单彩虹表破解(仅适用于简单密码)
def crack_md5(target_hash, wordlist):
for word in wordlist:
if hashlib.md5(word.encode()).hexdigest() == target_hash:
return word
return NoneSHA 系列
特征识别:
| 算法 | 长度 | 特征 |
|---|---|---|
| SHA-1 | 40 位 | 十六进制,已不安全 |
| SHA-256 | 64 位 | 常用,安全性高 |
| SHA-512 | 128 位 | 更长,更安全 |
Python 处理:
python
import hashlib
# SHA-256
text = "hello"
sha256_hash = hashlib.sha256(text.encode()).hexdigest()
print(f"SHA-256: {sha256_hash}")
# SHA-1
sha1_hash = hashlib.sha1(text.encode()).hexdigest()
print(f"SHA-1: {sha1_hash}")1.3 对称加密
AES 加密
特征识别:
- 密文通常是 Base64 或十六进制格式
- 需要密钥(Key)和初始向量(IV)
- 密钥长度通常为 16/24/32 字节(AES-128/192/256)
- 常见模式:CBC、ECB、GCM
JavaScript 中的常见形式:
javascript
// CryptoJS AES
var encrypted = CryptoJS.AES.encrypt("message", "secret key").toString();
// 通常配合 Base64 输出
// U2FsdGVkX1+Z3zv7X8y+K7zF7X8y+K7zF7X8y+K7zF=Python 破解(需要找到密钥):
python
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
import base64
def decrypt_aes(ciphertext, key, iv=None, mode='CBC'):
"""
AES 解密
:param ciphertext: Base64 编码的密文
:param key: 密钥(16/24/32 字节)
:param iv: 初始向量(CBC 模式需要)
:param mode: 加密模式 CBC/ECB
"""
key = key.encode('utf-8')
if mode == 'CBC':
iv = iv.encode('utf-8') if iv else b'\x00' * 16
cipher = AES.new(key, AES.MODE_CBC, iv)
else: # ECB
cipher = AES.new(key, AES.MODE_ECB)
ct = base64.b64decode(ciphertext)
pt = unpad(cipher.decrypt(ct), AES.block_size)
return pt.decode('utf-8')
# 示例使用
key = "thisisakey123456" # 16 字节
encrypted = "U2FsdGVkX1+Z3zv7X8y+K7zF7X8y+K7zF7X8y+K7zF="
# decrypted = decrypt_aes(encrypted, key)如何从 JS 代码中找到 AES 密钥:
python
import re
def extract_aes_key(js_code):
"""从 JS 代码中提取可能的 AES 密钥"""
patterns = [
r'key["\']?\s*[:=]\s*["\']([^"\']+)["\']',
r'encrypt\(["\']([^"\']+)["\']',
r'password["\']?\s*[:=]\s*["\']([^"\']+)["\']',
]
keys = []
for pattern in patterns:
matches = re.findall(pattern, js_code)
keys.extend(matches)
return list(set(keys))DES/3DES 加密
特征识别:
- 密钥长度为 8 字节(DES)或 24 字节(3DES)
- 块大小为 8 字节
- 密文长度通常是 8 的倍数
Python 破解:
python
from Crypto.Cipher import DES
from Crypto.Util.Padding import unpad
import base64
def decrypt_des(ciphertext, key, iv=None):
"""DES 解密"""
key = key.encode('utf-8')[:8] # DES 密钥必须为 8 字节
iv = iv.encode('utf-8')[:8] if iv else b'\x00' * 8
cipher = DES.new(key, DES.MODE_CBC, iv)
ct = base64.b64decode(ciphertext)
pt = unpad(cipher.decrypt(ct), DES.block_size)
return pt.decode('utf-8')1.4 非对称加密
RSA 加密
特征识别:
- 公钥通常以
-----BEGIN PUBLIC KEY-----开头 - 私钥通常以
-----BEGIN PRIVATE KEY-----或-----BEGIN RSA PRIVATE KEY-----开头 - 密文长度固定,与密钥长度相关
- 加密数据通常经过 Base64 编码
常见场景:
- 登录密码加密
- 敏感数据传输
- 数字签名验证
Python 处理(需要私钥):
python
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
def decrypt_rsa(ciphertext, private_key_pem):
"""
RSA 解密
:param ciphertext: Base64 编码的密文
:param private_key_pem: PEM 格式的私钥
"""
private_key = RSA.import_key(private_key_pem)
cipher = PKCS1_v1_5.new(private_key)
ct = base64.b64decode(ciphertext)
pt = cipher.decrypt(ct, None)
return pt.decode('utf-8')
# 示例私钥格式
private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxgNSPM+TDyx...
-----END RSA PRIVATE KEY-----"""二、实战:逆向分析加密流程
2.1 分析步骤
抓包分析
bash# 使用 Chrome DevTools 或 Fiddler 抓包 # 关注 XHR/Fetch 请求 # 观察请求参数和响应数据定位加密代码
javascript// 在 Chrome DevTools 中搜索关键词 // encrypt, decrypt, sign, encode, decode // CryptoJS, aes, rsa, md5, sha断点调试
javascript// 在可疑代码处打断点 // 观察加密前后的数据变化 // 提取密钥和算法参数编写 Python 破解代码
2.2 实战案例:某网站登录加密
场景描述: 某网站登录接口,密码经过加密后传输。
分析过程:
javascript
// 在页面源码中发现
function encryptPassword(password) {
var key = CryptoJS.enc.Utf8.parse("1234567890123456");
var iv = CryptoJS.enc.Utf8.parse("abcdef1234567890");
var encrypted = CryptoJS.AES.encrypt(password, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}Python 破解:
python
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
def encrypt_password(password):
key = "1234567890123456".encode('utf-8')
iv = "abcdef1234567890".encode('utf-8')
cipher = AES.new(key, AES.MODE_CBC, iv)
pt = pad(password.encode('utf-8'), AES.block_size)
ct = cipher.encrypt(pt)
return base64.b64encode(ct).decode('utf-8')
# 使用
encrypted = encrypt_password("mypassword")
print(encrypted)2.3 实战案例:参数签名破解
场景描述: API 请求需要携带签名参数,防止重放攻击。
常见签名算法:
python
import hashlib
import hmac
import time
def generate_sign(params, secret_key):
"""
生成签名(常见算法)
:param params: 请求参数字典
:param secret_key: 密钥
"""
# 1. 按键排序
sorted_params = sorted(params.items())
# 2. 拼接字符串
param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
# 3. 添加密钥
sign_str = f"{param_str}&key={secret_key}"
# 4. MD5 或 HMAC
sign = hashlib.md5(sign_str.encode()).hexdigest().upper()
return sign
# HMAC 签名
def hmac_sign(message, secret_key):
return hmac.new(
secret_key.encode(),
message.encode(),
hashlib.sha256
).hexdigest()三、高级技巧
3.1 使用 PyExecJS 执行 JS 代码
python
import execjs
# 读取 JS 文件
with open('encrypt.js', 'r', encoding='utf-8') as f:
js_code = f.read()
# 编译执行
ctx = execjs.compile(js_code)
result = ctx.call('encryptPassword', 'mypassword')
print(result)3.2 使用 Playwright 直接调用页面 JS
python
from playwright.sync_api import sync_playwright
def get_encrypted_data(page, data):
"""在浏览器环境中执行加密函数"""
return page.evaluate(f"""
() => {{
return encryptData('{data}');
}}
""")
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://target-site.com")
encrypted = get_encrypted_data(page, "sensitive_data")
print(encrypted)
browser.close()3.3 WASM 加密处理
某些网站使用 WebAssembly 进行加密,处理更复杂:
python
# 1. 分析 WASM 导出函数
# 2. 使用 wasmtime 或 pywasm 调用
# 3. 或者通过 Playwright 在浏览器中调用四、反反爬虫策略
4.1 常见防护手段
| 防护手段 | 识别方法 | 应对策略 |
|---|---|---|
| 字体反爬 | @font-face 自定义字体 | 下载字体文件,建立映射表 |
| CSS 偏移 | 元素位置与实际显示不符 | 计算真实位置,还原数据 |
| Canvas 指纹 | 检测 Canvas 渲染特征 | 固定随机种子,或使用真实浏览器 |
| WebGL 指纹 | 检测显卡信息 | 使用常见硬件配置 |
| 行为检测 | 鼠标轨迹、点击模式 | 模拟人类行为,添加随机延迟 |
4.2 加密参数动态生成
python
import random
import time
def generate_device_id():
"""生成设备 ID"""
return ''.join(random.choices('abcdef0123456789', k=32))
def generate_timestamp():
"""生成时间戳"""
return str(int(time.time() * 1000))
def generate_random_str(length=16):
"""生成随机字符串"""
return ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=length))五、工具推荐
5.1 浏览器插件
- EditThisCookie: Cookie 管理
- JSON Viewer: JSON 格式化
- Wappalyzer: 技术栈识别
5.2 抓包工具
- Fiddler: Windows 平台
- Charles: 跨平台
- Proxyman: macOS
5.3 逆向工具
- Chrome DevTools: 必备
- PyCharm: Python 开发
- CyberChef: 在线编码转换
六、总结
| 加密类型 | 识别特征 | 破解难度 | 常用工具 |
|---|---|---|---|
| Base64 | 特定字符集,4的倍数 | ⭐ | base64 库 |
| MD5/SHA | 固定长度十六进制 | ⭐⭐ | hashlib |
| AES | 需密钥和 IV | ⭐⭐⭐ | pycryptodome |
| RSA | 公钥/私钥对 | ⭐⭐⭐⭐ | pycryptodome |
| 自定义 | 混淆代码 | ⭐⭐⭐⭐⭐ | JS 逆向 |
核心原则:
- 先识别加密类型,再选择破解方案
- 优先寻找密钥,而非硬破解
- 复杂加密考虑使用浏览器环境直接执行
- 遵守法律法规,仅用于合法爬虫开发
本文持续更新,欢迎交流讨论。

