最近爬取京东后台数据,遇到一个棘手问题,京东后台的手机号中间4位是****隐藏的,提供一个点击查看按钮,实则是一个查询按钮,但返回的是一串加密串:
点击查看后我们看一下返回数据:
我们看到,他接口返回的是一个加密串,不用想,肯定是后台返回加密串,在前端进行解密然后显示在页面非隐藏的手机号,那么肯定是js渲染的,我们先找到并分析js代码:
$.getJSON("/order/json/phoneSensltiveInfo", data, function (data) {
if (data.success) {
if (data.model) {
if (data.model.phone) {
$("#tel").html(htm(data.model.phone));
$("#tel").next().hide();
}
我们可以看到,通过ajax请求到的加密的手机号,是在前端js通过一个htm()的函数进行解密的,那么我们来看下这个函数:
~
~
~
找了好久没找到,算了,直接在控制台打印出来:
通过分析代码:
function htm(p){
var key=CryptoJS.enc.Utf8.parse("5a8f3244786ea9b8");
var decrypt=CryptoJS.AES.decrypt(p,key,{mode:CryptoJS.mode.ECB,padding:CryptoJS.pad.Pkcs7});
return CryptoJS.enc.Utf8.stringify(decrypt).toString()}"
我们可以知道,这个加密规则是js 内库中的crypto-js 以AES的方式进行加密的,查阅相关资料我们知道,这个加密规则是对称加密,且可逆的,也就是可以解密的,并且我们在js 中可以看到加密使用的秘钥key = “5a8f3244786ea9b8”
好,到这里我们已经成功了一半了,接下来就是解密了,我们python中也有相关第三方库的,下面直接贴代码:
#coding=utf-8
#AES-demo AES/ECB/PKCS5|Zero
#ECB不安全.建议CBC
import base64
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
def ByteToHex( bins ):
"""
Convert a byte string to it's hex string representation e.g. for output.
"""
return ''.join( [ "%02X" % x for x in bins ] ).strip()
'''
采用AES对称加密算法
'''
# str不是16的倍数那就补足为16的倍数. ZeroPadding
'''
在PKCS5Padding中,明确定义Block的大小是8位
而在PKCS7Padding定义中,对于块的大小是不确定的,可以在1-255之间
PKCS #7 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。
假定块长度为 8,数据长度为 9,
数据: FF FF FF FF FF FF FF FF FF
PKCS7 填充: FF FF FF FF FF FF FF FF FF 01 01 01 01 01 01 01 ?应该是填充01
python3:填充bytes(这个说法不对,AES的参数是字符串,不是byte)
length = 16 - (len(data) % 16)
data += bytes([length])*length
python2:填充字符串
length = 16 - (len(data) % 16)
data += chr(length)*length
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
'''
def add_to_16(value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 返回bytes
def ZeroPadding(value,bs):
while len(value) % bs != 0:
value += '\0'
return str.encode(value) # 返回bytes
def PKCS7Padding(value,bs):
pad = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)#PKS7
return str.encode(pad(value)) # 返回bytes
def PKCS7UnPadding(value):
#value = value[:-value[-1]]
unpad = lambda s : s[0:-ord(s[-1])] #获得数据的长度,截取
return unpad(value)
#加密方法
def encrypt_oracle():
# 秘钥
key = '5a8f3244786ea9b8'
# 待加密文本
text = '18979034826'
# 初始化加密器
aes = AES.new(add_to_16(key), AES.MODE_ECB)
bs = AES.block_size
pad2 = lambda s: s + (bs - len(s) % bs) * chr(bs - len(s) % bs)#PKS7 就是pkcs7padding
#先进行aes加密
#Zeropadding
#encrypt_aes = aes.encrypt(add_to_16(text))
#Pkcs7 padding
encrypt_aes = aes.encrypt(str.encode(pad2(text)))
#转为hex
print(ByteToHex(encrypt_aes)) #转为字符串
print(b2a_hex(encrypt_aes)) #b''
#用base64转成字符串形式
encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') # 执行加密并转码返回bytes
print(encrypted_text)
#和js的 结果相同 http://tool.chacuo.net/cryptaes
return encrypted_text
#解密方法
def decrypt_oralce(text):
# 秘钥
key = '5a8f3244786ea9b8'
# 密文
# 初始化加密器
aes = AES.new(add_to_16(key), AES.MODE_ECB)
#优先逆向解密base64成bytes
base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
#
decrypted_text = str(aes.decrypt(base64_decrypted),encoding='utf-8') # 执行解密密并转码返回str
unpad = lambda s : s[0:-ord(s[-1])] #pkcs7unpadding比较简单.
print(unpad(decrypted_text))
if __name__ == '__main__':
en=encrypt_oracle()
decrypt_oralce(en)
代码很详细,复制直接可运行使用的,测试结果:
到这里就成功的将京东加密的手机号用python进行解密了,大功告成。
本次解密过程差不多花了一个星期的时间琢磨和查阅相关资料,走了很多弯路,希望可以给遇到同样场景的朋友们提供参考,少走弯路,如果有其他问题,在下方评论,看到了随时回复。
参考链接:https://blog.csdn.net/laputa73/article/details/80476005