JWT与cookie基于令牌的身份验证对比

235

我读了一些关于"JWT vs Cookie"的文章,但它们只让我更加困惑...

  1. 我需要澄清一下,当人们谈论“基于令牌的身份验证与cookie”时,这里提到的cookie仅仅是指会话cookie吗? 我的理解是cookie就像介质,它可以用于实现基于令牌的身份验证(在客户端存储能够标识已登录用户的信息)或基于会话的身份验证(在客户端存储一个常量,该常量与服务器端的会话信息匹配)。

  2. 我们为什么需要JSON Web Token? 我以前使用标准cookie来实现基于令牌的身份验证(不使用会话ID,不使用服务器内存或文件存储):Set-Cookie: user=innocent; preferred-color=azure,我观察到的唯一区别是JWT包含负载和签名...而对于HTTP头,您可以选择签名或明文 cookie。 在我看来,签名cookie (cookie:'time=s%3A1464743488946.WvSJxbCspOG3aiGi4zCMMR9yBdvS%2B6Ob2f3OG6%2FYCJM') 更加节省空间,唯一的缺点是客户端无法读取令牌,只有服务器可以...但我认为这没问题,因为就像在JWT中的声明是可选的一样,令牌不必具有意义。


嗨,我注意到你7年前问了这个问题。我有一个类似的问题https://stackoverflow.com/questions/77444165/confusion-about-cookie-authentication-vs-token-authentication-what-is-the-diffe,只是不是特别关于JWT,但我对会话控制感到很困惑。也许你可以看一下。我想知道这些年来你是否有更多的见解。 - undefined
6个回答

279
令牌和cookie之间最大的区别在于,浏览器会自动发送cookie,而需要显式添加bearer令牌到HTTP请求中。
这个特性使得cookie成为安全网站的好方式,其中用户使用链接登录并在页面之间导航。
浏览器自动发送cookie也有一个巨大的缺点,即跨站请求伪造(CSRF)攻击。 在CSRF攻击中,恶意网站利用您的浏览器将身份验证cookie自动附加到该域的请求上的事实,并欺骗您的浏览器执行请求的情况。
假设https://www.example.com网站允许经过身份验证的用户通过将新密码POSThttps://www.example.com/changepassword来更改密码,而不需要发布用户名或旧密码。
如果您在访问恶意网站时仍然登录到该网站,并在您的浏览器中加载触发POST到该地址的页面,则您的浏览器将忠实地附加身份验证cookie,从而允许攻击者更改您的密码。
Cookie也可以用于保护Web服务,但现在大多数情况下使用令牌。 如果您使用cookie来保护Web服务,则该服务需要驻留在设置身份验证cookie的域上,因为同源策略不会将cookie发送到另一个域。
此外,Cookie使得非基于浏览器的应用程序(如移动到平板电脑应用程序)更难以使用您的API。

21
如果你在访问一个恶意网站时仍然登录了那个网站,该恶意网站将加载浏览器中的一个页面,该页面会触发一个POST请求到该网站的地址。此时,你的浏览器会自动附加认证cookie,从而允许攻击者更改你的密码。CORS是否可以防止这种攻击? - kbuilds
31
只有恶意页面使用 AJAX 提交表单,才会触发此问题。如果攻击者让您点击常规表单的提交按钮,则不会涉及跨域资源共享(CORS)的问题。 - MvdD
6
这是否意味着,只有在未使用CSRF token时,该网站才会存在漏洞? - kbuilds
10
没问题,CSRF攻击可以使用CSRF令牌来减轻其影响。但这是需要明确地做出的措施。 - MvdD
18
值得一提的是,Set-Cookie的SameSite属性可以有效地防止CSRF攻击。https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite 由于现代浏览器现在将SameSite=Lax作为默认值,并且如果您在GET请求上不进行更改,则默认情况下受到保护。但是只有在现代浏览器的情况下才是如此。 SameSite=Strict甚至是更强大的变体。阅读的好文章:https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/ - RussCoder
显示剩余8条评论

193

概述

您所询问的是客户端向服务器发送JSON Web Tokens(JWT)时使用cookie和bearer token之间的区别。

两者都可以发送数据。

其中一个区别是,cookie用于发送和存储任意数据,而bearer token专门用于发送授权数据。

该数据通常编码为JWT。

Cookie

Cookie是一对名称-值,在Web浏览器中存储,并具有到期日期和相关域名。

