Features: - Receive SMS from TranspondSms Android APP - HMAC-SHA256 signature verification (optional) - SQLite database storage - Web UI with login authentication - Multiple API tokens support - Timezone conversion (Asia/Shanghai) - Search, filter, and statistics - Auto refresh and session management Tech Stack: - Flask 3.0 - SQLite database - HTML5/CSS3 responsive design
119 lines
3.1 KiB
Python
119 lines
3.1 KiB
Python
"""
|
||
签名验证工具
|
||
参考 TranspondSms 的签名规则:
|
||
timestamp + "\n" + secret -> HMAC-SHA256 -> Base64 -> URL Encode
|
||
"""
|
||
|
||
import hmac
|
||
import hashlib
|
||
import base64
|
||
import urllib.parse
|
||
import time
|
||
|
||
|
||
def generate_sign(secret: str, timestamp: int = None) -> str:
|
||
"""
|
||
生成签名
|
||
|
||
Args:
|
||
secret: 密钥
|
||
timestamp: 时间戳(毫秒),不传则使用当前时间
|
||
|
||
Returns:
|
||
签名字符串
|
||
"""
|
||
if timestamp is None:
|
||
timestamp = int(time.time() * 1000)
|
||
|
||
string_to_sign = f"{timestamp}\n{secret}"
|
||
hmac_code = hmac.new(
|
||
secret.encode('utf-8'),
|
||
string_to_sign.encode('utf-8'),
|
||
digestmod=hashlib.sha256
|
||
).digest()
|
||
sign = urllib.parse.quote(base64.b64encode(hmac_code).decode())
|
||
|
||
return sign
|
||
|
||
|
||
def verify_sign(secret: str, sign: str, timestamp: int, max_age: int = 3600000) -> tuple[bool, str]:
|
||
"""
|
||
验证签名
|
||
|
||
Args:
|
||
secret: 密钥
|
||
sign: 待验证的签名
|
||
timestamp: 时间戳(毫秒)
|
||
max_age: 签名最大有效时间(毫秒),默认1小时
|
||
|
||
Returns:
|
||
(是否有效, 错误信息)
|
||
"""
|
||
current_time = int(time.time() * 1000)
|
||
|
||
# 检查时间戳是否过期
|
||
if abs(current_time - timestamp) > max_age:
|
||
return False, f"签名过期,时间差: {abs(current_time - timestamp) / 1000:.1f}秒"
|
||
|
||
# 生成期望的签名
|
||
expected_sign = generate_sign(secret, timestamp)
|
||
|
||
# 比较签名
|
||
if sign != expected_sign:
|
||
return False, "签名不匹配"
|
||
|
||
return True, "签名有效"
|
||
|
||
|
||
def verify_from_app(from_number: str, content: str, timestamp: int,
|
||
sign: str, secret: str, max_age: int = 3600000) -> tuple[bool, str]:
|
||
"""
|
||
验证 TranspondSms APP 发来的请求
|
||
|
||
Args:
|
||
from_number: 发送方手机号
|
||
content: 短信内容
|
||
timestamp: 时间戳
|
||
sign: 签名
|
||
secret: 密钥
|
||
max_age: 最大有效时间(毫秒)
|
||
|
||
Returns:
|
||
(是否有效, 错误信息)
|
||
"""
|
||
# 检查必填字段
|
||
if not from_number or not content:
|
||
return False, "缺少必填字段"
|
||
|
||
# 如果没有签名,跳过验证(取决于配置)
|
||
if not sign:
|
||
return True, "无签名,跳过验证"
|
||
|
||
return verify_sign(secret, sign, timestamp, max_age)
|
||
|
||
|
||
# 测试代码
|
||
if __name__ == '__main__':
|
||
# 测试签名生成和验证
|
||
secret = "test_secret"
|
||
timestamp = int(time.time() * 1000)
|
||
|
||
# 生成签名
|
||
sign = generate_sign(secret, timestamp)
|
||
print(f"Timestamp: {timestamp}")
|
||
print(f"Sign: {sign}")
|
||
|
||
# 验证签名
|
||
is_valid, message = verify_sign(secret, sign, timestamp)
|
||
print(f"验证结果: {is_valid}, {message}")
|
||
|
||
# 测试过期签名
|
||
old_timestamp = timestamp - 7200000 # 2小时前
|
||
is_valid, message = verify_sign(secret, sign, old_timestamp)
|
||
print(f"过期验证: {is_valid}, {message}")
|
||
|
||
# 测试错误签名
|
||
wrong_sign = "wrong_signature"
|
||
is_valid, message = verify_sign(secret, wrong_sign, timestamp)
|
||
print(f"错误签名: {is_valid}, {message}")
|