Crispin's Blog

开发一个 Chrome DevTools 接口解密插件

在金融项目中,通常接口url和参数都会进行加密,所以在调试的时候,Network 面板里看到的全是密文,调试起来非常痛苦。这篇文章介绍我开发的 xxx Network —— 一个 Chrome DevTools 插件,能够自动拦截并解密 xxx 系统的加密接口,让调试回归正常。

背景

xxx 系统的接口采用 AES-CBC 加密,请求体、响应体以及真实路径都是密文。打开 Chrome DevTools 的 Network 面板,看到的是这样的内容:

1
2
3
request body:  "U2FsdGVkX1+..."
response body: "U2FsdGVkX1+..."
x_x_path: "U2FsdGVkX1+..."

每次调试都要手动解密,效率极低。于是我决定写一个 DevTools 插件,把解密过程自动化。

技术方案

整体架构分两层:

  • Chrome Extension 层:利用 chrome.devtools.network API 监听网络请求,用 chrome.scripting.executeScript 从页面 localStorage 读取加密密钥(xxxSalt)。
  • 前端 UI 层:用 Vue 3 + Element Plus 构建 DevTools 面板,展示解密后的请求列表和响应内容,并支持语法高亮。
1
2
3
4
5
6
7
8
9
10
xxx-network/
├── manifest.json # 插件配置
├── devtools.html # DevTools 入口页
├── devtools.js # 注册自定义面板
├── xxx-network/ # Vue3 前端项目
│ └── src/
│ ├── util.ts # AES 解密核心逻辑
│ └── components/
│ └── Main.vue # 主界面
└── chrome-plugin-xxx-network.sh # 打包脚本

核心:AES-CBC 解密

加密算法是 AES-CBC + PKCS7 填充,IV 固定为 xxx_mgr_item_a,密钥由 xxxSalt 经过一次解密得到(两层嵌套)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// util.ts
function getIvAndkey(xxxSalt: string, key: string) {
return {
iv: "xxx_mgr_item_a",
key: decrypt(xxxSalt, { iv: "xxx_mgr_item_a", key }),
};
}

function decrypt(str: string, { iv, key }: { iv: string; key: string }) {
if (str.startsWith("{")) return formatJSONString(str); // 未加密,直接解析
str = str.replace(/"/gi, "");
const decrypted = CryptoJS.AES.decrypt(str, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC,
});
const result = decrypted.toString(CryptoJS.enc.Utf8).trim();
if (result.startsWith("{")) return formatJSONString(result);
return result;
}

generate 函数把一条原始网络请求转换成可读的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export const generate = (network: any, { content, salt }) => {
const token = network.request.headers.find((h) =>
["xxxtoken", "token"].includes(h.name.toLowerCase()),
);
const xxPath = network.request.headers.find(
(h) => h.name.toLowerCase() === "x_x_path",
);

const { iv, key } = getIvAndkey(salt, token?.value);

return {
status: network.response.status,
url: network.request.url,
path: decrypt(xxPath?.value, { iv, key }) || network.request.url, // 真实路径
params: JSON.stringify(
decrypt(network.request.postData?.text, { iv, key }),
),
reponseObj: decrypt(content, { iv, key }), // 解密响应体
};
};

监听网络请求

Main.vueonMounted 中,通过 chrome.devtools.network.onRequestFinished 监听请求完成事件,过滤出 /manager 路径下的接口,再用 chrome.scripting.executeScript 注入脚本从页面读取 xxxSalt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
chrome.devtools.network.onRequestFinished.addListener((request) => {
if (!request.request.url.includes("/manager")) return;

request.getContent((content) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.scripting
.executeScript({
target: { tabId: tabs[0].id },
function: () => localStorage.getItem("xxxSalt"),
})
.then(([{ result: salt }]) => {
requests.push(generate(request, { salt, content }));
});
});
});
});

UI 界面

面板左侧是请求列表,展示状态码、URL、解密后的真实路径和请求参数;右侧点击 “Response” 后显示格式化的 JSON 响应,通过 Prism.js 做语法高亮(字符串绿色、数字蓝色、布尔值红色)。

操作按钮只有两个,保持简洁:

  • Clear All:清空所有请求记录
  • Copy Response:复制当前响应内容到剪贴板

构建与安装

1
2
3
4
5
6
7
8
# 开发调试
cd xxx-network
npm i && npm run dev

# 打包
npm run build
cd ..
bash ./chrome-plugin-xxx-network.sh

脚本会把 Vite 构建产物、manifest.jsondevtools.js 等文件整合到 chrome-xxx-network/ 目录,在 Chrome 的 chrome://extensions/ 页面加载该目录即可。

manifest.json 关键配置:

1
2
3
4
5
6
{
"manifest_version": 3,
"permissions": ["tabs", "activeTab", "scripting"],
"devtools_page": "devtools.html",
"host_permissions": ["http://localhost/*", "*://*.xxx.com/*"]
}

host_permissions 限定了插件生效的域名,避免对无关站点产生影响。

小结

整个插件的核心思路很清晰:DevTools API 拦截请求 → 从页面上下文读取密钥 → AES 解密 → Vue UI 展示。代码量不大,但解决了实际痛点。

如果你的项目也有类似的接口加密场景,可以参考这个思路,把解密逻辑替换成自己的算法,快速定制一个专属的 DevTools 调试面板。