跳到内容

Cookie窃取缓解备忘单

简介

随着2FA和通行密钥的普及,登录过程变得更加健壮,即使攻击者只窃取了密码,也难以进行欺骗攻击。

然而,如果攻击者能够窃取有效的会话Cookie,则有可能在会话生命周期内劫持用户会话。换句话说,窃取会话Cookie的影响与窃取认证凭证直到其过期相同。无论您的认证过程多么健壮,它都不会是Cookie窃取的充分对策。

通常,Cookie窃取是通过恶意软件或网络钓鱼攻击直接针对用户进行的。因此,服务唯一能做的就是尽快检测到被盗Cookie的使用。

会话Cookie在用户登录时授予。如果这些Cookie被攻击者窃取并用于从攻击者的设备劫持会话,则用于会话连接的某些环境信息将会改变。

例如,如果攻击者从另一个国家使用被盗的Cookie,您可以通过检测IP地址的显著变化来发现这一点。

通过这种方式,有多种向量可用于检测用户环境是否已更改。

  • 来自不同区域的访问 (IP地址)
  • 来自不同设备的访问 (User-Agent)
  • 来自不同语言设置的访问 (Accept-Language)
  • 在不同时间点的访问 (日期)

如果在建立会话时保存此信息并在每个请求中进行比较,则可以检测用户环境是否已更改。

当然,仅凭简单比较难以做出判断。例如,如果用户更改了他们连接的Wi-Fi网络,他们的IP地址就会改变。如果用户更新了他们的浏览器,User-Agent也会改变。因此,不仅需要比较值,还需要检查这些值的含义是否没有发生显著变化。

假阴性/假阳性

假设一个在某个国家被授予访问权限的会话Cookie被从另一个国家使用。这可能是一次攻击,也可能仅仅是用户旅行了。

换句话说,仅仅因为IP地理位置发生了变化,并不能确定地说这是一次攻击。这意味着这种检测方法存在**假阳性**(看似是攻击,但实际不是)的情况。

同时,即使IP地理位置没有变化,攻击者也有可能从同一国家内部进行攻击。这意味着这种检测方法存在**假阴性**(看似不是攻击,但实际是)的情况。

通过在会话建立时在服务器端存储会话信息,当该信息发生显著变化时,可以检测到会话劫持。

以下是应该保存的核心信息。

  • 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窃取攻击。

参考资料