SPA认证和会话管理最佳实践

本文探讨了使用Angular,Ember等框架构建单页应用(SPA)时的身份验证和会话管理最佳实践。建议使用基于令牌的身份验证系统,强调了SSL/TLS的重要性以防止中间人攻击。讨论了HTTP基本认证、摘要认证、OAuth 2.0等方法的优缺点,以及在客户端存储凭据的风险。推荐使用HTTPS和JWT(JSON Web Tokens)增强安全性,以防止XSS攻击和会话劫持。
摘要由CSDN通过智能技术生成

本文翻译自:SPA best practices for authentication and session management

When building SPA style applications using frameworks like Angular, Ember, React, etc. what do people believe to be some best practices for authentication and session management? 使用Angular,Ember,React等框架构建SPA风格的应用程序时,人们认为什么是身份验证和会话管理的最佳实践? I can think of a couple of ways of considering approaching the problem. 我可以考虑考虑解决该问题的几种方法。

  1. Treat it no differently than authentication with a regular web application assuming the API and and UI have the same origin domain. 假定API和UI具有相同的原始域,则对待它与使用常规Web应用程序进行身份验证没有区别。

    This would likely involve having a session cookie, server side session storage and probably some session API endpoint that the authenticated web UI can hit to get current user information to help with personalization or possibly even determining roles/abilities on the client side. 这可能涉及到具有会话cookie,服务器端会话存储以及可能经过身份验证的Web UI可以访问以获取当前用户信息以帮助进行个性化甚至可能确定客户端角色/功能的某些会话API端点。 The server would still enforce rules protecting access to data of course, the UI would just use this information to customize the experience. 服务器当然仍然会执行保护访问数据的规则,UI只会使用此信息来定制体验。

  2. Treat it like any third-party client using a public API and authenticate with some sort of token system similar to OAuth. 像使用公共API的任何第三方客户端一样对待它,并使用类似于OAuth的某种令牌系统进行身份验证。 This token mechanism would used by the client UI to authenticate each and every request made to the server API. 客户端UI将使用此令牌机制来验证对服务器API的每个请求。

I'm not really much of an expert here but #1 seems to be completely sufficient for the vast majority of cases, but I'd really like to hear some more experienced opinions. 我在这里并不是真正的专家,但是对于大多数情况来说,#1似乎已经足够了,但是我真的很想听听一些更有经验的意见。


#1楼

参考:https://stackoom.com/question/1pXvj/SPA认证和会话管理最佳实践


#2楼

I would go for the second, the token system. 我将第二讲令牌系统。

Did you know about ember-auth or ember-simple-auth ? 您知道ember-authember-simple-auth吗? They both use the token based system, like ember-simple-auth states: 它们都使用基于令牌的系统,例如ember-simple-auth状态:

A lightweight and unobtrusive library for implementing token based authentication in Ember.js applications. 一个轻量级且轻巧的库,用于在Ember.js应用程序中实现基于令牌的身份验证。 http://ember-simple-auth.simplabs.com http://ember-simple-auth.simplabs.com

They have session management, and are easy to plug into existing projects too. 他们具有会话管理,并且很容易插入现有项目。

There is also an Ember App Kit example version of ember-simple-auth: Working example of ember-app-kit using ember-simple-auth for OAuth2 authentication. 还有一个Ember App Kit示例版本的ember-simple-auth: 使用ember-simple-auth进行OAuth2身份验证的ember-app-kit的工作示例。


#3楼

This question has been addressed, in a slightly different form, at length, here: 在这里,以略有不同的形式解决了这个问题:

RESTful Authentication RESTful身份验证

But this addresses it from the server-side. 但这是从服务器端解决的。 Let's look at this from the client-side. 让我们从客户端来看。 Before we do that, though, there's an important prelude: 但是,在此之前,有一个重要的前奏:

Javascript Crypto is Hopeless JavaScript加密是绝望的

Matasano's article on this is famous, but the lessons contained therein are pretty important: Matasano在这方面的文章很有名,但是其中包含的教训非常重要:

http://www.matasano.com/articles/javascript-cryptography/ http://www.matasano.com/articles/javascript-cryptography/

To summarize: 总结一下:

  • A man-in-the-middle attack can trivially replace your crypto code with <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script> 中间人攻击可以用<script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>简单替换您的密码<script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script> <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • A man-in-the-middle attack is trivial against a page that serves any resource over a non-SSL connection. 对于通过非SSL连接提供任何资源的页面,中间人攻击是微不足道的。
  • Once you have SSL, you're using real crypto anyways. 一旦有了SSL,您无论如何都会使用真实的加密货币。

