You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP带“记住我”功能的安全登录API实现安全问题咨询

解决Token跨设备窃取的实用方案

针对你遇到的Cookie Token被他人窃取后跨设备登录的问题,以下是几个可落地的PHP后端解决方案:

1. 绑定设备特征+IP段(而非完整IP)

不要直接绑定完整IP,而是结合用户代理(User-Agent)哈希和IP前两段(IPv4)/IP前缀(IPv6)生成设备标识,和Token一起存入数据库。验证时不仅校验Token有效性,还要比对当前请求的设备标识与数据库记录是否一致:

// 生成Token时创建设备标识
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
// 提取IP前两段(适配IPv4,IPv6可取前32位前缀)
$ipSegment = implode('.', array_slice(explode('.', $ip), 0, 2));
$deviceFingerprint = hash('sha256', $userAgent . $ipSegment);

// 将Token与设备标识关联存入数据库
$stmt = $pdo->prepare("INSERT INTO user_tokens (user_id, token, device_fingerprint, expires_at) VALUES (?, ?, ?, ?)");
$stmt->execute([$userId, $token, $deviceFingerprint, date('Y-m-d H:i:s', time() + 7*86400)]);

// 验证Token时校验设备标识
$stmt = $pdo->prepare("SELECT * FROM user_tokens WHERE token = ? AND expires_at > NOW()");
$stmt->execute([$token]);
$tokenRecord = $stmt->fetch(PDO::FETCH_ASSOC);

if ($tokenRecord) {
    $currentUserAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $currentIp = $_SERVER['REMOTE_ADDR'] ?? '';
    $currentIpSegment = implode('.', array_slice(explode('.', $currentIp), 0, 2));
    $currentFingerprint = hash('sha256', $currentUserAgent . $currentIpSegment);
    
    if ($currentFingerprint !== $tokenRecord['device_fingerprint']) {
        http_response_code(401);
        exit('设备验证失败,请重新登录');
    }
}

这种方式既避免了动态IP的问题,又大幅提高了跨设备复用Token的门槛。

2. 强化Cookie安全属性

给Token Cookie设置HttpOnly、Secure、SameSite属性,从传输和读取层面减少窃取风险:

setcookie('auth_token', $token, [
    'expires' => time() + 7*86400,
    'path' => '/',
    'domain' => 'your-domain.com',
    'secure' => true, // 仅在HTTPS连接下传输
    'httponly' => true, // 禁止前端JS读取Cookie,防范XSS窃取
    'samesite' => 'Strict' // 严格限制跨站请求携带Cookie,降低CSRF和跨设备复用可能
]);

3. 实现Token定期刷新+单Token失效机制

给Token设置较短的有效期(比如7天),每次用户产生活跃行为(如访问页面、提交操作)时,后端生成新Token,替换数据库中的旧Token并更新Cookie。这样即使Token被窃取,有效期内用户一旦活跃,旧Token就会失效,窃取者无法继续使用。

4. 双Token(Access Token + Refresh Token)架构

  • Access Token:有效期短(15-30分钟),用于接口鉴权,可存在内存或临时Cookie中;
  • Refresh Token:有效期长(7-30天),存在HttpOnly Cookie中,仅用于过期后获取新的Access Token和Refresh Token。
  • 关键规则:Refresh Token只能使用一次,每次刷新后旧Refresh Token立即失效,生成新的存入数据库。

示例代码(刷新Token逻辑):

// 接收Refresh Token
$refreshToken = $_COOKIE['refresh_token'] ?? '';

// 验证Refresh Token
$stmt = $pdo->prepare("SELECT * FROM user_refresh_tokens WHERE token = ? AND expires_at > NOW()");
$stmt->execute([$refreshToken]);
$refreshRecord = $stmt->fetch(PDO::FETCH_ASSOC);

if ($refreshRecord) {
    // 生成新的Access Token和Refresh Token
    $newAccessToken = bin2hex(random_bytes(32));
    $newRefreshToken = bin2hex(random_bytes(64));
    
    // 失效旧Refresh Token
    $stmt = $pdo->prepare("UPDATE user_refresh_tokens SET expires_at = NOW() WHERE id = ?");
    $stmt->execute([$refreshRecord['id']]);
    
    // 存入新Refresh Token
    $stmt = $pdo->prepare("INSERT INTO user_refresh_tokens (user_id, token, expires_at) VALUES (?, ?, ?)");
    $stmt->execute([$refreshRecord['user_id'], $newRefreshToken, date('Y-m-d H:i:s', time() + 7*86400)]);
    
    // 返回新Access Token,更新Refresh Token Cookie
    setcookie('refresh_token', $newRefreshToken, [
        'expires' => time() + 7*86400,
        'path' => '/',
        'domain' => 'your-domain.com',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Strict'
    ]);
    
    echo json_encode(['access_token' => $newAccessToken]);
} else {
    http_response_code(401);
    exit('请重新登录');
}

5. 异常登录提醒+主动下线

当Token验证发现设备标识或IP段差异较大时,后端触发邮件/短信提醒用户,并提供主动下线所有异常设备的功能(清空该用户所有有效Token)。


内容的提问来源于stack exchange,提问作者Shivam Shukla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:17:22