0


请求数据通过URL加入sign验证加密与解密

1.通过Sign加密的请求URL案例

不少网站做数据反爬虫会做一系列的的措施,就包括这里要说的url加密,例如如下url是请求b站游戏id为109905的游戏的评分数据 (无意冒犯,仅首页随机选取做学习案例使用):

https://line1-h5-pc-api.biligame.com/game/comment/summary?game_base_id=109905&ts=1679988261931&request_id=YKceEELUnX5m4aELQACuqX2mG48wv13B&appkey=h9Ejat5tFh81cq8V&sign=00d3c6791f798be86bac45576b31dc32

大家能看出, 这个请求包含了5个参数:

game_base_id: 109905
ts: 1679988261931
request_id: YKceEELUnX5m4aELQACuqX2mG48wv13B
appkey: h9Ejat5tFh81cq8V
sign: 00d3c6791f798be86bac45576b31dc32

通过此请求可以得到正常的返回数据:

{“code”:0,“message”:“成功”,“request_id”:“9dbfc9cac59045709dcda62e32cac670”,“ts”:1679988274628,“data”:{“grade”:8.3,“comment_number”:1709,“valid_comment_number”:1704,“star_number_list”:[246,59,69,41,951],“state”:3}}

回头分析那5个参数,明显对于用户和实际申请数据来说, 只需要game_baseid这一个参数就足够了.
ts明显是时间戳,而request_id翻译过来应该是随机生成的请求ID.
根据经验, 参数sign放请求参数里就大多是加密了. 那么我们假设其他4个数据都是作为加密使用的.
为了验证我们的想法, 我们可以任意修改这5个参数中的一个.
发现无论修改五个中的哪一个, 都会得到错误的返回:

{“code”:-1203,“message”:“invalid request”}

说明五个参数都参与了加密或者验证.

当其他人想通过复制这个URL, 然后仅修改game_base_id参数来获取各游戏的数据, 就都会遇到

invalid request

的错误返回.

2.解密, 考察sign加密方式

这是个不错的防爬虫方案, 那么这个sign具体是如何生成的呢. 我们可以去测试更多的其他请求,来对比所有请求的参数构造.
如appid分别为107681和141的请求的参数:

game_base_id: 107681
ts: 1679989802458
request_id: WjSH6StUYwo0LcFi5yhvfoopzWXKdNTU
appkey: h9Ejat5tFh81cq8V
sign: 528c7740a4e62d01e6f208db4f1409ac

game_base_id: 141
ts: 1679989855423
request_id: vYUoWMZTDHAx6wnFYh6akUXzCmcNd060
appkey: h9Ejat5tFh81cq8V
sign: 1687d53da12c13fb8c3a2d207d434fc4

通过比较多个请求,我们发现

  1. sign都是32位, 而常用的md5加密也有32位
  2. appkey都没变
  3. request_id通常都是随机的,只是参与加密验证,而不会直接验证真伪

之后的解密稍微走了些弯路: 暴力尝试. 这个在踩坑环节再说. 下面继续说正确的解密姿势

既然目前最大的可能是通过sign加密,那么我们就开始下一步,找源码.
我这里是chrome浏览器, 按F12进入调试模式, F5刷新请求数据的页面. Ctrl+Shift+F打开全局搜索, 搜 sign, 会搜到很多, 快速过滤掉不对的. 就找到了这一段代码:
在这里插入图片描述

    i[n(137)]="h9Ejat5tFh81cq8V",n(157));return i.sign =h(""[n(130)](d[n(122)](i,{sort:function(t, e){return t[n(152)](e)}}))[n(130)](e))

注意到这里甚至有我们的appkey, 所以基本可以确定这里就是我们要找到JS代码. 这里sign应该就是我们想找的算法, 但是这段JS被混淆处理了.
这里又走了些弯路–反混淆化, 也在后面踩坑环节说, 先继续正确的解密方法
我们在这里打断点,刷新页面
在这里插入图片描述首先看到这最外围的h()函数还是一个未知函数,我们先不管. 然后复制函数里所有东西,到控制台输出
在这里插入图片描述