And to add a corollary of my own: 并添加一个我自己的推论:

  • A successful XSS attack can result in an attacker executing code on your client's browser, even if you're using SSL - so even if you've got every hatch battened down, your browser crypto can still fail if your attacker finds a way to execute any javascript code on someone else's browser. 即使您使用的是SSL,成功的XSS攻击也可能导致攻击者在客户端的浏览器上执行代码-因此,即使您遇到各种麻烦,如果攻击者找到了执行方法,浏览器加密仍然可能失败他人浏览器上的任何JavaScript代码。

This renders a lot of RESTful authentication schemes impossible or silly if you're intending to use a JavaScript client. 如果您打算使用JavaScript客户端,这会使许多RESTful身份验证方案变得不可能或很愚蠢。 Let's look! 我们看看吧!

HTTP Basic Auth HTTP基本认证

First and foremost, HTTP Basic Auth. 首先,HTTP基本身份验证。 The simplest of schemes: simply pass a name and password with every request. 最简单的方案:只需在每个请求中传递名称和密码。

This, of course, absolutely requires SSL, because you're passing a Base64 (reversibly) encoded name and password with every request. 当然,这绝对需要SSL,因为您将在每个请求中传递Base64(可逆)编码的名称和密码。 Anybody listening on the line could extract username and password trivially. 任何在线路上监听的人都可以轻松提取用户名和密码。 Most of the "Basic Auth is insecure" arguments come from a place of "Basic Auth over HTTP" which is an awful idea. 大多数“基本身份验证是不安全的”参数都来自“可怕的HTTP上的基本身份验证”。

The browser provides baked-in HTTP Basic Auth support, but it is ugly as sin and you probably shouldn't use it for your app. 该浏览器提供了内置的HTTP Basic Auth支持,但它很丑陋,您可能不应该在应用程序中使用它。 The alternative, though, is to stash username and password in JavaScript. 但是,替代方法是将用户名和密码隐藏在JavaScript中。

This is the most RESTful solution. 这是最RESTful的解决方案。 The server requires no knowledge of state whatsoever and authenticates every individual interaction with the user. 服务器不要求任何状态知识,并验证与用户的每个单独交互。 Some REST enthusiasts (mostly strawmen) insist that maintaining any sort of state is heresy and will froth at the mouth if you think of any other authentication method. 一些REST爱好者(大多数是稻草人)坚持认为,维持任何一种状态都是异端,如果您想到其他任何身份验证方法,它们都会在您的嘴上泛滥。 There are theoretical benefits to this sort of standards-compliance - it's supported by Apache out of the box - you could store your objects as files in folders protected by .htaccess files if your heart desired! 这种标准的遵从性在理论上有好处-即装即用的Apache支持-如果您愿意,可以将对象作为文件存储在受.htaccess文件保护的文件夹中!

The problem ? 问题吗? You are caching on the client-side a username and password. 您正在客户端缓存用户名和密码。 This gives evil.ru a better crack at it - even the most basic of XSS vulnerabilities could result in the client beaming his username and password to an evil server. 这给了evil.ru一个更好的破解-甚至最基本的XSS漏洞都可能导致客户端将其用户名和密码发送到邪恶的服务器。 You could try to alleviate this risk by hashing and salting the password, but remember: JavaScript Crypto is Hopeless . 您可以尝试通过对密码进行散列和加盐处理来减轻这种风险,但是请记住: JavaScript Crypto is Hopeless You could alleviate this risk by leaving it up to the Browser's Basic Auth support, but.. ugly as sin, as mentioned earlier. 您可以通过将其留给浏览器的基本身份验证支持来减轻这种风险,但是如前所述,这很丑陋。

HTTP Digest Auth HTTP摘要验证

Is Digest authentication possible with jQuery? 使用jQuery可以进行摘要式身份验证吗?

A more "secure" auth, this is a request/response hash challenge. 更“安全”的身份验证,这是一个请求/响应哈希挑战。 Except JavaScript Crypto is Hopeless , so it only works over SSL and you still have to cache the username and password on the client side, making it more complicated than HTTP Basic Auth but no more secure . 除了JavaScript Crypto是Hopeless以外 ,它只能在SSL上运行,并且您仍然必须在客户端缓存用户名和密码,这使其比HTTP Basic Auth更为复杂,但不再安全

