令牌认证 vs. Cookies

244

令牌认证和使用Cookie进行认证有何区别?

我正在尝试实现Ember Auth Rails Demo,但我不理解在Ember Auth FAQ中描述的为什么要使用令牌身份验证。


4
Token可以发放给您的移动应用程序并存储在变量中(由您)以供以后使用,或通过JavaScript在您的浏览器中保存(由您)以供在SPA请求中使用。Cookie通常在浏览器中使用(由浏览器)。 - tinmac
4
请查看2016年撰写的文章https://auth0.com/blog/cookies-vs-tokens-definitive-guide/。 - Michael Freidgeim
Cookie本身无法进行身份验证。身份验证是通过将令牌存储在Cookie内完成的。 - apple apple
1
我认为你的疑惑将会在这里得到解答[链接](https://roadmap.sh/guides/token-authentication)。 - Darshan Adakane
10个回答

523

HTTP是无状态的。为了授权,您需要为您发送给服务器的每个请求“签名”。

令牌身份验证

  • 向服务器发出的请求由“令牌”签名 - 通常意味着设置特定的HTTP标头,但是它们可以发送到HTTP请求的任何部分(POST正文等)

  • 优点:

  • 您只能授权希望授权的请求。(Cookies-即使是授权cookie也会发送到每个请求。)

  • 免疫XSRF(XSRF的简短示例 - 我会通过电子邮件向您发送一个链接,看起来像<img src="http://bank.example?withdraw=1000&to=myself" />,如果您通过cookie身份验证登录到bank.example ,且bank.example 没有任何XSRF保护措施,我将从您的帐户中提取资金,因为您的浏览器将触发对该URL的已经授权的GET请求。)请注意,您可以使用基于Cookie的身份验证采取防伪措施,但必须实现这些措施。

  • Cookies绑定到单个域。在域foo.example 上创建的cookie无法由域bar.example 读取,而您可以将令牌发送到任何您喜欢的域。这对于消耗多个需要授权的服务的单页应用程序特别有用-因此我可以在域myapp.example 上拥有Web应用程序,该应用程序可以向myservice1.example myservice2.example发出经过授权的客户端请求。

  • 缺点:

    • 你必须将令牌存储在某个地方,而cookie可以“开箱即用”存储。我能想到的位置是localStorage(缺点:即使关闭浏览器窗口,令牌也会被持久化),sessionStorage(优点:关闭浏览器窗口后会丢弃令牌,缺点:在新标签页中打开链接将使该标签页成为匿名的)和cookie(优点:关闭浏览器窗口后会丢弃令牌。如果使用会话cookie,则在打开新标签页时将进行身份验证,并且您免于受到XSRF攻击,因为您忽略了身份验证cookie,只是使用它作为令牌存储。缺点:为每个请求发送cookie。如果此cookie未标记为https only,则容易受到中间人攻击。)
  • 基于令牌的身份验证稍微容易遭受XSS攻击(例如,如果我能够在您的站点上运行注入脚本,我可以窃取您的令牌;但是基于cookie的身份验证也不是万无一失 - 虽然标记为http-only的cookie无法被客户端读取,但客户端仍然可以代表您发出请求,这些请求将自动包括授权cookie。)

  • 要下载文件的请求,只能由授权用户使用,需要使用File API。对于基于cookie的身份验证,同一请求可以“开箱即用”。

cookie身份验证

  • 服务器始终通过授权cookie进行签名。
  • 优点:
    • Cookie可以标记为“http-only”,这使得它们无法在客户端上读取。这对于XSS攻击保护更好。
    • “开箱即用” - 无需在客户端实现任何代码。
  • 缺点:
    • 仅适用于单个域。 (因此,如果您有一个向多个服务发出请求的单页面应用程序,您可能会做一些疯狂的事情,例如反向代理。)
    • 容易受到XSRF攻击。您必须采取额外措施来保护您的站点免受跨站点请求伪造。
  • 每个请求都会发送令牌(即使对于不需要身份验证的请求也是如此)。

总体而言,我认为令牌会给你更好的灵活性(因为你不受单个域名的限制)。缺点是你需要做相当多的编程工作。


3
谢谢 @ondrej-svejdar。这绝对是最好的答案!我只想与“相当多编码”这部分争论。几乎任何语言都有很多可用的库。因此,除非您真的想了解JWT实现的机制,否则无需从头开始。 - FullStackForger
4
每个请求都会发送令牌。 - Eugen Konkov
24
不一定需要。只有在添加标题时才需要。无论您想要还是不想要,浏览器都会发送cookie。 - Royi Namir
2
@Zack - 这很重要。Cookie的问题在于它们会被浏览器自动附加到请求中。另一方面,令牌是由JavaScript附加到XHR请求中的。对于evildomain.com来说,无法访问mysite.com的本地存储(顺便说一下,我不建议将令牌存储在本地存储中)或RAM(我假设您在这里指的是包含令牌的JavaScript变量),因为该变量被沙盒化在不同的浏览器窗口中。 - Ondrej Svejdar
4
整体而言,答案很好,但我觉得这部分有点误导性。在单个 URL 端点下反向代理 API 并不算“疯狂的事情”,有时甚至是必需的(例如网站需要支持 IE9 客户端的 SPA 网站)。为 API 设置单独的端点可以很好地分散负载,特别是在 SPA 网站或其他应用程序经常使用 API 的情况下。将反向代理标记为“疯狂的事情”有些夸张了。 - ryancdotnet
显示剩余12条评论

67

针对谷歌员工:

  • 不要将状态性状态传输机制混合使用

状态性

  • 有状态的 = 在服务器端保存授权信息,这是传统方式
  • 无状态的 = 在客户端保存授权信息,并附带签名以确保完整性

机制

  • Cookie = 特殊的头部,受浏览器特殊处理(访问、存储、过期、安全性、自动传输)
  • 自定义头部 = 例如Authorization,只是普通的头部,没有特殊处理,客户端必须管理传输的所有方面
  • 其他。可以利用其他转移机制,例如查询字符串曾经是传输认证ID的选择,但由于其不安全而被放弃。

状态性比较

  • “状态性授权”表示服务器在服务器端存储和维护用户授权信息,使授权成为应用程序状态的一部分
  • 这意味着客户端只需要保存一个“auth ID”,服务器可以从其数据库中读取授权详细信息
  • 这意味着服务器保持一组活动的授权(已登录的用户),并将查询此信息以供每个请求使用
  • "无状态授权"意味着服务器不存储和维护用户认证信息,它只是不知道哪些用户已经登录,并依赖客户端生成认证信息
  • 客户端将完整的认证信息(比如用户ID、权限、过期时间等)存储起来,这不仅仅是认证ID,因此被赋予了一个新名称叫做令牌
  • 显然客户端是不可信的,所以认证数据会与由hash(data+秘钥)生成的签名一起存储,其中秘钥只有服务器知道,因此可以验证令牌数据的完整性
  • 请注意,令牌机制仅确保完整性,但并不保密,客户端必须自行实现保密处理
  • 这也意味着对于每个请求,客户端都必须提交完整的令牌,这会产生额外的带宽负担
  • 机制比较

    • "Cookie"只是一个头部信息,但在浏览器上预装了一些操作
    • Cookie可以由服务器设置并被客户端自动保存,并且将自动发送到同一域名
    • Cookie可以标记为httpOnly,从而防止客户端JavaScript访问
    • 预装的操作可能在不同于浏览器的平台上(例如移动设备)无法使用,这可能会导致额外的努力
    • "自定义头部信息"只是没有预装操作的自定义头部信息
  • 客户端负责接收、存储、保护、提交和更新每个请求的自定义头部,这可以帮助防止一些简单的恶意URL嵌入
  • 总结

    • 没有什么魔法,认证状态必须在服务器或客户端某处存储
    • 你可以使用cookie或其他自定义头部实现有状态或无状态
    • 当人们谈论这些事情时,他们的默认思维模式大多是:无状态=令牌+自定义头部,有状态=认证ID+cookie;这些不是唯一可能的选项
    • 它们各有优缺点,但即使对于加密令牌,你也不应该存储敏感信息

    37
    一个典型的Web应用程序大多是无状态的,这是由于其请求/响应的性质所决定的。HTTP协议是无状态协议的最佳例子。但是,由于大多数Web应用程序需要状态,为了保持服务器和客户端之间的状态,使用cookie,以便服务器可以在每个响应中向客户端发送一个cookie。这意味着客户端发出的下一个请求将包括此cookie,因此将被服务器识别。这样,服务器就可以与无状态的客户端维护会话,几乎了解应用程序状态的一切,但存储在服务器中。在这种情况下,客户端没有任何时候保存状态,这不是Ember.js的工作方式。
    在Ember.js中,情况有所不同。 Ember.js让程序员的工作更加容易,因为它确实在客户端保存状态,而不必向服务器请求状态数据即可随时了解其状态。
    然而,在客户端保持状态有时也会引入并发问题,这种情况在无状态情况下根本不存在。Ember.js 能够帮助你解决这些问题,特别是 ember-data 是为此而构建的。总之,Ember.js 是专为有状态客户端设计的框架。
    Ember.js 不像典型的无状态 Web 应用程序那样,会将会话、状态和相应的 cookie 几乎完全由服务器处理。Ember.js 完全在 JavaScript 中保存其状态(在客户端内存中,而不是像其他一些框架一样在 DOM 中),不需要服务器管理会话。这使得 Ember.js 在许多情况下更加灵活,例如当您的应用程序处于离线模式时。
    显然,出于安全原因,它确实需要在每次请求时发送某种令牌或唯一密钥以进行身份验证。这样,服务器可以查找发送的令牌(最初由服务器发行),并在向客户端发送响应之前验证其是否有效。
    在我看来,使用身份验证令牌而不是cookie的主要原因,正如Ember Auth FAQ所述,主要是因为Ember.js框架的性质,并且它更符合有状态的Web应用程序范例。因此,在构建Ember.js应用程序时,cookie机制并不是最佳方法。
    我希望我的回答能让你的问题更加明确。

    101
    为什么Token比Cookie更好/不同,我仍然不理解。无论如何,您都需要将某些东西发送到API服务器以识别有效会话。假设您在单个域上运行所有内容(即使Ember和API位于不同的服务器上,您只需在CDN后运行即可完成此操作,这也是您应该做的),那么令牌提供了什么优势,值得进行额外的设置工作并增加易受时序攻击的风险? - Michael Johnston
    54
    同意Michael Johnston的观点,这个答案一直在解释令牌认证是什么,但实际上并没有回答问题。我能看到最相关的信息是在最后一段中的“因为Ember.js框架的性质以及它更符合有状态Web应用程序的范例”,但这并不是一个很好的答案。我也有同样的问题。 - Daniel
    6
    我同意这里两个评论的观点...事实上,我觉得整个"这就是Ember的方式"有点推卸责任的味道。 - Grapho
    6
    就 cookie 和通过其他方式提交的令牌而言,我认为状态性是一个误导人的问题。我认为它混淆了用户证据和其他用户配置文件信息的概念。我可以像使用 HTTP 标头或其他渠道提交令牌一样使用 cookie。我认为差异更多地与绕开 cookie 的单一来源策略问题或从后端本机客户端中删除实现 cookie 容器的负担有关。 - Michael Lang
    25
    不要宣传Ember.js,关注问题本身。抱歉有些粗鲁。 - Waleed Mohsin
    显示剩余5条评论

    35
    • 令牌需要存储在某个地方(本地/会话存储或cookie中)

    • 令牌可以像cookie一样过期,但您有更多的控制权

    • 本地/会话存储无法跨域工作,请使用标记cookie

    • 每个CORS请求都将发送预检请求

    • 当您需要流式传输数据时,请使用令牌来获取签名请求

    • 处理XSS比处理XSRF更容易

    • 令牌会发送到每个请求中,请注意其大小

    • 如果存储机密信息,请加密令牌

    • JSON Web Tokens可用于OAuth

    • 令牌不是万能钥匙,请仔细考虑您的授权用例

    http://blog.auth0.com/2014/01/27/ten-things-you-should-know-about-tokens-and-cookies/

    http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/


    18
    你提到的点数是针对Cookie还是Token的并不清楚,它们的顺序是怎样的? - AncientSwordRage
    8
    我不明白为什么你可以比对cookie拥有更多的令牌控制权。 - Aaron
    @onsmith 从我的理解来看,这里不仅仅是单个要点。首先,每个请求都会发送cookie。发送令牌是由JavaScript代码触发的。它们不会自动发送。此外,根据rfc section 4的规定,JWT被设计为用于在各方之间传输基于安全声明的容器。这提供了更精细的控制,并使能够生成具有一组权限的身份验证令牌,以便第三方代表您使用它们。 - FullStackForger

    12

    我认为这里有一些混淆。基于cookie的身份验证和现在使用的HTML5 Web存储的重要区别是,浏览器被构建为每当它们从设置了cookie的域请求资源时发送cookie数据。除非关闭cookie,否则无法阻止它。浏览器不会发送Web Storage中的数据,除非页面中的代码发送它。而且页面只能访问它们存储的数据,而不能访问其他页面存储的数据。

    因此,一个担心他们的cookie数据可能被Google或Facebook使用的用户可能会关闭cookie。但是,除非广告商找到了利用它的方法,否则他们没有关掉Web Storage的理由。

    所以,基于cookie的身份验证与基于令牌的身份验证之间的区别就在于后者使用了Web Storage。


    6

    基于令牌的身份验证是无状态的,服务器不需要在会话中存储用户信息。这使得应用程序能够扩展而不必担心用户登录的位置。虽然基于cookie的Web服务器框架亲和力较强,但这对基于令牌的身份验证并不是问题。因此,可以使用相同的令牌从已登录的域以外获取安全资源,避免了另一个uid/pwd身份验证。

    这里有一篇非常好的文章:

    http://www.toptal.com/web/cookie-free-authentication-with-json-web-tokens-an-example-in-laravel-and-angularjs


    4

    使用令牌(Token)的情况包括:

    需要联合验证时。例如,您想要使用一个提供程序(Token Dispensor)作为令牌发行者,并将您的API服务器用作令牌验证器。应用程序可以通过Token Dispensor进行身份验证,接收一个令牌,然后将该令牌呈现给您的API服务器进行验证。(同样适用于Google登录、Paypal、Salesforce.com等)。

    需要异步处理时。例如,您希望客户端发送请求,然后将该请求存储在某个地方,以便由另一个系统“稍后”执行。该分离的系统将不会与客户端有同步连接,也可能没有直接连接到中央令牌提供机构。JWT可以被异步处理系统读取,以确定是否可以和应该在稍后的时间完成工作项。这在某种程度上与上述联合验证思想有关。但请注意: JWT有过期时间。如果保存工作项的队列在JWT生命周期内未被处理,则不再信任声明。

    需要客户端签名请求时。在此情况下,请求是由客户端使用私钥签署的,服务器将使用客户端已注册的公钥进行验证。


    3

    主要区别之一是cookie受到同源策略的限制,而令牌则不受。这会产生各种下游影响。

    由于cookie只发送到特定主机并从特定主机接收,因此该主机必须承担验证用户的负担,并且用户必须在该主机上创建带有安全数据的帐户以进行验证。

    另一方面,令牌是由发行者发放的,不受同源策略的限制。发行者可以是任何人,由主机决定信任哪些发行者。像Google和Facebook这样的发行者通常很值得信赖,因此主机可以将验证用户(包括存储所有用户安全数据)的负担转移到另一方,并且用户可以将其个人数据整合到特定发行者下,而不必为与每个主机交互时记住一堆不同的密码。

    这允许单点登录场景降低用户体验中的总体摩擦。理论上,随着专门的身份提供者涌现来提供认证服务,网络也变得更加安全,而不是让每个网站都自己开发可能不完善的认证系统。随着这些提供者的出现,即使对于非常基本的资源,提供安全的Web资源的成本也趋近于零。

    因此,总的来说,令牌减少了提供身份验证所涉及的摩擦和成本,并将安全Web的各个方面的负担转移到更能够实施和维护安全系统的集中化方。


    0

    会话 cookie 需要您与存储您登录状态的另一端数据库共享个人密钥。这里没有任何花哨的东西,只是一个密码短语来标识您的会话,但细节可能有所不同。

    身份验证令牌使用复杂的加密技术,消除了需要存储您登录状态的数据库的需求,通过发出一份文件并在指定提供者的签名下发布允许您进行的操作及其截止日期。

    打个比喻,cookie 就像会员卡,接待员需要打电话或查询数据库以检查您是否是会员。而身份验证令牌就像带有签名和过期日期的支票。安全性基于签名的假设,即很难伪造,而无需直接向发行方询问。

    在两种情况下,都涉及授权,而非认证。拥有会员卡或支票的任何人都拥有访问权,他们不能证明您的身份,只是证明您有权访问所请求的资源。因此,它们必须小心防范盗窃,特别是对于难以撤销的身份验证令牌更是如此。


    你在谈论JWT。并不是所有的令牌都需要自签名,它们可以像会话ID一样运行。 - Pithikos

    0
    简而言之:
    JWT vs Cookie Auth
    
    |                    | Cookie        | JWT                             |
    | Stateless          | No            | Yes                             |
    | Cross domain usage | No            | Yes                             |
    | Mobile ready       | No            | Yes                             |
    | Performance        | Low           | High (no need in request to DB) |
    | Add to request     | Automatically | Manually (if not in cookie)     |
    

    10
    完全错误!你可以拥有无状态cookie。JWT是一种格式,不能与cookie - 一个容器相比较。您可以使用cookie来存储JWT。 - Kevin

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