1.1. 如何有效的防止API重放攻击
基于 TOTP [Time-Based One-Time Password Algorithm]
表示基于时间戳算法的一次性密码。
1.1.1. 需要同步服务器时间
- 基于时间戳的 容错
- reqID 每个 tk 下只能使用一次 保证API无法重放
- 容错时间 要小于 ck 的过期时间
1.2. 包
spomky-labs/otphp
1.3. 简单例子
public function SignCheck($appid, $appSecret, $reqid, $tk, $sign)
{
$S = md5(base64_encode($appid . $appSecret . $reqid . $tk));
if ($sign === $S) {
return true;
}
return false;
}
public function ToTpAuth($appid, $appSecret, $reqid, $tk)
{
$cK = "Totp:channle_{$appid}:{$tk}:{$reqid}"; //设置失败 说明reqid使用过 防止重放
if (\RedisDB::setnx($cK, 1)) {
\RedisDB::expire($cK, 60); //设置过期时间
$appSecret = Base32::encodeUpper("{$appSecret}", "=");
$totp = TOTP::create($appSecret, 50, 'sha1', 12); //容错时间
return $totp->verify($tk,time());
}
return false;
}
$appid = $request->input('appid');
$reqid = $request->input('reqid');
$tk = $request->input('tk');
$sign = $request->input('sign');
if (empty($appid) || empty($reqid) || empty($tk) || empty($sign))
{
return Functions::getMessageBody("Err.", [], 222222);
}
$ChannleModel = new ChannleModel();
$Info = $ChannleModel->where('app_id', $appid)->where('status', 1)->first();
// TODO 查询 渠道信息 获取密钥 简单写 可以改成从redis 获取配置
if (empty($Info)) {
return Functions::getMessageBody("Err.", [], 333333);
}
if (!$this->SignCheck($appid, $Info->app_secret, $reqid, $tk, $sign)) {
return Functions::getMessageBody("Sign Err.", [], 333334);
}
if (!$this->ToTpAuth($appid, $Info->app_secret, $reqid, $tk)) {
return Functions::getMessageBody("Sign Err.", [], 333335);
}