| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- """
- POC 自验证脚本 - 不需要用户账号密码
- ===================================
- 目的:纯验证 HMAC 签名算法和 Python 实现是否正确。
- 方法:调 996 的 /api/user/appParam 接口(公开接口,不触发登录),
- 带上我们算的 sign,看是否返回 captcha_id。
- 如果返回 200 + data.captcha_id:签名层 ✓ 通过
- 如果返回签名错误/参数错误:签名层有问题,要修
- 如果网络错误:网络问题,跟签名无关
- """
- import hashlib
- import hmac
- import io
- import json
- import sys
- import urllib.parse
- # 强制 UTF-8 输出,避开 Windows PowerShell 的 GBK 编码陷阱
- if hasattr(sys.stdout, "reconfigure"):
- sys.stdout.reconfigure(encoding="utf-8", errors="replace")
- sys.stderr.reconfigure(encoding="utf-8", errors="replace")
- try:
- import requests
- import urllib3
- # 开发期自验证书,签名层已通过后再切回严格模式
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
- except ImportError:
- print("缺少 requests,请运行: pip install requests")
- sys.exit(1)
- SIGN_KEY = "k1ebbkxlojjdbnffnp3w2ykk3p625xnn"
- APPID = 155342
- GAME_ID = 214256
- PROMOTE_ID = 261888
- def key_sort(params: dict) -> str:
- return "&".join(f"{k}={params[k]}" for k in sorted(params.keys()))
- def hmac_str(sign_str: str, key: str) -> str:
- sig = hmac.new(key.encode("utf-8"), sign_str.encode("utf-8"), hashlib.sha1).hexdigest()
- return urllib.parse.quote(sig, safe="")
- def main():
- print("=" * 60)
- print("POC 签名层自验证")
- print("=" * 60)
- print()
- print("1) 计算 HmacSHA1 签名(与前端 JS 对照)")
- print("-" * 60)
- # 复现 protocolInit 调 appParam 时的签名
- params = {
- "appid": APPID,
- "game_id": GAME_ID,
- "vType": "csvKey",
- }
- sorted_str = key_sort(params)
- print(f" keySort 后: {sorted_str!r}")
- print(f" 期望顺序: appid={APPID}&game_id={GAME_ID}&vType=csvKey")
- print(f" 一致: {sorted_str == f'appid={APPID}&game_id={GAME_ID}&vType=csvKey'}")
- sig = hmac_str(sorted_str, SIGN_KEY)
- print(f" HmacSHA1 hex: {hmac.new(SIGN_KEY.encode(), sorted_str.encode(), hashlib.sha1).hexdigest()}")
- print(f" URL-encoded: {sig}")
- print()
- print("2) 调 /api/user/appParam 验证签名是否被服务端接受")
- print("-" * 60)
- print(" [重要] 前端调这个接口用的是 form-urlencoded,不是 JSON")
- print(" (axios.post(url, params) 默认走 form)")
- print()
- signed = dict(params)
- signed["sign"] = hmac_str(key_sort(signed), SIGN_KEY)
- # 保证 appid/game_id 是字符串(与前端 URL 参数一致)
- signed["appid"] = str(signed["appid"])
- signed["game_id"] = str(signed["game_id"])
- url = "https://user-sdkv2.dhsf.996db.cn/api/user/appParam"
- print(f" POST {url}")
- print(f" form body: {signed}")
- print()
- try:
- # appParam 是 form-urlencoded,不用 json=
- r = requests.post(
- url,
- data=signed, # form-urlencoded
- headers={"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
- timeout=15,
- verify=False,
- )
- except requests.RequestException as e:
- print(f"[NETWORK_ERROR] {type(e).__name__}: {e}")
- print(" 可能原因:DNS 解析失败 / 996 服务端问题 / 你的网络环境")
- sys.exit(1)
- print(f" HTTP 状态: {r.status_code}")
- try:
- body = r.json()
- except Exception:
- body = {"raw": r.text[:500]}
- print(f" 返回 body: {json.dumps(body, ensure_ascii=False, indent=2)[:1000]}")
- print()
- print("=" * 60)
- code = body.get("code")
- if code == 200 and body.get("data", {}).get("captcha_id"):
- print("[OK] 签名层验证通过!")
- print(f" captcha_id = {body['data']['captcha_id']}")
- print(f" jiyan_status = {body['data'].get('jiyan_status')}")
- print(" 下一步:用 demo_login.py 跑 --login 验证完整登录流程")
- elif code == 10000 and "appid" in str(body.get("msg", "")):
- print("[FAIL] 仍然 'appid 不能为空'")
- print(" 可能问题:")
- print(" - appid 的实际值不对(你 URL 里可能是另一个值)")
- print(" - form 编码有问题(试试看请求实际 body 是什么)")
- print(" - 还有别的必填字段没传")
- elif "sign" in str(body).lower() or "签名" in str(body):
- print("[FAIL] 签名层失败")
- print(f" 返回: {body}")
- elif code is not None:
- print(f"[NOTE] code={code}, msg={body.get('msg')!r}")
- print(" 签名可能通过,但有别的参数或风控问题")
- else:
- print("[WARN] 无法解析返回")
- print("=" * 60)
- if __name__ == "__main__":
- main()
|