一下就明朗了, 里面就是被加密的字符串. 当然我们可以继续细致的看各个部分的含义,比如出现了2次的n(130)函数是concat() 用concat连接的这段函数,得到的是拼起来的parameters. 包含了sign以外的全部4个参数. 拼接格式也得到了, 用’&'拼接, 而最后还拼接了一个字符串, 可以知道这个字符串是变量e.
这个e是不属于参数的, 我们称呼它为secret. 至于这个值具体如何取的. 我们先验证这个值是否固定, 如果不固定, 那么就要继续逆推JS.
我们再去打开其他appid的网站.重复以上调试方式.发现这个e值固定不变.
另外我们注意到代码里有个sort关键字. 因此sign的计算方式已经呼之欲出了:

将请求中所有明参数按key的字母排序, 以key=value格式化, 再用&拼接起来, 最后再拼接上secret得到原始字符, 对此字符进行md5加密, 得到最后的sign

然后我们来验证我们的想法. 下面是根据此猜想写的一个python自动生成带sign加密参数的URL的代码

deftaskMd5():import requests
    url = getURL(107681)
    res = requests.get(url)if res.status_code ==200:print(res.text)defgetURL(gameid):
    appkey ='h9Ejat5tFh81cq8V'
    secret =#上文提到的e值,这里按网站要求不暴露实际值#
    params ={'appkey': appkey,'game_base_id': gameid,'request_id':'cpdThHlVU7jZMfFzn33tBy7AvKMypvP5','ts': math.ceil(time.time()*1000)}
    str_params =[]for k in params.keys():
        str_params.append(f'{k}={params[k]}')
    org ='&'.join(str_params)+ secret
    #print(org)
    sign = getMD5(org)#print(sign)
    params['sign']= sign
    str_params.append(f'sign={sign}')
    url ='https://line1-h5-pc-api.biligame.com/game/comment/summary?'+'&'.join(str_params)#print(url)return url

任意测试一个实际存在的gameid, 返回成功

{“code”:0,“message”:“成功”,“request_id”:“c0bdf44236bf428ebc968813d8aad1ea”,“ts”:1679993440018,“data”:{“grade”:8.7,“comment_number”:18167,“valid_comment_number”:17708,“star_number_list”:[1412,394,744,1158,9619],“state”:3}}

验证成功!

至此我们通过此案例介绍了通过sign加密请求的常用方式, 即:
将参与加密的参数(不一定是所有参数), 按一定方式排序, 一定格式拼接, 再加上一个秘xx钥组合成原始字符串, 然后使用MD5或者SHA等加密, 得到sign, 在响应时进行验证

3.踩坑

最后说下坑

  1. 暴力反解MD5误区. 本以为这个sign加密可能appkey就是秘oo钥. sign可以只通过这4个参数计算, 就用了暴力反解, 尝试各种格式拼接计算出加密结果与实际sign值比较:
defmd5_crack():
    format_param =['{0}={1}','{1}']
    str_splits =['|','&','']
    key_format =['appkey={}',"{}"]
    appkey ='h9Ejat5tFh81cq8V'
    param ={'game_base_id':'109905','request_id':'ApHV8L2fNmxD5d2dd2mIPsuRo8YJvYhI','ts':'1679884518004'}
    sign ="cedaac200fd97738677b4beb3a2c50bd"for f in format_param:for kf in key_format:for sp in str_splits:
                str_params =[]for key in param.keys():
                    concat = f.format(key, param[key])
                    str_params.append(concat)
                alls =[kf.format(appkey)]+ str_params
                org = sp.join(alls)print(org)print(sign==getMD5(org), getMD5(org))print()
                alls = str_params +[kf.format(appkey)]
                org = sp.join(alls)print(org)print(sign==getMD5(org), getMD5(org))print()
  1. 反混淆误区. 发现加密代码被混淆化处理之后, 做反混淆的处理, 发现该代码混淆后足足有8W行, 很难完成反混淆处理.

本文转载自: https://blog.csdn.net/dreamworld204/article/details/129817213
版权归原作者 墨烬 所有, 如有侵权,请联系我们删除。

“请求数据通过URL加入sign验证加密与解密”的评论:

还没有评论