应对12306反爬虫机制:JS加密参数逆向分析

简介: 应对12306反爬虫机制:JS加密参数逆向分析

一、 理论基石:理解12306的加密防御体系
1.1 为什么参数加密如此有效?
参数加密机制的核心在于:服务器通过验证请求参数的完整性和时效性来区分人类用户与机器程序。当您在网页上点击"查询"时,浏览器会执行复杂的JS代码,生成一个或多个经过加密的签名参数。这些参数往往具有:
● 时效性:与时间戳绑定,短时间内失效
● 唯一性:每次请求都会变化,防止重放攻击
● 关联性:与用户会话、查询条件等上下文关联
1.2 12306加密的典型特征
通过对12306网络请求的观察,我们可以发现以下典型特征:
● 动态令牌:如_json_att等字段,每次会话都会变化
● 签名参数:如查询接口中的leftTicketSecret等哈希值
● 加密密钥:某些接口参数使用非对称加密保护
二、 实战准备:搭建逆向分析环境
2.1 必备工具栈
● Chrome DevTools:核心分析工具(F12)
● Pretty-print:格式化压缩的JS代码({}按钮)
● Overrides:本地JS文件替换与调试
● Postman/Charles:API调试与抓包
● Python环境:执行JS代码(PyExecJS、Node.js)
2.2 关键分析步骤
逆向分析遵循"由外到内"的原则:

  1. 网络监控:识别关键API调用及加密参数
  2. 调用栈追踪:找到生成这些参数的JS函数
  3. 代码分析:理解加密逻辑与算法
  4. 代码移植:将JS逻辑转化为Python可执行代码
    三、 深度实战:逆向12306查询接口加密参数
    让我们以车票查询接口为例,进行完整的逆向分析。
    3.1 识别加密参数
    首先在浏览器中打开12306车票查询页面,开启Network监控,执行一次查询。观察请求的Query String Parameters,会发现类似以下的参数结构:
    javascript
    // 原始请求参数 leftTicketDTO: { train_date: "2024-11-24", from_station: "BJP", to_station: "SHH", purpose_codes: "ADULT" } // 实际请求URL(示例) https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2024-11-24&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT&_json_att=...
    注意_json_att这个参数,它就是我们需要破解的加密参数之一。
    3.2 定位加密函数
    在Chrome DevTools中:
  5. 搜索_json_att在所有JS文件中的出现位置
  6. 在初始化代码处设置断点
  7. 使用Call Stack追踪调用链
    我们会发现关键代码通常隐藏在压缩的JS文件中。使用Pretty-print格式化后,可以找到类似这样的代码段:
    javascript
    ```// 格式化后的关键JS代码片段
    function generateJsonAtt() {
    var e = Math.random().toString(36).substr(2);
    var t = new Date().getTime();
    var n = encryptMethod(e + "_" + t);
    return window.btoa(n);
    }

function encryptMethod(str) {
// 复杂的加密逻辑,可能涉及多个步骤
var key = CryptoJS.enc.Utf8.parse('1234567812345678');
var iv = CryptoJS.enc.Utf8.parse('1234567812345678');
var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(str), key, {
keySize: 128 / 8,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}

3.3 算法分析与代码移植
分析上述代码,我们发现加密流程:
1. 生成随机字符串 + 时间戳
2. 使用AES-CBC模式加密
3. 进行Base64编码
现在我们需要在Python中重现这个逻辑:
python
```import execjs
import time
import random
import string

class Zhang12306JSEncrypt:
    def __init__(self):
        # 编译JS加密代码
        with open('12306_encrypt.js', 'r', encoding='utf-8') as f:
            js_code = f.read()
        self.ctx = execjs.compile(js_code)

    def generate_json_att(self):
        """生成_json_att参数"""
        return self.ctx.call('generateJsonAtt')

# 对应的JS文件 (12306_encrypt.js)
"""
const CryptoJS = require('crypto-js');

function generateRandomString(length) {
    return Math.random().toString(36).substr(2, length);
}

function generateJsonAtt() {
    var random_str = generateRandomString(16);
    var timestamp = new Date().getTime();
    var raw_str = random_str + "_" + timestamp;
    var encrypted = encryptMethod(raw_str);
    return Buffer.from(encrypted.toString(), 'binary').toString('base64');
}

function encryptMethod(str) {
    var key = CryptoJS.enc.Utf8.parse('1234567812345678');
    var iv = CryptoJS.enc.Utf8.parse('1234567812345678');
    var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(str), key, {
        keySize: 128 / 8,
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}

module.exports = {
    generateJsonAtt: generateJsonAtt
};
"""

