【问题标题】:saving authentication token in cookie (Django Rest Framework + React)在 cookie 中保存身份验证令牌(Django Rest Framework + React)
【发布时间】:2018-12-11 02:26:01
【问题描述】:

所以,正如标题所说,我正在使用 Django Rest Framework,结合 React。

我使用令牌身份验证对用户进行身份验证。现在我面临一个问题。当我重新加载页面(例如按 F5 键)时,所有状态都消失了,在这种情况下我无法保存令牌,需要用户再次登录。

我曾考虑将令牌存储在 cookie 中,但这似乎不太安全。

还有其他类似的问题,但没有答案能真正解释这有多大的安全风险。我认为它相当高,因为拥有令牌似乎足以对后端进行身份验证。

所以,我的问题是:我认为将身份验证令牌存储在 cookie 中不安全的假设是否正确?

注意:我正在考虑切换到基于会话的身份验证,但我宁愿保护我的工作并保留令牌身份验证。

【问题讨论】:

  • 非常需要讨论。我很想知道是否还有其他人面临同样的问题?这个任务只有 3k 浏览量
  • @Rik Schoonbeek 这个身份验证流程有什么实用的代码示例吗?

标签: django security authentication cookies


【解决方案1】:

我忘记了我的旧答案仍然在互联网上引起了一些关注......而且我后来意识到它有一部分是错误的。

更新答案:

I thought about storing the token in a cookie, but that doesn't seem very safe.

如果操作正确,就足够安全了。是的,任何对系统有物理访问权限的人仍然可以读取 cookie。更多内容请参见下文。

让我们明确一点,绝对有必要在客户端(本例中为浏览器)存储一些数据,并通过 API 调用发送这些数据以对用户进行身份验证。我们称这个数据为“令牌”。

当您在 API 调用中发送此令牌时,任何对系统具有物理访问权限的人都可以查看它。此外,没有必要对其进行加密,因为...

  1. 您需要在发送前对其进行解密,这样才能使未加密的字符串可读。
  2. 您还需要将解密密钥存储在客户端,这让我们又回到了同样的问题。

有哪些选择?老实说,顺其自然吧。大多数网站(几乎)都是这样工作的。除非该网站包含一些非常敏感的信息。在这种情况下,是否可以查看基于时间的密码、基于硬件的密码、生物特征?这些方法大多将安全保管密钥的责任转嫁给用户。

您当然可以让它更安全。以下是一些提示:

  1. 在一段时间/不活动后从服务器中删除令牌。
  2. 根据随机请求更新令牌,并使之前的请求无效。
  3. 允许用户查看和删除活动会话。
  4. 将其绑定到用户 IP 或难以复制的东西。每次用户使用不同的 IP 登录时,都要求输入密码。

考虑到这一点,让我们谈谈存储令牌。

我们只需要确保其他网站、恶意脚本和软件无法访问存储的令牌。 (如上所述,用户始终可以读取令牌。)

您可以将其存储在cookieslocalStorage。两者都可以正常工作,但localStore 旨在存储更大的数据。 Cookies 最多可以存储 4096 字节的数据 - 这足以存储令牌。 Cookies 在处理 SSR(服务器端渲染)时也有帮助。不过,在 React 中处理 cookie 可能会很棘手。 (提示:试试 next.js,它内置了对 cookie 和 React 的 SSR 的支持。)

如果有帮助,您还可以在 cookie 中指定过期时间。

TL;DR: 使用Cookies 非常好。正确使用即可。

感谢@ShayanSalehian 指出这一点:LocalStorage is subject to XSS and cookie is subject to CSRF. So I think using cookies + CSRF is the most secure way even in TokenAuthentication for storing tokens on client...


旧答案(剧透警告,部分错误。):

这也是我必须处理的事情。在此过程中失眠了几个晚上。

免责声明:我不是任何安全专家。只是有点痴迷(阅读:偏执)。

短版(回答您的问题):我最终使用 window.localStorage 来存储令牌。虽然我不相信自己这是最好的做法,但这不仅仅是关于“存储”部分 - 阅读长版本以了解更多信息。

加长版: 首先,让我们澄清一些事情。 React 更像是一个移动应用程序,而不是网页/网站。我不是在谈论 React Native - 我的意思是 React.js

为什么我说它更像是一个移动应用而不是一个网站? 传统网站通常使用基于会话的身份验证,通常会为此准备好浏览器/服务器。显然,这是一项简单而无缝的任务。

在移动应用程序(或客户端独立应用程序)中,您需要维护某种令牌来基本上告诉服务器“嘿,是我!我前段时间访问过。这是我的身份证。让我进去,请?”。问题是,在客户端很难保证令牌的安全。 Android 本身直到 Android v4.3 才提供任何安全的方式来存储身份验证令牌。这也不够安全,所以他们不久前引入了硬件支持的密钥库。这就是为什么某些应用程序不能(并且仍然不能)与有根设备一起使用的原因。在此处阅读更多信息:https://stackoverflow.com/a/19669719/3341737

与 React/独立网络应用程序相比,Google(在某种程度上)控制着 Android 客户端。他们实现基于硬件的密钥库相对容易。对于网络应用程序,有大量的浏览器,有数百个版本等等。

回到 window.localStorage。与 Cookie 类似,localStorage 对每个域都是隔离的。由于它是一个较新的 API,因此它的设计方式比旧的 Cookie 更好。

加密密钥没有意义(尽管您可能会混淆它),因为您还需要将解密密钥存储在本地某处。因此,如果有人可以访问令牌,他们也可以访问解密密钥。

这个问题的第二个方面(以及为什么“存储”不是唯一的问题)是 - 你真的想保护谁的令牌?

  1. 中间的人?使用 SSL。
  2. 其他网站?他们无法访问您域的 localStorage。
  3. 某个人?真的。如果他们对 PC 有物理访问权限,他们就可以轻松获得令牌。拥有物理访问权限实际上使他们成为用户(您想保护令牌不被用户访问?)。考虑到此人具有物理访问权限,即使您以某种方式安全地存储令牌,您也无法保护令牌。

为什么不呢?因为您需要随每个请求发送令牌 - 随每个请求发送的数据在浏览器网络检查器中可用。因此,无论您将令牌存储在何处以及如何存储,它都可能被具有 PC 物理访问权限的人窃取。

为什么不使用 Cookie?两个原因(实际上是 1 个):

  1. 默认情况下,每个请求都会发送 Cookie。你真的不需要这个,因为你只需要在 API 调用期间发送令牌(而不是页面加载)。此外,由于您使用的是 DRF(适用于任何 RESTful API 后端),您可能需要将令牌发送到服务器的特定方式 - 任何方式都需要自定义方法。
  2. window.localStorage 是一个简洁的小 API(只需 window.localStorage.setItem('key', 'value'))。此外,与 Cookie 相比,最大大小限制要高得多。

因此,window.localStorage 对我来说似乎是一个可行的选择。如果您有更好的解决方案,请启发我。

话虽如此,但这并不意味着您无法提高安全性。以下是一些建议:

  1. 在一段时间或一段时间不活动后,使令牌从数据库中失效/删除。
  2. 随请求随机发送更新的令牌(也使之前的令牌无效),并用新的令牌替换存储的令牌。
  3. 授予用户删除其帐户中保存的令牌以及每个令牌的最后活动的权限。
  4. 如果您真的(真的)担心,请将令牌与用户 IP(或其他无法克隆到另一个系统的东西)绑定。如果用户从新设备/位置/浏览器登录,请使用 2FA。

【讨论】:

  • 您能否简要说明使用localStoragesessionStorage 之间的区别?
  • sessionStorage 在这种情况下不相关,这就是为什么没有提到它。关闭选项卡/浏览器时会自动删除 sessionStorage。
  • localStorage 可通过 javascript 访问(与 HttpOnly cookie 相反),使其容易受到 XSS 攻击。
  • @ShayanSalehian 有趣。您对此有何看法?
  • @TheKalpit LocalStorage 受 XSS 约束,cookie 受 CSRF 约束。所以我认为使用 cookie + CSRF 是最安全的方式,即使在 TokenAuthentication 中用于在客户端存储令牌。但是,我看过的 DRF 和其他流行的库并不很好地支持这一点。我最终编写了一个自定义的 DRF TokenAuthentication,以返回 set-cookie 响应标头并在 HttpOnly cookie 中设置 auth_token。此外,我确保所有发布请求都正确发送和返回 CSRF(尤其是对于 LoginView,因为 DRF 只检查 CSRF 以查找已登录的用户)。
【解决方案2】:

下一个方法是什么:不是存储令牌而是存储 refresh_token 就像在 OAuth https://auth0.com/docs/tokens/refresh-token/current 中一样?

您应该决定为您的安全需要保留哪些超时,因此每次令牌失效或为空后,您都可以尝试将会话恢复为存储在 cookie 中的refresh_token

即使潜在的破解者会在 const 超时后获得 refresh_token 也没有意义

【讨论】:

    猜你喜欢
    • 2019-01-18
    • 2021-11-06
    • 2021-05-22
    • 2015-12-15
    • 2020-09-12
    • 2018-03-25
    • 2019-05-18
    • 2016-10-04
    • 2016-10-20
    相关资源
    最近更新 更多