Query Authentication with Additional Signature Parameters. 使用其他签名参数的查询身份验证。

Another more "secure" auth, where you encrypt your parameters with nonce and timing data (to protect against repeat and timing attacks) and send the. 另一个更“安全”的身份验证,您可以使用随机数和定时数据对参数进行加密(以防止重复和定时攻击)并发送。 One of the best examples of this is the OAuth 1.0 protocol, which is, as far as I know, a pretty stonking way to implement authentication on a REST server. 最好的例子之一是OAuth 1.0协议,据我所知,这是在REST服务器上实现身份验证的一种非常糟糕的方式。

http://tools.ietf.org/html/rfc5849 http://tools.ietf.org/html/rfc5849

Oh, but there aren't any OAuth 1.0 clients for JavaScript. 哦,但是没有用于JavaScript的OAuth 1.0客户端。 Why? 为什么?

JavaScript Crypto is Hopeless , remember. 记住, JavaScript加密是绝望的 JavaScript can't participate in OAuth 1.0 without SSL, and you still have to store the client's username and password locally - which puts this in the same category as Digest Auth - it's more complicated than HTTP Basic Auth but it's no more secure . JavaScript无法在没有SSL的情况下参与OAuth 1.0,并且您仍然必须在本地存储客户端的用户名和密码-这将其与Digest Auth放在同一类别中-它比HTTP Basic Auth复杂,但并不安全

Token 代币

The user sends a username and password, and in exchange gets a token that can be used to authenticate requests. 用户发送用户名和密码,作为交换,用户将获得可用于认证请求的令牌。

This is marginally more secure than HTTP Basic Auth, because as soon as the username/password transaction is complete you can discard the sensitive data. 这一点比HTTP Basic Auth更加安全,因为一旦用户名/密码事务完成,您就可以丢弃敏感数据。 It's also less RESTful, as tokens constitute "state" and make the server implementation more complicated. 由于令牌构成“状态”并使服务器实现更加复杂,因此它的RESTful也较少。

SSL Still SSL静态

The rub though, is that you still have to send that initial username and password to get a token. 麻烦的是,您仍然必须发送该初始用户名和密码才能获得令牌。 Sensitive information still touches your compromisable JavaScript. 敏感信息仍会触及您易受攻击的JavaScript。

To protect your user's credentials, you still need to keep attackers out of your JavaScript, and you still need to send a username and password over the wire. 为了保护用户的凭据,您仍然需要使攻击者远离JavaScript,并且仍然需要通过网络发送用户名和密码。 SSL Required. 需要SSL。

Token Expiry 代币到期

It's common to enforce token policies like "hey, when this token has been around too long, discard it and make the user authenticate again." 强制执行令牌策略,例如“嘿,当此令牌的时间过长时,将其丢弃并让用户再次进行身份验证”。 or "I'm pretty sure that the only IP address allowed to use this token is XXX.XXX.XXX.XXX ". 或“我很确定可以使用此令牌的唯一IP地址是XXX.XXX.XXX.XXX ”。 Many of these policies are pretty good ideas. 这些政策很多都是不错的主意。

Firesheeping 火警

However, using a token Without SSL is still vulnerable to an attack called 'sidejacking': http://codebutler.github.io/firesheep/ 但是,使用没有SSL的令牌仍然容易受到称为“ sidejacking”的攻击: http ://codebutler.github.io/firesheep/

The attacker doesn't get your user's credentials, but they can still pretend to be your user, which can be pretty bad. 攻击者没有获得您用户的凭据,但是他们仍然可以伪装成您的用户,这很糟糕。

tl;dr: Sending unencrypted tokens over the wire means that attackers can easily nab those tokens and pretend to be your user. tl; dr:通过网络发送未加密的令牌意味着攻击者可以轻松获取这些令牌并假装成为您的用户。 FireSheep is a program that makes this very easy. FireSheep是一个使此操作非常简单的程序。

A Separate, More Secure Zone 一个单独的,更安全的区域

The larger the application that you're running, the harder it is to absolutely ensure that they won't be able to inject some code that changes how you process sensitive data. 您正在运行的应用程序越大,绝对难以确保它们将无法注入某些代码来更改您处理敏感数据的方式就越困难。 Do you absolutely trust your CDN? 您完全相信您的CDN吗? Your advertisers? 您的广告商? Your own code base? 您自己的代码库?

