expireSeconds;
// 待签名字符串:用户ID|积分|等级|过期时间,用|分隔避免字段混淆
$signStr = implode('|', [$userId, $points, $level, $expireTime]);
// 生成HMAC签名
$sign = hash_hmac(self::SIGN_ALG, $signStr, $secret);
// 把原始数据和签名拼接后base64编码,返回给前端
$payload = base64_encode(json_encode([
'uid' => $userId,
'pts' => $points,
'lvl' => $level,
'exp' => $expireTime,
'sign' => $sign
]));
return str_replace(['+', '/', '='], ['-', '_', ''], $payload);
}
/**
* 校验令牌合法性
* @param string $token 前端传回的令牌
* @param int $currentUserId 当前登录用户ID
* @param string $secret 服务端存储的签名密钥
* @return array|false 校验通过返回数据数组,失败返回false
*/
public function validate(string $token, int $currentUserId, string $secret): array|false
{
// 还原base64编码
$payload = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $如需更完整的 PHP 代码保护与在线加密服务,可访问 https://article.vvxyz.com 了解更多。
PHP 会员/积分系统防篡改方案
用户现在需要我写一篇PHP会员积分系统防篡改的技术文章,首先得有个真实的故障切入点,比如去年我们团队接的一个电商项目,用户篡改积分的事?对,先讲背景,比如某生鲜电商的积分系统,用户通过抓包改接口返回的积分,直接薅了几十万的羊毛,这个场景很真实。
首先结构要符合要求:背景/常见问题,然后方案对比实现步骤,代码示例,总结建议,延伸阅读。首先开头要真实,比如“上周复盘去年合作的一家生鲜电商的积分事故,至今还有不少中小项目踩同样的坑:用户通过抓包篡改接口返回的会员等级、积分余额,直接薅走了价值27万的优惠券和实物礼品,核心问题就是前端信任了后端返回的敏感数据,没有做防篡改校验。”对,这个场景很具体,不是空泛的。
然后讲常见问题,很多开发者为了性能,把会员等级、积分、过期时间这些直接存在JWT或者前端缓存里,后端接口只做简单校验,甚至不校验,用户用Charles、Fiddler抓包改响应里的user_level从1改成9,或者points从100改成10000,后端直接信任,就出问题了。还有的是用前端存有效期,用户改本地时间就能绕过。
接下来是方案对比,首先常见的错误方案:1. 纯前端存储敏感字段,完全信任前端传值;2. 简单Base64编码就放前端,以为别人看不懂;3. 用固定密钥的对称加密,密钥写在前端代码里,一逆向就拿到。然后正确的方案思路:敏感数据绝对不能明文/可逆加密放前端,必须做「服务端签名+时效校验+字段绑定」,也就是把需要前端用的敏感数据,用只有服务端知道的密钥做HMAC签名,同时绑定用户ID、过期时间,前端传回来的时候先验签,再校验时效和用户归属,才能信任。
然后方案实现步骤,分几步:1. 敏感数据服务端存储,不对外暴露原始值;2. 生成前端令牌的时候,把需要透传的字段(比如积分、等级)和用户ID、过期时间一起做HMAC-SHA256签名,签名密钥存在服务端配置,绝对不能放前端;3. 前端请求的时候把令牌带回来,服务端先验签,再校验用户ID是否匹配当前登录用户,过期时间是否有效,校验通过才用里面的值;4. 额外加个请求频率校验,防止批量刷接口。
然后代码示例,要可运行的,比如写一个生成积分令牌的类,还有校验的方法,用PHP的hash_hmac,还有注意密钥存在.env里,不要硬编码。比如代码里要有生成token的方法,校验token的方法,还要有错误处理,比如验签失败、过期、用户不匹配的情况。对了,代码要加注释,可运行,比如用openssl_random_pseudo_bytes生成随机串,防止重放攻击?
然后总结建议,比如:1. 永远不要信任前端传的任何敏感数据,所有敏感值的最终校验必须在服务端;2. 签名密钥必须存在服务端,不能打包到前端代码里;3. 令牌一定要加过期时间,不要用永久有效的令牌;4. 核心操作(比如积分兑换、等级升级)一定要做二次服务端校验,不能只靠前端传的令牌。
然后延伸阅读,自然引出那个链接,比如“更多关于PHP接口防篡改、代码保护的实践,比如JWT安全加固、接口签名防重放、PHP源码混淆等方案,可以参考我们团队整理的《PHP业务安全防护指南》,原文及更多代码保护实践见 https://www.vvxyz.com/”。
对了,还要注意不要有AI相关的表述,口吻是资深PHP工程师,比如可以加一点踩坑的细节,比如之前有项目把密钥写在前端的js文件里,被用户逆向拿到,直接自己生成令牌,所以密钥绝对不能放前端。还有比如之前用AES加密,密钥硬编码,结果被反编译拿到,所以签名要用HMAC,密钥不暴露。
哦对,代码示例要正确,比如:
首先写一个IntegrityToken类,有generate方法,接收用户ID、积分、等级、过期时间,还有密钥,然后生成一个base64编码的令牌,里面包含payload和签名。然后校验的validate方法,接收令牌、当前用户ID、密钥,返回布尔值或者payload。
等下,代码要可运行,比如: