Django 签名 cookie 会话存储、重放攻击和 SESSION_COOKIE_AGE

Kye*_*e R 5 django cookies session

根据Django 文档,签名 cookie 会话存储容易受到重放攻击:

另请注意,虽然 MAC 可以保证数据的真实性(它是由您的站点生成的,而不是其他人生成的)和数据的完整性(它都在那里并且是正确的),但它不能保证新鲜度,即你被送回你发送给客户的最后一件事。这意味着对于会话数据的某些用途,cookie 后端可能会让您遭受重放攻击。与其他会话后端保持每个会话的服务器端记录并在用户注销时使其失效不同,基于 cookie 的会话在用户注销时不会失效。因此,如果攻击者窃取了用户的 cookie,即使用户退出,他们也可以使用该 cookie 以该用户的身份登录。如果 Cookie 比您的 SESSION_COOKIE_AGE 更旧,则它们只会被检测为“过时”。

这是否意味着:

  1. 我们依靠 cookie 的客户端过期来确保会话数据被销毁(因此,如果在浏览器删除 cookie 之前捕获 cookie 内容,则仍有可能进行重放攻击),或
  2. 服务器检测SESSION_COOKIE_AGE数据的陈旧性(与它认为陈旧的数据进行比较并明确拒绝数据。

从技术上讲,Django 似乎可以在不保留此数据服务器端的情况下确定会话的“旧”程度,但是文档似乎不清楚这是否正在完成,或者 Django 是否依赖/信任用户的浏览器杀死旧的 cookie(因此如果数据在到期之前被捕获,会话仍然可以重播)。

Lou*_*uis 6

Django 通过SessionMiddleware确实将HTTP cookie 过期设置为SESSION_COOKIE_AGE。这会告诉浏览器 cookie 何时应该被视为“太旧”和过期。这是很好的家政服务。此外,如果浏览器检测到不应再使用 cookie,它也不会使用它,这样 Django 就不必花时间检查无论如何都不能使用的 cookie 。

然而,Django 并不依赖浏览器来确保过期的 cookie 不会被再次使用。流氓代理可以获取 cookie 的副本,然后在过期日期后重新使用它。Django 通过将 cookie 发送到浏览器来防止这种情况,格式如下:

<payload>:<creation stamp>:<signature>
Run Code Online (Sandbox Code Playgroud)

冒号是按字面出现在数据中的分隔符。<payload>是实际的会话数据。<creation stamp>是创建 cookie 时添加的时间戳,并且<signature>是对有效负载和创建戳进行签名的 MAC 签名。

您可以在django.core.signing. 文件顶部的注释为您提供了使用Signer该类的基本签名方案。这个类不知道时间戳。但是,在签署 cookie 时,Django 使用TimestampSigner,它知道时间戳并生成我上面显示的格式。

TimestampSigner 取消对 cookie 的签名时,它会检查时间戳并在它太旧时引发异常:

def unsign(self, value, max_age=None):
    """
    Retrieve original value and check it wasn't signed more
    than max_age seconds ago.
    """
    result = super().unsign(value)
    value, timestamp = result.rsplit(self.sep, 1)
    timestamp = baseconv.base62.decode(timestamp)
    if max_age is not None:
        if isinstance(max_age, datetime.timedelta):
            max_age = max_age.total_seconds()
        # Check timestamp is not older than max_age
        age = time.time() - timestamp
        if age > max_age:
            raise SignatureExpired(
                'Signature age %s > %s seconds' % (age, max_age))
    return value
Run Code Online (Sandbox Code Playgroud)