某公开数据简单逆向

政府网站 API 逆向分析实战:噪声数据采集

一句话总结

从目标网站前端 JS 中提取硬编码的第三方 API 地址和认证凭据,直接调用外部数据平台接口获取结构化数据。


逆向流程

步骤 1:访问目标网站

页面正常返回 200,HTML 中包含多个 <script> 标签。

步骤 2:定位核心 JS 文件

在 HTML 中找到关键脚本引用:

html 复制代码
<script src="../../images/zsjs*_sthjt_jkjs.js"></script>

步骤 3:下载并分析 JS

从目标网站下载对应的 JS 文件,发现是未混淆的明文代码

关键发现 1:API 配置(硬编码)

javascript 复制代码
var API_CONFIG = {
  baseUrl: "https://*.jlzslw.com:50003",   // 第三方数据平台
  username: "InterfaceCall",
  secretKey: "******",                      // 硬编码密钥
  sysCode: "NOISE"
};

关键发现 2:3 个 API 接口

搜索 JS 中的 ajax / Get 关键词,找到 3 个接口路径:

接口路径 功能
/GetExternalApiToken 获取 Token
/GetBSDStationListAsync 获取站点列表
/GetDATStationHourDisplayListAsync 获取噪声小时数据

步骤 4:分析 getToken 函数