我们可以通过JavaScript或HTTP响应头在Web浏览器中存储cookie。

document.cookie = 'my_cookie_name=my_cookie_value'   // JavaScript
Set-Cookie: my_cookie_name=my_cookie_value           // HTTP Response Header

网络浏览器会自动在每个请求中向cookie所属的域名发送cookie。

GET http://www.bigfont.ca
Cookie: my_cookie_name=my_cookie_value               // HTTP Request Header

Bearer Token

Bearer Token是一个值,用于放置在任何HTTP请求的“Authorization”头中。它不会自动存储在任何地方,没有过期日期,也没有关联的域。它只是一个值。我们手动将该值存储在客户端,并手动将该值添加到HTTP授权头中。

GET http://www.bigfont.ca
Authorization: Bearer my_bearer_token_value          // HTTP Request Header

JWT和基于Token的认证

在进行基于Token的认证,例如OpenID、OAuth或OpenID Connect时,我们会从受信任的机构接收到访问令牌(access_token)(有时还包括id_token)。通常我们想要将其存储起来,并随着HTTP请求一起发送给受保护的资源。我们该如何做呢?

选项1是将token(s)存储在cookie中,这样可以处理存储操作并自动将token(s)在每个请求的Cookie头中发送到服务器。服务器然后解析cookie,检查token(s),并做出相应的响应。

选项2是将Token存储在本地/会话存储中,然后手动设置每个请求的Authorization头。在这种情况下,服务器读取标头并像使用cookie一样继续执行。

值得阅读相关的RFC以了解更多信息。


1
将敏感数据存储到本地/会话存储中是否安全?还是因为令牌是短暂的,所以无论如何都可以? - hyunchel
应用服务器一旦接收到包含声明的ID令牌,它会将该令牌发送给浏览器吗?以便后续的API调用可以传递该ID令牌,从而允许应用服务器知道是谁调用了该端点。 - variable
选项2中,会话存储与cookie不同吗?会话存储是什么意思? - Gezim
@Gezim 一个重要的区别是:浏览器会自动将Cookie与其域中的所有请求一起发送;但浏览器在使用会话存储时不会这样做。另一个相关的重要区别是:许多浏览器为了避免跟踪而阻止Cookie,但它们倾向于不阻止会话或本地存储,因为它们对于跟踪的需求较少。 - Shaun Luttin
在浏览器中进行HTTP请求时,您不能使用Set-Cookie头。您必须使用document.cookie,浏览器会在发送请求之前自动添加HttpOnly cookie。 - NobodyImportant
一个cookie是一个存储在网络浏览器中的键值对。我曾以为cookie是由浏览器存储在本地机器上并由浏览器发送的。实际上,它们并不是存储在浏览器中的。 - undefined

41
除了MvdD提到的关于自动发送cookie的内容之外,还有以下补充:
  1. Cookie可以作为媒介,但它最重要的功能是与浏览器的交互。Cookie由服务器设置并以非常特定的方式在请求中发送。另一方面,JWT专门作为一个媒介,它是某些事实的断言,具有特定的结构。如果你愿意,你可以把JWT作为你的身份验证cookie。在阅读比较它们的文章时,通常是指使用从前端代码发送的bearer token作为JWT,以及对应于后端缓存会话或用户数据的身份验证cookie。
  2. JWT提供了许多功能,并将它们放入标准中,以便在各方之间使用。JWT可以在许多不同的位置上作为一种签名断言。无论你在cookie中放入什么数据或是否对其进行签名,它只在浏览器和特定后端之间使用才真正有意义。JWT可以从浏览器到后端、在由不同方控制的后端之间(OpenId Connect就是一个例子)或在一个组织的后端服务之间使用。关于您刚提到的已签名cookie的特定示例,您可能可以在该用例中实现与JWT相同的功能(“不使用会话ID,不使用服务器内存或文件存储”),但您会失去库和标准的同行评审,另外还有其他答案中谈到的CSRF问题。

总之:你正在阅读的帖子可能在比较JWT作为bearer token与浏览器到服务器身份验证用途的身份验证cookie。但JWT可以做更多的事情,它引入了用于在您可能正在考虑的用例之外使用的标准化和功能。


9
很好,澄清比较的确是在Bearer令牌和Cookie之间。 - Shaun Luttin

37

虽然cookie会随请求自动发送,从而增加CSRF攻击的风险,但当设置了HttpOnly标志时,它们可以降低XSS攻击的风险,因为注入到页面中的任何脚本都无法读取cookie。

