加解密,编解码,js逆向
加密与编码流程详解
为什么要加密与编码?
在现代 Web 应用中,为了防止数据被非法获取或篡改,服务器与客户端之间的通信通常会使用加密和编码的方式进行保护。尤其是在电商、金融、社交等敏感场景下,这种做法尤为重要。
当你第一次访问某个网站时,服务器会下发一段 JavaScript 代码(通常是通过 <script src="xxx.js">
引入),这段 JS 中可能包含了加密算法逻辑以及密钥生成规则。浏览器执行这些脚本后,后续发送的请求参数(如搜索词、分页、用户行为)都会经过加密处理,并通过 Base64 等方式进行编码后再发送给服务器。
对爬虫的影响:
- 无法直接构造请求参数:如果你不知道加密方式和密钥,就无法构造出合法的请求体。
- 无法解析返回结果:即使你成功发出请求,服务器返回的数据也可能是加密过的,你需要有对应的解密逻辑才能提取有效信息。
- 需要逆向分析 JS 脚本:要破解加密逻辑,必须定位并提取 JS 中的加密函数、密钥生成方式,甚至模拟执行环境(如使用
Pyppeteer
或Selenium
)。
举例说明:
假设你想批量爬取某网站第1到第10页的内容,但每一页的请求参数都经过 AES 加密。你如果不掌握加密逻辑,只能手动点击页面按钮,复制加密后的参数,再逐个请求,效率极低且容易被封 IP。
流程概述
发送方(客户端/爬虫):
1. 加密
使用对称加密算法(如 AES、DES、3DES)对明文数据进行加密。以 AES 为例:
- 密钥(key):用于加密和解密,长度必须符合要求(如 AES-128 为 16 字节)
- 初始化向量(IV):用于 CBC 模式,增加加密数据的随机性
- 填充方式:如 PKCS7,确保数据长度是块大小的整数倍
2. 编码
加密后的数据通常是二进制格式,不适合直接传输。因此常使用 Base64 编码,将其转换为 ASCII 可打印字符。
- Base64 编码特点:
- 使用 64 个字符表示数据:A-Z、a-z、0-9、+、/
- 数据末尾可能会出现
=
进行补位
接收方(服务端/爬虫):
1. 解码
将接收到的 Base64 字符串还原成原始的二进制加密数据。
2. 解密
使用相同的密钥和 IV 对数据进行解密,恢复原始明文。
Python 示例代码
AES 加密 + Base64 编码
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import base64
data = "hello world".encode()
data_padded = pad(data, 16) # PKCS7 填充
key = b"1234567890abcdef"
iv = b"0987654321zxcvbn"
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_data = cipher.encrypt(data_padded)
base64_encrypted = base64.b64encode(encrypted_data).decode()
print("加密并Base64编码后的结果:", base64_encrypted)
Base64 解码 + AES 解密
from Crypto.Util.Padding import unpad
encrypted_bytes = base64.b64decode(base64_encrypted)
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_padded = cipher.decrypt(encrypted_bytes)
original_data = unpad(decrypted_padded, 16)
print("解密后的原始数据:", original_data.decode())
MD5 散列算法简介
MD5 是一种常用的散列算法,用于生成固定长度的摘要(128位)。虽然不适用于加密,但常用于验证文件完整性。
Python 示例
import hashlib
data = "hello world".encode()
md5_hash = hashlib.md5(data).hexdigest()
print("MD5摘要:", md5_hash)
RSA 非对称加密简介
RSA 是一种非对称加密算法,使用公钥加密、私钥解密。适用于安全传输密钥、数字签名等场景。
Python 示例
生成密钥对
from Crypto.PublicKey import RSA
key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()
with open("private.pem", "wb") as f:
f.write(private_key)
with open("public.pem", "wb") as f:
f.write(public_key)
加密
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
key = RSA.import_key(open("public.pem").read())
cipher_rsa = PKCS1_OAEP.new(key)
data = "hello world".encode()
encrypted_data = cipher_rsa.encrypt(data)
print("RSA加密结果:", encrypted_data)
解密
key = RSA.import_key(open("private.pem").read())
cipher_rsa = PKCS1_OAEP.new(key)
decrypted_data = cipher_rsa.decrypt(encrypted_data)
print("RSA解密结果:", decrypted_data.decode())
JS逆向分析技巧
为什么要js逆向
在第一次访问某网站时,网站服务器会发给你的浏览器一套js代码,这套代码中包含了加密的方式和密钥,你和服务器之间的通信要按照这套js代码,进行加密和编码,这样就可以防止爬虫进行批量爬取了,举例来说,我想批量爬取1到10页的内容,如果我不知道加密方式和密钥,我只能手动点击一到10页的按钮,一个个去拷贝加密后的请求参数,然后复制过来再去请求,同时,我在收到加密的回复后,我得不到有用的信息,所以我必须想个办法拿到那套Js代码中的加密和密钥部分,这样我才能使用脚本
当你在处理前端 JavaScript 代码时,如果遇到加密逻辑,可以通过以下步骤来逆向分析:
常用方法
- 全局搜索:查找可能用于加密的函数或变量名,如
decrypt
、encrypt
、key
、iv
等。 - 调试工具:利用浏览器开发者工具设置断点,逐步跟踪代码执行过程,观察参数传递情况。
- 确定关键信息:通过调试找到加密过程中使用的具体密钥(key)、初始向量(IV)和其他相关参数。
案例分析
-
案例一:AES 加密
- 在 JavaScript 中查找类似
CryptoJS.AES.encrypt
的调用。 - 设置断点并查看传入的密钥和 IV。
- 在 JavaScript 中查找类似
-
案例二:RSA 加密
- 查找使用
window.crypto.subtle.encrypt
或第三方库(如forge
)的加密逻辑。 - 调试获取公钥和加密数据。
- 查找使用
-
案例三:Base64 编码
- 查找
btoa()
或Buffer.from(...).toString('base64')
的使用。 - 观察输入和输出是否与预期一致。
- 查找
关键步骤
-
全局搜索关键词
- 搜索
encrypt
,decrypt
,key
,iv
,CryptoJS
,forge
,btoa
,atob
- 查找调用加密函数的位置,例如
CryptoJS.AES.encrypt(...)
或自定义函数
- 搜索
-
设置断点调试
- 在浏览器开发者工具中打开 Sources 面板
- 找到包含加密逻辑的 JS 文件
- 在加密函数前后设置断点,观察输入输出值
-
提取关键信息
- 密钥(key)、初始化向量(IV)可能硬编码,也可能动态生成
- 有时会使用时间戳、随机数等作为种子生成密钥
- 注意是否有混淆或压缩,必要时可使用在线反混淆工具
-
模拟执行环境
- 如果 JS 依赖浏览器上下文(如 window、document),可以使用
Pyppeteer
或Playwright
控制真实浏览器 - 如果只是纯函数,可以提取 JS 函数并在 Node.js 或 PyExecJS 中运行
- 如果 JS 依赖浏览器上下文(如 window、document),可以使用
总结笔记建议
编写加密相关的笔记时,建议记录以下内容:
项目 | 内容 |
---|---|
加密算法类型 | 如 AES、RSA、MD5 |
加密模式 | 如 CBC、ECB |
填充方式 | 如 PKCS7、ZeroPadding |
密钥来源 | 固定值?动态生成?来自 cookie? |
IV 来源 | 同上 |
编码方式 | Base64、Hex、URL Encode 等 |
JS 调试方法 | 断点位置、变量名、函数调用栈 |
逆向难点 | 是否有混淆、是否依赖浏览器环境 |