javascript 复制代码
function getToken() {
  $.ajax({
    type: "GET",
    url: API_CONFIG.baseUrl + "/api/noiseproduct/AirCityBaseCommon/GetExternalApiToken",
    data: { UserName: API_CONFIG.username, SecretKey: API_CONFIG.secretKey },
    dataType: "json",
    success: function (response) {
      if (response && response.result) {
        RESULT_DATA.token = response.result;

关键点

  • 请求方式:GET
  • 参数名大小写敏感:UserNameSecretKey(首字母大写)
  • Token 位置:response.result

步骤 5:分析站点列表接口

javascript 复制代码
$.ajax({
  type: "GET",
  url: API_CONFIG.baseUrl + "/api/noiseproduct/AirCityProductBase/GetBSDStationListAsync",
  headers: { SysCode: API_CONFIG.sysCode, Authorization: "Bearer " + RESULT_DATA.token },

关键点

  • 请求方式:GET
  • 必须携带 Headers:SysCodeAuthorization: Bearer {token}

步骤 6:分析噪声数据接口

javascript 复制代码
$.ajax({
  type: "GET",
  url: API_CONFIG.baseUrl + "/api/noiseproduct/airdata/DATStationHour/GetDATStationHourDisplayListAsync",
  headers: { SysCode: API_CONFIG.sysCode, Authorization: "Bearer " + RESULT_DATA.token },
  data: $.param({
    codes: cityData.map(function (item) { return item.stationCode; }),
    dataType: 0,
    timePoint: dayjs().format("YYYY-MM-DD HH:00:00")
  }, true),

关键点

  • codes:站点编码列表
  • dataType: 0:固定值
  • timePoint必需字段 ,格式 YYYY-MM-DD HH:00:00

步骤 7:Python 验证

python 复制代码
import requests

session = requests.Session()
session.verify = False

# 1. 获取 Token
token = session.get(
    API_BASE + "/api/noiseproduct/AirCityBaseCommon/GetExternalApiToken",
    params={"UserName": "InterfaceCall", "SecretKey": "******"}
).json()["result"]

# 2. 获取站点列表
stations = session.get(
    API_BASE + "/api/noiseproduct/AirCityProductBase/GetBSDStationListAsync",
    headers={"SysCode": "NOISE", "Authorization": f"Bearer {token}"}
).json()["result"]

# 3. 获取噪声数据
data = session.get(
    API_BASE + "/api/noiseproduct/airdata/DATStationHour/GetDATStationHourDisplayListAsync",
    headers={"SysCode": "NOISE", "Authorization": f"Bearer {token}"},
    params={"codes": "1001A", "dataType": 0, "timePoint": "2026-05-22 12:00:00"}
).json()["result"]

数据流转图

复制代码
─────────────────────────────────────────────────────────────┐
│  1. GET /GetExternalApiToken                                │
│     → Token: "a24dbae..."                                  │
─────────────────────────────────────────────────────────────┤
│  2. GET /GetBSDStationListAsync                             │
│     Headers: SysCode=NOISE, Authorization=Bearer {token}    │
│     → 154 个站点 [{stationCode:"1001A", ...}, ...]         │
├─────────────────────────────────────────────────────────────┤
│  3. GET /GetDATStationHourDisplayListAsync                  │
│     ?codes=1001A&dataType=0&timePoint=2026-05-22 12:00:00  │
│     → 噪声数据 [{code:"1001A", leq:"48.4", ...}]           │
└─────────────────────────────────────────────────────────────

API 接口详情

1. 获取 Token

项目
URL https://*.jlzslw.com:50003/api/noiseproduct/AirCityBaseCommon/GetExternalApiToken
方法 GET
参数 UserName={硬编码用户名}&SecretKey={硬编码密钥}
返回 {"success":true, "result":"a24dbae..."}

2. 获取站点列表

项目
URL https://*.jlzslw.com:50003/api/noiseproduct/AirCityProductBase/GetBSDStationListAsync
方法 GET
Headers SysCode: NOISE Authorization: Bearer {token}
返回 {"result": [{id, positionName, stationCode, cityName, ...}]}

3. 获取噪声数据

项目
URL https://*.jlzslw.com:50003/api/noiseproduct/airdata/DATStationHour/GetDATStationHourDisplayListAsync
方法 GET
Headers 同上
参数 codes={站点编码}&dataType=0&timePoint={YYYY-MM-DD HH:00:00}
返回 {"result": [{code, name, leq, l5, l10, ...}]}

噪声数据字段说明

字段 说明 示例
code 站点编码 1001A
name 站点名称 某某小区
timePoint 监测时间 2026-05-22T12:00:00
leq 等效声级 dB(A) 48.4
l5 L5 52.9
l10 L10 51.4
l50 L50 46.1
l90 L90 42.3
lMax 最大声级 64.1
temperature 温度 ℃ 25.4
humidity 湿度 % 43
windSpeed 风速 m/s 2.3
isOverStandard 是否超标 false

与高难度站点的对比

对比项 高难度站点 本案站点
逆向难度 极难(VM 字节码混淆) 简单(JS 明文)
Token 来源 动态生成(解密函数) 固定凭据调用接口
API 地址 自有系统 第三方数据平台
数据托管 自有 托管至第三方平台

认证凭据(已脱敏)

复制代码
baseUrl:    https://*.jlzslw.com:50003
username:   InterfaceCall
secretKey:  ******
sysCode:    NOISE

注意:这些凭据硬编码在前端 JS 中,无需注册即可使用。


免责声明

本文仅用于技术学习和研究目的,展示的是如何通过前端逆向分析理解数据接口的工作原理。实际使用时请遵守相关法律法规和网站服务条款。

相关推荐
quantdash_cc几秒前
量化数据管道为什么会出现历史 K 线缺口?从 API 请求到数据质量的完整排查方法
开发语言·python·数据分析·量化·股票数据·quantdash
用户77833661321116 小时前
用 React Hook 封装搜索数据:useSerp 的防抖、缓存与错误处理
python·api
65岁退休Coder19 小时前
LangGraph v1.2.9 节点容错策略 & 流式输出 & 持久化记忆管理
后端·python·langchain
ikun_文1 天前
Django框架路由Router的使用
python·pycharm·django
IvanCodes1 天前
Python 基础语法(二):字符串与常用操作
python
昭昭日月明1 天前
LangChain 生态:从链到代理,开发者需要掌握的三大核心
python·langchain·agent
Csvn1 天前
🐍 Day 8:面向对象编程
后端·python
程序员天天困1 天前
向量检索不准怎么办:混合检索与 Rerank 重排序召回优化实战
后端·python·ai编程
alphaTao1 天前
LeetCode 每日一题 2026/8/24-2026/8/30
python·算法·leetcode
苏灿烤鱼1 天前
当 AI Agent 遇见真实科学环境:深度拆解 Scientific Agent Skills,把"聊天机器人"变成"AI 科学家"
python·开源·agent