CSRF: 攻击者在其网站上让用户点击链接(或查看图片),从而导致浏览器向受害者网站发送请求。如果受害者使用cookie,则浏览器将自动包含cookie在请求中,并且如果GET请求可以引起非只读操作,受害者网站就容易受到攻击。

XSS: 攻击者将脚本嵌入到受害者网站中(只有在输入未正确进行过滤时,受害者网站才容易受到攻击),攻击者的脚本可以执行页面上允许的任何JavaScript操作。 如果您将JWT令牌存储在本地存储中,则攻击者的脚本可以读取那些令牌,并将那些令牌发送到他们控制的服务器上。如果您使用带有HttpOnly标志的cookie,则攻击者的脚本将无法读取您的cookie。尽管如此,他们成功注入的脚本仍然可以执行JavaScript可以执行的任何操作,因此您仍然有风险(即,尽管他们可能无法读取cookie以便稍后使用,但是他们可以使用XHR向受害者网站发送请求,这些请求会包括cookie)。


有没有办法防止攻击者通过localStorage或发送带有JWT的cookie来获取您的JWT? - Dignity Dignity
@DignityDignity 你可以将JWT存储在localstorage或cookie中(如此处所述:https://dev59.com/8VoU5IYBdhLWcg3wTVzE#38470665);我的答案只是提到了cookie与bearer令牌的权衡取舍(从某种意义上来说,cookie可能更安全,但在另一方面则不太安全)。 - cwa
CSRF几乎是不可能的,因为sameSite的默认值是lax。 - David Klempfner

28
参考 - JSON Web Token 的必要性

Cookies

在使用 cookies 方式进行认证时,一旦用户被认证通过,Gmail 服务器将会创建一个唯一的 session id。与该 session id 相对应的,服务器将在内存中存储所有 Gmail 服务器为了识别该用户并允许其执行操作所需的用户信息。
在所有随后的请求和响应中,这个 session id 也会被传递。因此当服务器收到请求时,它将检查该 session id。使用此 session id 将检查是否存在相应的信息。然后它将允许用户访问资源,并返回带有 session id 的响应。

enter image description here

Cookie 的缺点

  • Cookies/session id 不是自包含的。它是一个引用令牌。每次验证时,Gmail 服务器需要获取相应的信息。
  • 不适合涉及多个 API 和服务器的微服务架构。

enter image description here

JSON Web Token(JWT)

  • JWT 是一个自包含的令牌,其中包含用于验证和授权的信息。
  • 在使用 JWT 进行身份验证时,一旦用户被认证通过,Gmail 服务器将创建一个包含用户 ID 和其他有关用户的信息的 JWT。该 JWT 将被返回给客户端,并存储在本地存储中。
  • 以后的每个请求将携带此 JWT。然后服务器将解密并验证 JWT,而无需访问数据库或其他服务来获取有关用户的信息。这使得 JWT 很适合用于微服务架构。
JWT是自包含的,它是一个值令牌。因此,在每次验证期间,Gmail服务器不需要获取与其对应的信息。
  • 它经过数字签名,因此如果有人修改它,服务器会得知
  • 它最适用于微服务架构
  • 它还具有指定过期时间等其他优点。
  • 输入图像描述


    8
    如果用户点击“退出所有会话”,那么每次请求都必须通过数据库调用验证令牌-因此其自我包含的想法是不成立的。短期过期可能有所帮助,但并不完美。 - vaughan
    谷歌云盘如何知道用于验证JWT令牌的Gmail服务中的秘密密钥? - Amit
    2
    @Amit Gmail和Google Drive将共享相同的密钥。 - Charlie Martin

    1
    1. 是的,Cookie 可以用于任何事情。
    2. 我们为什么需要 JWT?

    JSON 是最好的(也是唯一需要的)数据格式,你可以在其中放置任何想要的内容。

    使用 JWT,您不受数据大小的限制,但使用 cookies,每个域只有 4093 字节 - 对于所有 cookies,而不是一个。

    尽管如上所述,我会使用 cookies 实现 JWT 认证。即生成 JWT 访问令牌并将其存储在 cookie 中。


    1
    不要把JWT访问令牌存储在cookie中。最好将其存储在会话存储中,因为将其存储在cookie中很容易受到XSS攻击,而Httponly cookie仍然容易受到CSRF攻击的影响。 - User-92

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接