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

基于Google API PHP Client的OAuth2登录流程安全优化咨询

Google OAuth2登录流程安全问题修复指导

核心问题定位

你的代码存在致命逻辑错误,直接导致了审计指出的token窃取风险,同时Cookie配置也缺乏必要的安全防护:

  1. 错误将授权码(code)当作ID Token处理:OAuth2流程中,$_GET['code']是授权码,而非ID Token。你直接用verifyIdToken()验证授权码,还将其存入Cookie——攻击者拿到这个code后,可以直接用它向Google Token端点交换真实的用户ID Token和Access Token,完全接管用户账号。
  2. Cookie无安全属性:未设置HttpOnly、Secure、SameSite属性,极易被XSS攻击窃取,或引发CSRF风险。
  3. 错误使用301永久重定向:登录状态是临时的,用301会让浏览器缓存重定向规则,导致后续验证失败后无法正常跳转。

修复后的完整流程

正确的OAuth2授权流程应该是:
用户访问受保护页面 → 检查会话有效性 → 无效则跳转Google授权页(带state参数防CSRF) → 用户授权后,用返回的code向Google交换ID Token/Access Token → 验证ID Token有效性 → 生成自有会话(或安全存储ID Token) → 后续请求用会话/安全Cookie验证身份

1. 受保护页面代码(例如dashboard.php)

<?php
session_start();
define('COOKIENAME', 'user_session');

// 登出逻辑
if(isset($_POST['Logout'])){
    setcookie(COOKIENAME, "", time() - 3600, "/", "", true, true);
    session_destroy();
    header("Location: /login.php", true, 302);
    exit;
}

try {
    // 优先检查自有会话(推荐方案)
    if (!isset($_COOKIE[COOKIENAME]) || !isset($_SESSION['user_id'])) {
        header("Location: /login.php", true, 302);
        exit;
    }

    // 若坚持用ID Token验证,取消下方注释
    // require_once 'vendor/autoload.php';
    // $client = new Google_Client();
    // $client->setAuthConfig('./clientInfo.json');
    // $token = $client->verifyIdToken($_COOKIE[COOKIENAME]);
    // if (!$token) {
    //     header("Location: /login.php", true, 302);
    //     exit;
    // }
} catch (Exception $e) {
    print "An error occurred: " . $e->getMessage();
    exit;
}
?>
<!-- 页面内容 -->

2. 登录/回调页面代码(login.php)

<?php
session_start();
define('COOKIENAME', 'user_session');
require_once 'vendor/autoload.php';

$client = new Google_Client();
$client->setAuthConfig('./clientInfo.json');
// 写死redirect_uri,必须和Google控制台配置完全一致,禁止动态拼接
$client->setRedirectUri('https://your-domain.com/login.php');
$client->setAccessType('offline');
// 只请求必要权限
$client->addScope('openid');
$client->addScope('email');

// 处理授权回调
if (isset($_GET['code'])) {
    // 验证state参数,防止CSRF
    if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['oauth_state']) {
        echo "Invalid request (CSRF detected)";
        exit;
    }

    try {
        // 用授权码交换Token
        $token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
        // 验证ID Token
        $id_token = $client->verifyIdToken($token['id_token']);
        if (!$id_token) {
            echo "Invalid ID Token";
            exit;
        }

        // 方案1:生成自有会话(推荐),服务器端存储用户信息
        $_SESSION['user_id'] = $id_token['sub'];
        $_SESSION['user_email'] = $id_token['email'];
        // 强化SESSION Cookie安全属性
        setcookie(session_name(), session_id(), time() + 3600*24, "/", "", true, true);

        // 方案2:若必须存ID Token到Cookie,添加安全属性
        // setcookie(COOKIENAME, $token['id_token'], $id_token['exp'], "/", "", true, true);

        header("Location: /dashboard.php", true, 302);
        exit;
    } catch (Exception $e) {
        print "An error occurred: " . $e->getMessage();
        exit;
    }
} else {
    // 生成state参数防CSRF
    $state = bin2hex(random_bytes(16));
    $_SESSION['oauth_state'] = $state;
    // 跳转Google授权页
    $auth_url = $client->createAuthUrl(['state' => $state]);
    header("Location: " . $auth_url, true, 302);
    exit;
}
?>

关键安全强化措施

  • 强制Cookie安全属性:
    • HttpOnly:禁止JavaScript读取Cookie,防范XSS窃取
    • Secure:仅在HTTPS下传输Cookie
    • SameSite=Strict:防止CSRF攻击
  • 使用自有会话替代直接存储ID Token:服务器端存储用户身份信息,客户端仅存会话ID,便于服务器端主动失效会话,降低token泄露风险
  • 添加CSRF防护:授权请求时生成随机state参数并存在Session,回调时验证该参数
  • 固定redirect_uri:禁止动态拼接,必须与Google Cloud控制台中配置的授权回调地址完全一致
  • 限制权限范围:仅请求必要的OAuth2权限(如openid、email),最小化泄露影响

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:25:21