Common for credit card details and less common for username and password - some implementers keep 'sensitive data entry' on a separate page from the rest of their application, a page that can be tightly controlled and locked down as best as possible, preferably one that is difficult to phish users with. 信用卡详细信息通用,用户名和密码通用-一些实施者将“敏感数据输入”与应用程序的其余部分保持在单独的页面上,该页面可以被严格控制和锁定,最好是一个页面。很难用网络钓鱼用户。

Cookie (just means Token) Cookie(仅表示令牌)

It is possible (and common) to put the authentication token in a cookie. 可以(并且通常)将身份验证令牌放入cookie。 This doesn't change any of the properties of auth with the token, it's more of a convenience thing. 这不会使用令牌更改auth的任何属性,这更方便了。 All of the previous arguments still apply. 前面所有的论点仍然适用。

Session (still just means Token) 会话(仍仅表示令牌)

Session Auth is just Token authentication, but with a few differences that make it seem like a slightly different thing: 会话身份验证只是令牌身份验证,但有一些区别,使它看起来似乎有点不同:

  • Users start with an unauthenticated token. 用户以未经身份验证的令牌开头。
  • The backend maintains a 'state' object that is tied to a user's token. 后端维护一个与用户令牌绑定的“状态”对象。
  • The token is provided in a cookie. 令牌在cookie中提供。
  • The application environment abstracts the details away from you. 应用程序环境将细节从您那里抽象出来。

Aside from that, though, it's no different from Token Auth, really. 除此之外,实际上,它与令牌认证没有什么不同。

This wanders even further from a RESTful implementation - with state objects you're going further and further down the path of plain ol' RPC on a stateful server. 这从RESTful实现中走得更远-使用状态对象,您将在有状态服务器上的普通RPC路径上走得越来越远。

OAuth 2.0 OAuth 2.0

OAuth 2.0 looks at the problem of "How does Software A give Software B access to User X's data without Software B having access to User X's login credentials." OAuth 2.0解决了“软件A如何在不让软件B有权访问用户X的登录凭证的情况下,使软件B能够访问用户X的数据”的问题。

The implementation is very much just a standard way for a user to get a token, and then for a third party service to go "yep, this user and this token match, and you can get some of their data from us now." 对于用户来说,该实现方式只是一种标准方式,然后让第三方服务去“是的,该用户与该令牌匹配,您现在就可以从我们这里获取他们的一些数据”。

Fundamentally, though, OAuth 2.0 is just a token protocol. 不过,从根本上讲,OAuth 2.0只是一个令牌协议。 It exhibits the same properties as other token protocols - you still need SSL to protect those tokens - it just changes up how those tokens are generated. 它具有与其他令牌协议相同的属性-您仍然需要SSL保护这些令牌-只是改变了这些令牌的生成方式。

There are two ways that OAuth 2.0 can help you: OAuth 2.0可以通过两种方式为您提供帮助:

  • Providing Authentication/Information to Others 向他人提供身份验证/信息
  • Getting Authentication/Information from Others 从他人那里获取身份验证/信息

But when it comes down to it, you're just... using tokens. 但是归根结底,您只是在使用令牌。

Back to your question 回到您的问题

So, the question that you're asking is "should I store my token in a cookie and have my environment's automatic session management take care of the details, or should I store my token in Javascript and handle those details myself?" 因此,您要问的问题是:“我应该将令牌存储在cookie中,让环境的自动会话管理来处理这些详细信息,还是应该将令牌存储在Javascript中并自己处理这些详细信息?”

And the answer is: do whatever makes you happy . 答案是: 做任何让自己快乐的事情

The thing about automatic session management, though, is that there's a lot of magic happening behind the scenes for you. 但是,关于自动会话管理的事情是,幕后发生了许多不可思议的事情。 Often it's nicer to be in control of those details yourself. 通常,自己控制这些细节会更好。

I am 21 so SSL is yes 我21岁,所以SSL是

The other answer is: Use https for everything or brigands will steal your users' passwords and tokens. 另一个答案是:对所有内容都使用https,否则盗版者将窃取用户的密码和令牌。


#4楼

You can increase security in authentication process by using JWT (JSON Web Tokens) and SSL/HTTPS. 您可以使用JWT (JSON Web令牌)和SSL / HTTPS来提高身份验证过程中的安全性。

The Basic Auth / Session ID can be stolen via: 可以通过以下方式窃取基本身份验证/会话ID:

  • MITM attack (Man-In-The-Middle) - without SSL/HTTPS MITM攻击(中间人)- 不使用SSL / HTTPS
  • An intruder gaining access to a user's computer 入侵者可以访问用户的计算机
  • XSS XSS

