应对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语言基础,又需要对加密算法的深刻理解,更需要耐心细致的调试分析能力。
通过本文的深度剖析,我们不仅掌握了一套具体的技术方案,更重要的是建立了一种逆向思维的方**法论。在面对任何复杂的反爬虫机制时,我们都能够:
冷静分析:从网络请求入手,识别关键参数
精准定位:利用开发者工具,追踪调用链路
深度还原:分析加密逻辑,重现算法流程
工程实现:构建稳定可靠的爬虫系统

相关文章
|
1月前
|
Web App开发 数据采集 前端开发
集成Scrapy与异步库:Scrapy+Playwright自动化爬取动态内容
集成Scrapy与异步库:Scrapy+Playwright自动化爬取动态内容
|
7天前
|
数据采集 文字识别 JavaScript
基于文本检测的 Python 爬虫弹窗图片定位与拖动实现
基于文本检测的 Python 爬虫弹窗图片定位与拖动实现
|
18天前
|
人工智能 前端开发 IDE
仅凭几张图片,我们是如何让 AI 自动生成 70% 可用前端代码的?
本文系统总结了在仅有 UI 图片、无设计稿和交互说明的情况下,如何通过 AI 技术实现高质量前端代码自动生成。
仅凭几张图片,我们是如何让 AI 自动生成 70% 可用前端代码的?
|
17天前
|
数据采集 人工智能 供应链
2025年适合汽车行业与互联网企业的BI产品选型指南
2025年,数字化转型加速,BI工具成企业决策核心。本文对比瓴羊Quick BI、Power BI、Tableau、永洪科技、Domo五大主流产品,从能力、行业适配、案例等维度解析,重点推荐阿里云旗下瓴羊Quick BI,其在汽车与互联网行业表现突出,兼具AI分析、高性能计算与信创合规优势,助力企业实现数据价值最大化。
|
20天前
|
安全 Linux 网络安全
Linux下搭建L2TP/IPsec VPN服务(手把手教你配置安全远程连接)
本教程详细介绍如何在Ubuntu 22.04上搭建L2TP/IPsec VPN服务器,涵盖软件安装、IPsec与xl2tpd配置、用户添加、防火墙设置及连接测试,适合初学者快速掌握Linux下安全的VPN搭建方法。
|
21天前
|
人工智能 安全 网络安全
最具影响力+实力企业!瑞数信息荣获网络与数据安全“磐石之星”两项大奖
瑞数信息荣膺“磐石之星”双项大奖,获评上海最具影响力与本土最强网络数据安全企业。作为“磐石行动”核心力量,深度参与百余家单位攻防演练,保障千余个系统安全运行,拦截超600亿次攻击,以动态安全技术筑牢数字时代安全防线。
|
21天前
|
存储 监控 安全
RFID部队物资管理呈智能化趋势
RFID部队物资管理通过射频识别技术实现物资全流程智能化管控,涵盖入库、出库、盘点、追溯等环节,提升管理效率90%以上,降低人为误差与安全风险,保障战备物资精准、安全、高效调配。
|
13天前
|
前端开发 JavaScript Python
基于Splash的搜狗图片动态页面渲染爬取实战指南
基于Splash的搜狗图片动态页面渲染爬取实战指南
|
26天前
|
数据采集 存储 Java
异步与并发:利用Java多线程技术提升淘宝商品爬取效率
异步与并发:利用Java多线程技术提升淘宝商品爬取效率
|
27天前
|
数据采集 存储 调度
定时抓取与更新:用Python爬虫构建自己的新闻简报系统
定时抓取与更新:用Python爬虫构建自己的新闻简报系统

热门文章

最新文章