Cookie窃取缓解备忘单¶
简介¶
随着2FA和通行密钥的普及,登录过程变得更加健壮,即使攻击者只窃取了密码,也难以进行欺骗攻击。
然而,如果攻击者能够窃取有效的会话Cookie,则有可能在会话生命周期内劫持用户会话。换句话说,窃取会话Cookie的影响与窃取认证凭证直到其过期相同。无论您的认证过程多么健壮,它都不会是Cookie窃取的充分对策。
通常,Cookie窃取是通过恶意软件或网络钓鱼攻击直接针对用户进行的。因此,服务唯一能做的就是尽快检测到被盗Cookie的使用。
Cookie窃取缓解¶
会话Cookie在用户登录时授予。如果这些Cookie被攻击者窃取并用于从攻击者的设备劫持会话,则用于会话连接的某些环境信息将会改变。
例如,如果攻击者从另一个国家使用被盗的Cookie,您可以通过检测IP地址的显著变化来发现这一点。
通过这种方式,有多种向量可用于检测用户环境是否已更改。
- 来自不同区域的访问 (IP地址)
- 来自不同设备的访问 (User-Agent)
- 来自不同语言设置的访问 (Accept-Language)
- 在不同时间点的访问 (日期)
如果在建立会话时保存此信息并在每个请求中进行比较,则可以检测用户环境是否已更改。
当然,仅凭简单比较难以做出判断。例如,如果用户更改了他们连接的Wi-Fi网络,他们的IP地址就会改变。如果用户更新了他们的浏览器,User-Agent也会改变。因此,不仅需要比较值,还需要检查这些值的含义是否没有发生显著变化。
假阴性/假阳性¶
假设一个在某个国家被授予访问权限的会话Cookie被从另一个国家使用。这可能是一次攻击,也可能仅仅是用户旅行了。
换句话说,仅仅因为IP地理位置发生了变化,并不能确定地说这是一次攻击。这意味着这种检测方法存在**假阳性**(看似是攻击,但实际不是)的情况。
同时,即使IP地理位置没有变化,攻击者也有可能从同一国家内部进行攻击。这意味着这种检测方法存在**假阴性**(看似不是攻击,但实际是)的情况。
Cookie窃取检测¶
通过在会话建立时在服务器端存储会话信息,当该信息发生显著变化时,可以检测到会话劫持。
以下是应该保存的核心信息。
- IP地址
- User-Agent
- Accept-Language
- 日期
此外,以下根据设备和操作系统可能发生变化的标头,也作为有效的监控目标。
- Accept
- Accept-Encoding
此外,最近的浏览器发送名为`Sec-Fetch-*`的请求头,提供有关浏览上下文的信息,因此这些值也可以用作参考。它并非所有浏览器都发送,即使浏览器支持也并非总是发送,因此不应完全依赖。
- sec-ch-prefers-color-scheme
- sec-ch-ua
- sec-ch-ua-arch
- sec-ch-ua-bitness
- sec-ch-ua-form-factors
- sec-ch-ua-full-version
- sec-ch-ua-full-version-list
- sec-ch-ua-mobile
- sec-ch-ua-model
- sec-ch-ua-platform
- sec-ch-ua-platform-version
- sec-ch-ua-wow64
当在服务器上建立会话时,这些信息会像下面这样被收集并与会话关联保存。
const session = SessionStorage.create()
session.save({
ip: req.clientIP,
user_agent: req.headers.userAgent,
date: req.headers.date,
accept_language: req.headers.acceptLanguage,
// ...
})
如果在每次接收到请求时比较这些信息,检测到较大的变化,则会话可能已被劫持。
会话验证¶
如果会话有可能被劫持,最可靠的验证方法是重新认证。如果您暂时使用户的会话失效,要求他们再次认证,然后给他们一个新的会话Cookie,攻击者将无法再利用被盗的Cookie进行任何操作。
然而,如前所述,监控会话存在假阳性的可能性,因此如果需要频繁地重新认证,将会给用户带来不佳的体验。
另一种方法是使用验证码(CAPTCHA)或类似机制来做出判断。当被盗的会话Cookie被机器人或其他恶意程序使用时,这种方法尤其有用。
作为一种折衷方案,如果怀疑会话劫持,良好的做法是在正常浏览时显示验证码,并在访问机密信息或执行具有副作用的操作之前,使用重新认证提供可靠保护。
function cookieTheftDetectionMiddleware(req, res) {
const currentIP = req.clientIP
const expectedIP = req.session.ip
if (checkGeoIPRange(currentIP, expected) === false) {
// Validation
}
const currentUA = req.userAgent
const expectedUA = req.session.ua
if (checkUserAgent(currentUA, expectedUA)) {
// Validation
}
// ...
}
app.post("/users/delete", cookieTheftDetectionMiddleware, (req, res) => {
// ...
})
通常,此类功能以中间件的形式提供,或者由安装在Web服务器前的WAF(Web应用防火墙)提供。
如果这种比较对性能有显著影响,可以对其进行调整,为每个路径设置优先级,并仅对查看或修改重要信息的端点进行密集检查。
设备绑定会话凭证¶
导致Cookie窃取攻击的根本问题是,会话Cookie在不检查发送者是谁的情况下被接受。服务器只检查这些值是否有效。这些值通常被称为“不记名令牌”(Bearer Token)。
为了解决这个问题,将会话Cookie本身设为“发送者受限令牌”(Sender Constrained Token)是有效的。为此目的,提出了设备绑定会话凭证API。
这个API将公钥加密与会话Cookie的所有者验证相结合。通过使用浏览器内部生成的私钥验证所有者,即使攻击者成功窃取了会话Cookie,除非他们也窃取了浏览器内部秘密保存的私钥,否则他们将无法冒充用户。
此规范仍在起草阶段,但据认为未来它将能够解决Cookie窃取攻击。