3.4 完整请求示例
python
```import requests
from Zhang12306JSEncrypt import Zhang12306JSEncrypt

class Advanced12306Crawler:
def init(self):
self.session = requests.Session()
self.encryptor = Zhang12306JSEncrypt()

    # 代理配置
    self.proxyHost = "www.16yun.cn"
    self.proxyPort = "5445"
    self.proxyUser = "16QMSOML"
    self.proxyPass = "280651"

    # 设置请求头
    self.session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Referer': 'https://kyfw.12306.cn/otn/leftTicket/init',
    })

def get_proxies(self):
    """构造代理配置字典"""
    proxy_url = f"http://{self.proxyUser}:{self.proxyPass}@{self.proxyHost}:{self.proxyPort}"
    return {
        'http': proxy_url,
        'https': proxy_url
    }

def query_tickets(self, train_date, from_station, to_station):
    """查询车票信息"""
    # 生成加密参数
    json_att = self.encryptor.generate_json_att()

    # 构造请求参数
    params = {
        'leftTicketDTO.train_date': train_date,
        'leftTicketDTO.from_station': from_station,
        'leftTicketDTO.to_station': to_station,
        'purpose_codes': 'ADULT',
        '_json_att': json_att
    }

    url = 'https://kyfw.12306.cn/otn/leftTicket/query'

    try:
        # 使用代理发起请求
        response = self.session.get(
            url, 
            params=params, 
            proxies=self.get_proxies(),
            timeout=10  # 添加超时设置
        )

        if response.status_code == 200:
            data = response.json()
            if data.get('status'):
                return self.parse_ticket_data(data['data'])
            else:
                print("API返回状态错误:", data.get('messages', ['未知错误']))
        else:
            print(f"HTTP错误: {response.status_code}")

    except requests.exceptions.ProxyError as e:
        print(f"代理连接错误: {e}")
    except requests.exceptions.ConnectTimeout as e:
        print(f"连接超时: {e}")
    except requests.exceptions.ReadTimeout as e:
        print(f"读取超时: {e}")
    except Exception as e:
        print(f"请求异常: {e}")

    return None

def parse_ticket_data(self, data):
    """解析车票数据"""
    # 简化的解析逻辑
    result = []
    for item in data.get('result', []):
        info = item.split('|')
        if len(info) > 3:
            result.append({
                '车次': info[3],
                '出发站': info[6],
                '到达站': info[7],
                '出发时间': info[8],
                '到达时间': info[9],
                '历时': info[10]
            })
    return result

使用示例

if name == "main":
crawler = Advanced12306Crawler()
tickets = crawler.query_tickets('2024-11-24', 'BJP', 'SHH')
if tickets:
for ticket in tickets[:3]:
print(ticket)

四、 高级技巧:应对动态变化的加密逻辑
4.1 处理代码混淆与压缩
12306的JS代码经常更新且高度混淆。应对策略:
● 使用AST(抽象语法树)工具进行反混淆
● 建立代码特征库,快速定位关键函数
● 监控JS文件变化,自动触发重新分析
4.2 自动化更新机制
python
```import hashlib
import os

class AutoUpdateEncryptor:
    def __init__(self, js_url):
        self.js_url = js_url
        self.local_file = '12306_encrypt.js'
        self.ctx = None
        self.load_js()

    def get_js_md5(self):
        """计算JS文件MD5以检测变化"""
        with open(self.local_file, 'rb') as f:
            return hashlib.md5(f.read()).hexdigest()

    def download_js(self):
        """下载最新JS文件"""
        # 实现下载逻辑
        pass

    def load_js(self):
        """加载JS执行环境"""
        if not os.path.exists(self.local_file):
            self.download_js()

        with open(self.local_file, 'r', encoding='utf-8') as f:
            js_code = f.read()

        self.ctx = execjs.compile(js_code)

    def check_and_update(self):
        """检查并更新JS文件"""
        # 定期检查JS文件是否更新
        pass

五、 伦理边界与最佳实践
5.1 合法合规使用
严格遵守robots.txt协议
控制请求频率,避免对服务器造成压力
仅用于技术学习与研究目的
5.2 技术防护措施
实现请求失败的重试机制
使用IP代理池分散请求
建立完整的日志监控系统
结论:逆向工程的艺术与科学
破解12306的JS加密参数,是一场在技术边界上的精确舞蹈。它既需要扎实的JavaScript语言基础,又需要对加密算法的深刻理解,更需要耐心细致的调试分析能力。
通过本文的深度剖析,我们不仅掌握了一套具体的技术方案,更重要的是建立了一种逆向思维的方**法论。在面对任何复杂的反爬虫机制时,我们都能够:
冷静分析:从网络请求入手,识别关键参数
精准定位:利用开发者工具,追踪调用链路
深度还原:分析加密逻辑,重现算法流程
工程实现:构建稳定可靠的爬虫系统

相关文章
|
3天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
12天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
7天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
497 203
|
5天前
|
人工智能 移动开发 自然语言处理
2025最新HTML静态网页制作工具推荐:10款免费在线生成器小白也能5分钟上手
晓猛团队精选2025年10款真正免费、无需编程的在线HTML建站工具,涵盖AI生成、拖拽编辑、设计稿转代码等多种类型,均支持浏览器直接使用、快速出图与文件导出,特别适合零基础用户快速搭建个人网站、落地页或企业官网。
657 157
|
11天前
|
人工智能 自然语言处理 安全
国内主流Agent工具功能全维度对比:从技术内核到场景落地,一篇读懂所有选择
2024年全球AI Agent市场规模达52.9亿美元,预计2030年将增长至471亿美元,亚太地区增速领先。国内Agent工具呈现“百花齐放”格局,涵盖政务、金融、电商等多场景。本文深入解析实在智能实在Agent等主流产品,在技术架构、任务规划、多模态交互、工具集成等方面进行全维度对比,结合市场反馈与行业趋势,为企业及个人用户提供科学选型指南,助力高效落地AI智能体应用。
|
5天前
|
数据采集 消息中间件 人工智能
跨系统数据搬运的全方位解析,包括定义、痛点、技术、方法及智能体解决方案
跨系统数据搬运打通企业数据孤岛,实现CRM、ERP等系统高效互通。伴随数字化转型,全球市场规模超150亿美元,中国年增速达30%。本文详解其定义、痛点、技术原理、主流方法及智能体新范式,结合实在Agent等案例,揭示从数据割裂到智能流通的实践路径,助力企业降本增效,释放数据价值。
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
663 46