某公开数据简单逆向

政府网站 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 中,无需注册即可使用。


免责声明

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

相关推荐
asdzx671 小时前
Python PDF 拆分实战指南:单页拆分与按需页码范围拆分
开发语言·python·pdf
李昊哲小课1 小时前
咖啡工坊 · FastAPI 22 章教程合集
服务器·人工智能·python·fastapi
卷无止境1 小时前
Python 异常处理:从入门到工程实践
后端·python
garuda herb1 小时前
生成专题页Blog--建立并返回 MySQL 数据库连接
python·mysql
ONE_SIX_MIX2 小时前
qwenpaw-plugin-file-tools QwenPaw增强文件工具 更新 v1.1.1
python·qwenpaw
AOwhisky2 小时前
Python 学习笔记(第五期)——组合数据类型:列表、元组、集合与字典精讲——核心知识点自测与详解
开发语言·笔记·python·学习·云计算
码农学院2 小时前
GEO与SEO协同:从传统搜索到生成式搜索的平滑迁移路径
服务器·前端·python
程序员羽痕2 小时前
基于 Django + PyTorch 的中文字体识别系统
pytorch·python·django
大模型码小白12 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
麻雀飞吧13 小时前
最新量化学习路径,交易认知和技术实现要并行
人工智能·python