我有一个使用 OAuth 登录系统(Google、FB 等)的网站
www.example.com
当用户点击链接时,OAuth 系统会重定向到
login.example.com
并进行验证,然后重定向回
www.example.com
。
我正在使用基于
state
键中传递的值的请求验证。因此,当用户访问 OAuth 时,我会将
state
设置为某个随机值,当 OAuth 重定向返回我的网站时,我会检查
state
变量是否为有效请求。
不过,为了验证请求,我想使用 SESSION 存储来保存我这边的
state
。问题是,当我这样做时,子域上的 SESSION 变量是空的。
我的 SESSION 设置如下:
session_name('ID');
ini_set('session.use_cookies', 1);
ini_set('session.cookie_domain', ".example.com");
ini_set('session.cookie_samesite','Lax');
我曾尝试设置
None
代替
Lax
,但结果仍然是空的(另外,我不喜欢使用
None
进行设置)。如何解决这个问题?
除了 SESSION,还有其他验证方法吗?我不想使用任何外部库(Redis、memcache 等)。如果使用 DB,我就必须识别用户,而 IP 是不够的。
遇到的问题是跨子域共享 PHP session 的常见问题。虽然已正确设置
session.cookie_domain
来包含根域
.example.com
,但浏览器在重定向过程中可能不会发送 cookie,因为
session.cookie_samesite
设置为
Lax
。
以下是解决此问题并改进身份验证方法的一些建议:
1. 使用更安全的 SameSite 设置:
-
将
session.cookie_samesite
设置为None
: 这是最可靠的跨域 cookie 共享方法,但正如所提到的,它会带来安全风险。如果必须使用None
,请确保的网站和 OAuth 提供商都使用 HTTPS。 -
使用更细粒度的 SameSite 控制: 可以根据请求来源动态设置
session.cookie_samesite
。例如,当重定向到 OAuth 提供商时,将其设置为None
,并在其他情况下将其设置为Lax
。
2. 使用替代的存储机制:
-
数据库: 可以使用数据库表来存储
state
值及其关联的用户标识符。这提供了比 session 更持久和安全的存储机制。 -
临时文件: 可以将
state
值存储在服务器上的临时文件中,并使用唯一标识符将其与用户关联。但是,这种方法不如数据库安全和可扩展。
3. 改进身份验证流程:
-
使用 PKCE(Proof Key for Code Exchange): PKCE 是 OAuth 2.0 的扩展,可以防止授权代码拦截攻击。它通过在授权请求中包含一个代码验证器来实现这一点,该验证器用于在令牌请求期间验证客户端身份。
-
使用 JWT(JSON Web Token): 可以使用 JWT 来存储
state
值和其他相关信息。JWT 可以由的服务器签名,以防止篡改。
以下是一些代码示例,说明如何使用数据库来存储
state
值:
1. 生成并存储 state 值:
// 生成随机的 state 值
$state = bin2hex(random_bytes(32));
// 将 state 值存储到数据库
// ... 使用选择的数据库库执行插入操作 ...
$userId = getCurrentUserId(); // 获取当前用户的 ID
insertIntoDatabase('state_table', ['state' => $state, 'user_id' => $userId]);
// 将 state 值传递给 OAuth 提供商
$oauthUrl = 'https://oauth.provider.com/authorize?state=' . $state;
header('Location: ' . $oauthUrl);
2. 验证 state 值并检索用户信息:
// 从 OAuth 提供商获取 state 值
$state = $_GET['state'];
// 从数据库中检索 state 值
// ... 使用选择的数据库库执行查询操作 ...
$result = selectFromDatabase('state_table', ['state' => $state]);
if ($result) {
// 验证成功
$userId = $result['user_id'];
// ... 使用用户 ID 登录用户 ...
// 从数据库中删除 state 值
// ... 使用选择的数据库库执行删除操作 ...
deleteFromDatabase('state_table', ['state' => $state]);
} else {
// 无效的 state 值
// ... 处理错误 ...
}
请记住,安全性是一个持续的过程。请务必及时了解最新的安全最佳实践,并定期审查和更新的身份验证机制。
标签:php,session,oauth-2.0,subdomain From: 78573980