讨论数量:
AES 加密 肯定是没有加上时间戳的校验
// 生成签名
protected function generateUserApplySign($headers, $secretKey): string
{
// 移除 Api-Sign 字段
if (isset($headers['Api-Sign'])) {
unset($headers['Api-Sign']);
}
ksort($headers);
$buff = '';
foreach ($headers as $k => $v) {
if ($v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
// 去除最后一个&
$string = trim($buff, "&");
// 在string后加入KEY
$string = $string . "&key=" . $secretKey;
// sha256加密
$string = hash_hmac('SHA256', $string, $secretKey);
// 所有字符转为大写
return strtoupper($string);
}
/**
* 签名验证
* @param Request $request
* @return bool
* @throws InvalidRequestException
*/
protected function verifyUserApplySign(Request $request): bool
{
$requiredHeaders = ['Api-Appid', 'Api-Nonce-Str', 'Api-Timestamp', 'Api-Sign'];
foreach ($requiredHeaders as $header) {
if (!$request->headers->has($header)) {
throw new InvalidRequestException($header . ' 不能为空');
}
}
// 判断 Api-Appid 是否正确
$userApplySecret = UserApplySecret::query()->where('app_id', $request->headers->get('Api-Appid'))->first();
if (!$userApplySecret) {
throw new InvalidRequestException('Api-Appid 不存在');
}
if (!$userApplySecret['is_valid']) {
throw new InvalidRequestException('Api-Appid 已失效');
}
// 判断是否过期
if (Carbon::now()->gt($userApplySecret['valid_at'])) {
throw new InvalidRequestException('Api-Appid 已过期');
}
// 判断请求次数是否超限
if ($userApplySecret['request_count'] >= $userApplySecret['available_count']) {
throw new InvalidRequestException('Api-Appid 请求次数已超限');
}
$headers = [
'Api-Appid' => $request->headers->get('Api-Appid'),
'Api-Nonce-Str' => $request->headers->get('Api-Nonce-Str'),
'Api-Timestamp' => $request->headers->get('Api-Timestamp'),
'Api-Sign' => $request->headers->get('Api-Sign'),
];
if ($userApplySecret['time_out'] && !$this->allowTimestamp($headers['Api-Timestamp'], $userApplySecret['time_out'])) {
throw new InvalidRequestException('Api-Timestamp 超时');
}
// 生成签名
$sign = $this->generateUserApplySign($headers, $userApplySecret['secret_key']);
Log::info('Api-User-Sign: ', ['generateUserApplySign' => $sign, 'Api-User-Sign' => $headers['Api-Sign']]);
if (!hash_equals($sign, $headers['Api-Sign'])) {
throw new InvalidRequestException('Api-Sign 不正确');
}
// 请求成功, 记录请求次数
$userApplySecret->increment('request_count');
return true;
}
兼容性补救措施:
1、自动封IP,从请求信息里面多分析攻击者特征,有相同特征的都封掉,一般的攻击者,IP资源有限的。
2、同一账号限制登录频率。
3、将库里的简单密码用户,全部清理掉密码,后续登录让他们走密码修改流程。(和产品协商)
要保证以前的可用,先按照IP段代码处理返回500,比如 192.168.1.1 直接就把 192.168.1.* 全部代码处理返回500,再加一个IP日志记录,记录IP的访问次数,有些攻击ip动态也不一定会很多
先后端限制账号登录错误次数和间隔时间,然后逐步升级APP至有验证的新版本。
要设计设备指纹算法,每台设备有自己的指纹,一台设备不能注册太多账号,也可以使用公有云提供(比如https://cloud.tencent.com/product/tds)的方法。
推荐文章: