代理XMLHttpRequest,hook特定Ajax请求,隐层aes加密

通过代理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(), "加密解密错误"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值