我一直在寻找防止会话劫持的方法,即有人窃取会话 cookie 并使用它来访问系统。
诸如http://codebutler.com/firesheep之类的程序可以很容易地嗅探开放无线网络上的会话,而其他获取会话的方式包括跨站点脚本攻击,或者只是将它们从受害者的计算机中取出。
使用 SSL 保护所有会话 cookie/服务器通信对于防止 Firesheep 嗅探至关重要,并且在 cookie 上设置 HTTPOnly 有助于防止 JavaScript 在 XSS 攻击中读取会话 cookie,但它仍然容易受到基于 AJAX 的攻击。
另一层是在每次请求时更新的会话 cookie 中包含安全令牌或随机数。您将令牌存储在服务器端数据存储区和 cookie 中,并在每个请求中比较 cookie 中的令牌与数据存储区中的令牌匹配。
如果令牌不匹配,则可能表明有人窃取了会话并试图使用它,因此您可以忽略请求或使会话无效并要求用户重新进行身份验证。但是,不匹配的令牌也可能是由于连接缓慢/不稳定造成的。
例如,您可能遇到这样的情况:服务器接收到来自真实用户的请求,更新服务器数据存储中的会话令牌,并使用包含更新令牌的会话 cookie 响应用户。但是由于连接缓慢/不稳定,用户没有收到响应,因此用户仍然拥有旧的会话令牌,而新的会话令牌存储在服务器上。当用户重试请求时,令牌将不匹配。
缓解此问题的一种方法是让服务器保留最后几个令牌的历史记录并检查它们是否匹配,但随后会变成要保留多少令牌的情况,具体取决于连接的脆弱程度或用户的点击满意度如何,服务器可能会在连接恢复之前循环浏览历史记录,并且浏览器会更新用户的会话。
保留令牌历史记录的另一种方法是为每个会话添加时间戳,并检查时间戳是否在某个短的指定范围内,例如 30 秒。如果用户的会话 cookie 时间戳在服务器存储的会话时间戳的 30 秒内,则认为该会话是真实的。
示例伪代码
def authenticate_request():
if (stored_session.timestamp - session.timestamp > 30 seconds):
return False
return True
这避免了保留令牌历史记录——时间戳成为令牌——但攻击者在会话被盗后有 30 秒的机会劫持会话。虽然这是真的,但令牌历史替代方案并没有更好,因为它为攻击者提供了一个可能更长的机会窗口。
检查 IP 地址和用户代理更改的其他方法也存在问题。用户代理很容易被欺骗,如果攻击者能够获取用户的会话,他们可以通过相同的 XSS 代码或其他方式轻松确定用户代理。
如果用户在移动设备上,他们的 IP 地址可能会频繁更改,从而导致许多误报。此外,攻击者可能位于同一公司防火墙之后,因此用户和攻击者的 IP 与外部 Web 服务器相同。
使用时间戳令牌是正确的方法还是有更好的方法?30 秒的缓冲区是否正确?我错过了哪些边缘情况?