通过代理XMLHttpRequest.prototype原型的open方法hook特定的Ajax请求
// hook.js
const hook_urls = ["/user/info.do"];
(function () {
let proxy_open = window.XMLHttpRequest.prototype.open;
// let proxy_send = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function (method, url) {
const encryptkey_c = Math.random();
this.onprogress = function (event) {
if (event.loaded == event.total) {
if (this.readyState >= 3 && this.status === 200 && this.getResponseHeader("encryptkey")) {
const encryptkey_s = this.getResponseHeader("encryptkey");
const mode = "a".charCodeAt() - "0".charCodeAt();
const key_ = ((encryptkey_c + parseFloat(encryptkey_s)).toString().replace(".", "") + "0000000000000000").substr(0, 16);
let key = "";
for (let i = 0; i < key_.length; ++i) {
key += String.fromCharCode((key_[i].charCodeAt() + mode))
}
let response = JSON.parse(this.responseText);
if (response.code === "0000") {
response.data = aes_cbc_pkcs7_decrypt(key, response.data);
response.data = JSON.parse(response.data);
Object.defineProperty(this, 'responseText', {
writable: true
});
this.responseText = JSON.stringify(response);
console.log("result:", key, this.responseText);
}
}
}
}
// this.send = function (body) {
// for (let i = 0; i < hook_urls.length; ++i) {
// if (url.startsWith(hook_urls[i])) {
// body = aes_cbc_pkcs7_encrypt(encryptkey_c.toString().substr(0, 16), body);
// break;
// }
// }
// proxy_send.apply(this, [].slice.call(body));
// }
let result = proxy_open.apply(this, [].slice.call(arguments));
for (let i = 0; i < hook_urls.length; ++i) {
if (url.startsWith(hook_urls[i])) {
this.setRequestHeader("encryptkey", aes_cbc_pkcs7_encrypt(IV, encryptkey_c.toString()));
break;
}
}
return result;
};
})();
// aes.js
const IV = "abcdefghijklmnop";
function aes_cbc_pkcs7_encrypt(key, data) {
// CryptoJS.AES.encrypt 需要utf8编码的字符串
let result = CryptoJS.AES.encrypt(
data,
CryptoJS.enc.Utf8.parse(key),
{
iv: CryptoJS.enc.Utf8.parse(IV),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
// return CryptoJS.enc.Hex.stringify(result.ciphertext); //HEX
return CryptoJS.enc.Base64.stringify(result.ciphertext) //base64
}
function aes_cbc_pkcs7_decrypt(key, data) {
// CryptoJS.AES.decryptf 需要Base64编码的字符串 base64不需要下面两行
// data = CryptoJS.enc.Hex.parse(data);
// data = CryptoJS.enc.Base64.stringify(data);
let result = CryptoJS.AES.decrypt(
data,
CryptoJS.enc.Utf8.parse(key),
{
iv: CryptoJS.enc.Utf8.parse(IV),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
return CryptoJS.enc.Utf8.stringify(result);
}
<!--index.html-->
<html>
<head>
<title>
ajax hook
</title>
<script src="http://code.jquery.com/jquery-1.8.3.js" type="text/javascript"></script>
<script src="/static/crypto-js/4.1.1/core.js"></script>
<script src="/static/crypto-js/4.1.1/enc-base64.js"></script>
<script src="/static/crypto-js/4.1.1/md5.js"></script>
<script src="/static/crypto-js/4.1.1/evpkdf.js"></script>
<script src="/static/crypto-js/4.1.1/cipher-core.js"></script>
<script src="/static/crypto-js/4.1.1/aes.js"></script>
<script src="/static/crypto-js/4.1.1/pad-pkcs7.js"></script>
<script src="/static/crypto-js/4.1.1/mode-ecb.js"></script>
<script src="/static/crypto-js/4.1.1/enc-utf8.js"></script>
<script src="/static/crypto-js/4.1.1/enc-hex.js"></script>
<script src="/static/aes.js" type="text/javascript"></script>
<script src="/static/hook.js" type="text/javascript"></script>
</head>
<body>
<div id="user">
</div>
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: "/user/info.do",
type: "GET",
data: {
id: "001"
},
dataType: "json",
success: (response) => {
$("#user").html(response.data.name);
}
});
});
</script>
</body>
</html>
网络传递字符串可以有Base64和Hex两种格式,前后端注意对应
#app.py
import json
import random
from flask import Flask, make_response, render_template, request, jsonify
from utils.aes import aes_cbc_pkcs7_encrypt, aes_cbc_pkcs7_decrypt, iv
# 所有api请求json返回,格式{succ": True, "code": "0000", "msg": "成功", "data":dict}
app = Flask(import_name="demo", template_folder="template", static_folder="static", static_url_path="/static")
user = {"001": {"id": "001", "password": "吃俺老孙一棒", "name": "孙悟空", "age": "500"}, "002": {"id": "002", "password": "南无阿弥陀佛", "name": "唐三藏", "age": "30"}}
def api_response(func):
# data: dict, code: str = "0000", succ: bool = True, mag: str = "成功"
def wrap(*args, **kwargs):
result = func(*args, **kwargs)
if isinstance(result, dict):
data, code, msg = result, "0000", "成功"
elif isinstance(result, list):
data, code, msg = result
else:
assert "返回非标准格式"
if "encryptkey" in request.headers and code == "0000":
# 转化key
encryptkey_c = aes_cbc_pkcs7_decrypt(iv, request.headers["encryptkey"])
encryptkey_c, encryptkey_s, mod = float(encryptkey_c), random.random(), ord("a") - ord("0")
key = (str(encryptkey_c + encryptkey_s).replace(".", "") + "0000000000000000")[:16]
key = "".join(map(lambda c: chr(mod + ord(c)), key))
# 加密
data = aes_cbc_pkcs7_encrypt(key, json.dumps(data))
response = jsonify({"code": code, "msg": msg, "data": data})
response.headers["encryptkey"] = encryptkey_s
else:
response = jsonify({"code": code, "msg": msg, "data": data})
return response
return wrap
@app.route(rule="/index.html")
def index():
return make_response(render_template("index.html"))
@app.route(rule="/user/info.do", methods=["GET"])
@api_response
def user_info():
user_id = request.args["id"]
return user[user_id]
if __name__ == "__main__":
print(app.url_map)
app.run()
# utils/aes.py
import base64
import binascii
from Crypto.Cipher import AES
iv = "abcdefghijklmnop"
def pkcs7padding(text):
"""
明文使用PKCS7填充
最终调用AES加密方法时,传入的是一个byte数组,要求是16的整数倍,因此需要对明文进行处理
:param text: 待加密内容(明文)
:return:
"""
bs = AES.block_size # 16
length = len(text)
bytes_length = len(bytes(text, encoding='utf-8'))
# tips:utf-8编码时,英文占1个byte,而中文占3个byte
padding_size = length if (bytes_length == length) else bytes_length
padding = bs - padding_size % bs
# tips:chr(padding)看与其它语言的约定,有的会使用'\0'
padding_text = chr(padding) * padding
return text + padding_text
def pkcs7unpadding(text):
"""
处理使用PKCS7填充过的数据
:param text: 解密后的字符串
:return:
"""
length = len(text)
unpadding = ord(text[length - 1])
return text[0:length - unpadding]
def aes_cbc_pkcs7_encrypt(key: str, data: str) -> str:
"""aes加密
:Parameters:
key: str 加密密钥(16位不足补空,超过截取)
data : str 加密前字符串(16倍数不足补空)
:Return:
str 加密后字符串
"""
data = pkcs7padding(data).encode("utf-8")
cipher = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8"))
result = cipher.encrypt(data)
# result = binascii.b2a_hex(result).decode("utf-8") #输出Hex
result = base64.b64encode(result).decode("utf-8") # 输出Base64
print("result", key, result)
return result
def aes_cbc_pkcs7_decrypt(key: str, data: str) -> str:
"""aes解密
:Parameters:
key: str 加密密钥(16位不足补空,超过截取)
data : str 加密字符串(16倍数不足补空)
:Return:
str 加密前字符串
"""
# data = binascii.a2b_hex(data.encode("utf-8"))
data = base64.b64decode(data.encode("utf-8"))
cipher = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8"))
result = cipher.decrypt(data).decode("utf-8")
return pkcs7unpadding(result)
__all__ = [aes_cbc_pkcs7_encrypt, aes_cbc_pkcs7_decrypt, iv]
if __name__ == "__main__":
key, data = "test413435432512", "test孙悟空"
data1 = aes_cbc_pkcs7_decrypt(key, aes_cbc_pkcs7_encrypt(key, data))
assert data.strip() == data1.strip(), "加密解密错误"