verify_poc.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. """
  2. POC 自验证脚本 - 不需要用户账号密码
  3. ===================================
  4. 目的:纯验证 HMAC 签名算法和 Python 实现是否正确。
  5. 方法:调 996 的 /api/user/appParam 接口(公开接口,不触发登录),
  6. 带上我们算的 sign,看是否返回 captcha_id。
  7. 如果返回 200 + data.captcha_id:签名层 ✓ 通过
  8. 如果返回签名错误/参数错误:签名层有问题,要修
  9. 如果网络错误:网络问题,跟签名无关
  10. """
  11. import hashlib
  12. import hmac
  13. import io
  14. import json
  15. import sys
  16. import urllib.parse
  17. # 强制 UTF-8 输出,避开 Windows PowerShell 的 GBK 编码陷阱
  18. if hasattr(sys.stdout, "reconfigure"):
  19. sys.stdout.reconfigure(encoding="utf-8", errors="replace")
  20. sys.stderr.reconfigure(encoding="utf-8", errors="replace")
  21. try:
  22. import requests
  23. import urllib3
  24. # 开发期自验证书,签名层已通过后再切回严格模式
  25. urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  26. except ImportError:
  27. print("缺少 requests,请运行: pip install requests")
  28. sys.exit(1)
  29. SIGN_KEY = "k1ebbkxlojjdbnffnp3w2ykk3p625xnn"
  30. APPID = 155342
  31. GAME_ID = 214256
  32. PROMOTE_ID = 261888
  33. def key_sort(params: dict) -> str:
  34. return "&".join(f"{k}={params[k]}" for k in sorted(params.keys()))
  35. def hmac_str(sign_str: str, key: str) -> str:
  36. sig = hmac.new(key.encode("utf-8"), sign_str.encode("utf-8"), hashlib.sha1).hexdigest()
  37. return urllib.parse.quote(sig, safe="")
  38. def main():
  39. print("=" * 60)
  40. print("POC 签名层自验证")
  41. print("=" * 60)
  42. print()
  43. print("1) 计算 HmacSHA1 签名(与前端 JS 对照)")
  44. print("-" * 60)
  45. # 复现 protocolInit 调 appParam 时的签名
  46. params = {
  47. "appid": APPID,
  48. "game_id": GAME_ID,
  49. "vType": "csvKey",
  50. }
  51. sorted_str = key_sort(params)
  52. print(f" keySort 后: {sorted_str!r}")
  53. print(f" 期望顺序: appid={APPID}&game_id={GAME_ID}&vType=csvKey")
  54. print(f" 一致: {sorted_str == f'appid={APPID}&game_id={GAME_ID}&vType=csvKey'}")
  55. sig = hmac_str(sorted_str, SIGN_KEY)
  56. print(f" HmacSHA1 hex: {hmac.new(SIGN_KEY.encode(), sorted_str.encode(), hashlib.sha1).hexdigest()}")
  57. print(f" URL-encoded: {sig}")
  58. print()
  59. print("2) 调 /api/user/appParam 验证签名是否被服务端接受")
  60. print("-" * 60)
  61. print(" [重要] 前端调这个接口用的是 form-urlencoded,不是 JSON")
  62. print(" (axios.post(url, params) 默认走 form)")
  63. print()
  64. signed = dict(params)
  65. signed["sign"] = hmac_str(key_sort(signed), SIGN_KEY)
  66. # 保证 appid/game_id 是字符串(与前端 URL 参数一致)
  67. signed["appid"] = str(signed["appid"])
  68. signed["game_id"] = str(signed["game_id"])
  69. url = "https://user-sdkv2.dhsf.996db.cn/api/user/appParam"
  70. print(f" POST {url}")
  71. print(f" form body: {signed}")
  72. print()
  73. try:
  74. # appParam 是 form-urlencoded,不用 json=
  75. r = requests.post(
  76. url,
  77. data=signed, # form-urlencoded
  78. headers={"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"},
  79. timeout=15,
  80. verify=False,
  81. )
  82. except requests.RequestException as e:
  83. print(f"[NETWORK_ERROR] {type(e).__name__}: {e}")
  84. print(" 可能原因:DNS 解析失败 / 996 服务端问题 / 你的网络环境")
  85. sys.exit(1)
  86. print(f" HTTP 状态: {r.status_code}")
  87. try:
  88. body = r.json()
  89. except Exception:
  90. body = {"raw": r.text[:500]}
  91. print(f" 返回 body: {json.dumps(body, ensure_ascii=False, indent=2)[:1000]}")
  92. print()
  93. print("=" * 60)
  94. code = body.get("code")
  95. if code == 200 and body.get("data", {}).get("captcha_id"):
  96. print("[OK] 签名层验证通过!")
  97. print(f" captcha_id = {body['data']['captcha_id']}")
  98. print(f" jiyan_status = {body['data'].get('jiyan_status')}")
  99. print(" 下一步:用 demo_login.py 跑 --login 验证完整登录流程")
  100. elif code == 10000 and "appid" in str(body.get("msg", "")):
  101. print("[FAIL] 仍然 'appid 不能为空'")
  102. print(" 可能问题:")
  103. print(" - appid 的实际值不对(你 URL 里可能是另一个值)")
  104. print(" - form 编码有问题(试试看请求实际 body 是什么)")
  105. print(" - 还有别的必填字段没传")
  106. elif "sign" in str(body).lower() or "签名" in str(body):
  107. print("[FAIL] 签名层失败")
  108. print(f" 返回: {body}")
  109. elif code is not None:
  110. print(f"[NOTE] code={code}, msg={body.get('msg')!r}")
  111. print(" 签名可能通过,但有别的参数或风控问题")
  112. else:
  113. print("[WARN] 无法解析返回")
  114. print("=" * 60)
  115. if __name__ == "__main__":
  116. main()