By using JWT you're encrypting the user's authentication details and storing in the client, and sending it along with every request to the API, where the server/API validates the token. 通过使用JWT,您可以加密用户的身份验证详细信息并存储在客户端中,并将其与每个请求一起发送到API,服务器/ API在此验证令牌。 It can't be decrypted/read without the private key (which the server/API stores secretly) Read update . 它不能被解密/没有私钥(其中服务器/ API店暗地里 ), 读取,更新 阅读

The new (more secure) flow would be: 新的(更安全的)流程将是:

Login 登录

  • User logs in and sends login credentials to API (over SSL/HTTPS) 用户登录并将登录凭据发送到API (通过SSL / HTTPS)
  • API receives login credentials API接收登录凭据
  • If valid: 如果有效:
    • Register a new session in the database Read update 在数据库中注册新会话阅读更新
    • Encrypt User ID, Session ID, IP address, timestamp, etc. in a JWT with a private key. 使用私钥对JWT中的用户ID,会话ID,IP地址,时间戳等进行加密。
  • API sends the JWT token back to the client (over SSL/HTTPS) API将JWT令牌发送回客户端(通过SSL / HTTPS)
  • Client receives the JWT token and stores in localStorage/cookie 客户端收到JWT令牌并存储在localStorage / cookie中

Every request to API 每个对API的请求

  • User sends a HTTP request to API (over SSL/HTTPS) with the stored JWT token in the HTTP header 用户使用HTTP标头中存储的JWT令牌(通过SSL / HTTPS)向API发送HTTP请求
  • API reads HTTP header and decrypts JWT token with its private key API读取HTTP标头并使用其私钥解密JWT令牌
  • API validates the JWT token, matches the IP address from the HTTP request with the one in the JWT token and checks if session has expired API会验证JWT令牌,将HTTP请求中的IP地址与JWT令牌中的IP地址进行匹配,并检查会话是否已过期
  • If valid: 如果有效:
    • Return response with requested content 返回包含请求内容的回复
  • If invalid: 如果无效:
    • Throw exception (403 / 401) 抛出异常(403/401)
    • Flag intrusion in the system 标记入侵系统
    • Send a warning email to the user. 向用户发送警告电子邮件。

Updated 30.07.15: 15年7月30日更新:

JWT payload/claims can actually be read without the private key (secret) and it's not secure to store it in localStorage. 实际上,可以在没有私钥(秘密)的情况下读取JWT有效负载/声明,并且将其存储在localStorage中并不安全。 I'm sorry about these false statements. 我对这些虚假陈述感到抱歉。 However they seem to be working on a JWE standard (JSON Web Encryption) . 但是,他们似乎正在采用JWE标准(JSON Web加密)

I implemented this by storing claims (userID, exp) in a JWT, signed it with a private key (secret) the API/backend only knows about and stored it as a secure HttpOnly cookie on the client. 我通过在JWT中存储声明(用户ID,exp),使用私钥(秘密)对其进行签名来实现这一点,API /后端仅知道该私钥并将其作为安全的HttpOnly cookie存储在客户端上。 That way it cannot be read via XSS and cannot be manipulated, otherwise the JWT fails signature verification. 这样,就无法通过XSS读取它并且无法对其进行操作,否则JWT无法通过签名验证。 Also by using a secure HttpOnly cookie, you're making sure that the cookie is sent only via HTTP requests (not accessible to script) and only sent via secure connection (HTTPS). 同样通过使用安全的HttpOnly cookie,您可以确保仅通过HTTP请求(脚本无法访问)发送cookie,并且仅通过安全连接(HTTPS)发送cookie。

Updated 17.07.16: 更新了16.07.16:

JWTs are by nature stateless. JWT本质上是无状态的。 That means they invalidate/expire themselves. 这意味着他们会使自己失效/失效。 By adding the SessionID in the token's claims you're making it stateful, because its validity doesn't now only depend on signature verification and expiry date, it also depends on the session state on the server. 通过将SessionID添加到令牌的声明中,您使其成为有状态的,因为它的有效性现在不仅仅取决于签名验证和到期日期,而且还取决于服务器上的会话状态。 However the upside is you can invalidate tokens/sessions easily, which you couldn't before with stateless JWTs. 但是,好处是您可以轻松地使令牌/会话无效,这是使用无状态JWT之前无法实现的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值