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

当使用Angular,Ember,React等框架构建SPA风格的应用程序时,人们认为什么是认证和会话管理的最佳实践? 我可以想到几种考虑解决问题的方法。

  • 假设API和UI具有相同的原始域,对待它与对常规Web应用程序进行认证没有区别。

    这可能涉及拥有会话cookie,服务器端会话存储以及可能的一些会话API端点,认证的Web UI可以获取当前用户信息以帮助个性化,甚至可能确定客户端角色/功能。 服务器仍然会执行保护数据访问的规则,UI将仅使用这些信息来定制体验。

  • 像使用公共API的任何第三方客户端一样对待它,并使用类似于OAuth的某种令牌系统进行身份验证。 客户端UI使用此令牌机制来验证向服务器API发出的每个请求。

  • 我在这里并不是很专业,但绝大多数情况下,#1似乎已经完全够用了,但我真的很想听听一些更有经验的意见。


    这个问题已经以一种略有不同的形式详细阐述,在这里:

    RESTful认证

    但是这从服务器端来解决它。 我们从客户端来看这个。 然而,在我们这样做之前,有一个重要的前奏:

    Javascript Crypto是绝望的

    Matasano关于这方面的文章很有名,但其中的教训非常重要:

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

    总结:

  • 中间人攻击可以用<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>
  • 中间人攻击对于通过非SSL连接服务任何资源的页面是微不足道的。
  • 一旦你有SSL,无论如何你都使用真正的密码。
  • 并添加我自己的推论:

  • 即使您使用SSL,一个成功的XSS攻击也可能导致攻击者在您的客户端浏览器上执行代码 - 因此,即使您已将所有孵化器都击溃,如果攻击者找到执行方法,浏览器加密仍可能失败任何其他人的浏览器上的JavaScript代码。
  • 这使得如果您打算使用JavaScript客户端,很多RESTful认证方案不可能或很无聊。 我们看看吧!

    HTTP基本身份验证

    首先,HTTP基本身份验证。 最简单的方案:简单地传递每个请求的名称和密码。

    当然,这绝对需要SSL,因为每次请求都会传递Base64(可逆)编码的名称和密码。 任何听线的人都可以简单地提取用户名和密码。 大部分“基本身份验证是不安全的”参数来自“基于HTTP的基本身份验证”,这是一个糟糕的想法。

    浏览器提供了预先支持的HTTP Basic Auth支持,但它很丑陋,您可能不应该将它用于您的应用。 不过,替代方法是在JavaScript中存储用户名和密码。

    这是最具REST风格的解决方案。 服务器不需要知道任何状态,并且认证与用户的每个单独交互。 一些REST爱好者(主要是稻草人)坚持认为,维持任何状态都是异端邪说,如果想到其他任何认证方法,都会在口中发泡。 这种符合标准的理论好处 - 它支持Apache开箱即用 - 您可以将对象存储为由.htaccess文件保护的文件夹中的文件,如果您的心想要的话!

    问题 ? 您正在客户端缓存用户名和密码。 这给了evil.ru更好的破解 - 即使是最基本的XSS漏洞也可能导致客户端将他的用户名和密码发送到恶意服务器。 您可以尝试通过哈希和篡改密码来缓解这种风险,但请记住: JavaScript Crypto是绝望的 。 您可以通过将其留到浏览器的基本身份验证支持来缓解这种风险,但像前面提到的那样丑陋像罪恶一样。

    HTTP摘要验证

    Digest验证可以使用jQuery吗?

    更安全的身份验证,这是一个请求/响应哈希挑战。 除了JavaScript Crypto是绝望的 ,所以它只能在SSL上工作,并且仍然需要在用户端缓存用户名和密码,这使得它比HTTP Basic Auth更复杂,但不再安全。

    使用附加签名参数查询身份验证。

    另一个更“安全”的认证,在那里你用nonce和时间数据加密你的参数(以防止重复和定时攻击)并发送。 其中一个最好的例子就是OAuth 1.0协议,据我所知,这是一种在REST服务器上实现身份验证的相当惊人的方式。

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

    哦,但是没有任何适用于JavaScript的OAuth 1.0客户端。 为什么?

    请记住, JavaScript Crypto是无望的 。 JavaScript无法在没有SSL的情况下参与OAuth 1.0,并且您仍然需要在本地存储客户端的用户名和密码 - 这与Digest Auth相同 - 它比HTTP Basic Auth更复杂,但并不安全。

    代币

    用户发送用户名和密码,并作为交换获取可用于认证请求的令牌。

    这比HTTP Basic Auth更安全,因为只要用户名/密码交易完成,您就可以放弃敏感数据。 它也不太RESTful,因为令牌构成“状态”并使服务器实现更复杂。

    SSL仍然

    尽管如此,您仍然需要发送该初始用户名和密码才能获得令牌。 敏感信息仍会触及您的可折中JavaScript。

    为了保护用户的凭据,您仍然需要让攻击者远离JavaScript,并且您仍然需要通过线路发送用户名和密码。 需要SSL。

    令牌到期

    执行令牌策略很常见,例如“嘿,当这个令牌已经存在很长时间时,丢弃它并使用户再次进行验证。” 或者“我很确定允许使用此令牌的唯一IP地址是XXX.XXX.XXX.XXX ”。 许多这些政策是非常好的想法。

    Firesheeping

    但是,使用不带SSL的令牌仍然容易受到称为“劫持”的攻击:http://codebutler.github.io/firesheep/

    攻击者不会获得用户的凭据,但他们仍然可以伪装成您的用户,这可能非常糟糕。

    tl; dr:通过电子邮件发送未加密的标记意味着攻击者可以轻松地获取这些标记并假装成您的用户。 FireSheep是一个让这个非常简单的程序。

    独立的,更安全的区域

    您运行的应用程序越大,绝对确保他们将无法注入某些代码来改变处理敏感数据的方式就越困难。 你完全相信你的CDN吗? 您的广告客户? 你自己的代码库?

    通常用于信用卡详细信息,用户名和密码不常见 - 一些实施者将“敏感数据输入”与其他应用程序的其他页面保持在不同的页面上,该页面可以尽可能最好地严格控制和锁定,很难用网络钓鱼用户。

    Cookie(只是表示令牌)

    将身份验证令牌放在cookie中是可能的(也是常见的)。 这不会改变任何带有令牌的auth属性,这更方便。 所有以前的论点仍然适用。

    会话(仍然只是表示令牌)

    会话身份验证只是令牌身份验证,但有一些差异使其看起来有点不同:

  • 用户从未经身份验证的令牌开始。
  • 后端维护一个绑定到用户令牌的“状态”对象。
  • 令牌在cookie中提供。
  • 应用程序环境将您的详细信息抽象出来。
  • 除此之外,真的,它与Token Auth没有区别。

    这更偏离了RESTful实现 - 使用状态对象,您将进一步深入到有状态服务器上纯朴RPC的路径。

    OAuth 2.0

    OAuth 2.0着眼于“软件A如何让软件B访问用户X的数据而没有软件B访问用户X的登录凭证”的问题。

    这个实现对于用户来说是一种标准的获取令牌的方式,然后让第三方服务去“是的,这个用户和这个令牌是匹配的,现在你可以从我们这里获得他们的一些数据。”

    但基本上,OAuth 2.0只是一个令牌协议。 它具有与其他令牌协议相同的属性 - 您仍然需要SSL来保护这些令牌 - 它只是改变了这些令牌的生成方式。

    OAuth 2.0有两种方式可以帮助您:

  • 向他人提供认证/信息
  • 从别人那里获得认证/信息
  • 但是当它来到它时,你只是......使用令牌。

    回到你的问题

    所以,你问的问题是“我是否应该将令牌存储在cookie中,并让我的环境的自动会话管理负责细节处理,还是应该将令牌存储在Javascript中并自己处理这些细节?”

    答案是:做任何让你快乐的事情。

    不过,关于自动会话管理的事情是,幕后有很多魔术发生在你身上。 通常自己控制这些细节更好。

    我21岁,所以SSL是肯定的

    另一个答案是:使用https来处理所有事情,否则盗贼会窃取用户的密码和令牌。


    您可以使用JWT(JSON Web令牌)和SSL / HTTPS提高身份验证过程的安全性。

    基本身份验证/会话ID可以通过以下方式被盗取:

  • MITM攻击(Man-In-The-Middle) - 没有SSL / HTTPS
  • 入侵者可以访问用户的计算机
  • XSS
  • 通过使用JWT,您可以对用户的身份验证详细信息进行加密并存储在客户端中,并将其与每个请求一起发送到API,其中服务器/ API验证令牌。 如果没有私钥(服务器/ API秘密存储) 读取更新 ,则无法解密/读取。

    新的(更安全的)流量将是:

    登录

  • 用户登录并将登录凭据发送到API(通过SSL / HTTPS)
  • API接收登录凭证
  • 如果有效:
  • 在数据库中注册一个新的会话读取更新
  • 使用私钥在JWT中加密用户ID,会话ID,IP地址,时间戳等。
  • API将JWT令牌发送回客户端(通过SSL / HTTPS)
  • 客户端收到JWT令牌并存储在localStorage / cookie中
  • 对API的每个请求

  • 用户使用HTTP标头中存储的JWT令牌向API发送HTTP请求(通过SSL / HTTPS)
  • API读取HTTP头并使用其私钥解密JWT令牌
  • API验证JWT令牌,将HTTP请求中的IP地址与JWT令牌中的IP地址进行匹配,并检查会话是否已过期
  • 如果有效:
  • 用请求的内容返回响应
  • 如果无效:
  • 抛出异常(403/401)
  • 在系统中标志入侵
  • 向用户发送警告电子邮件。
  • 更新30.07.15:

    JWT有效负载/声明实际上可以在没有私钥(秘密)的情况下读取,并且将其存储在localStorage中是不安全的。 我对这些虚假陈述感到抱歉。 不过他们似乎正在使用JWE标准(JSON Web Encryption)。

    我通过将声明(userID,exp)存储在JWT中来实现这一点,使用API​​ /后端仅知道的私钥(秘密)对其进行签名,并将其作为安全的HttpOnly cookie存储在客户端上。 这样它就不能通过XSS读取并且不能被操纵,否则智威汤逊签名验证失败。 同样,通过使用安全的HttpOnly cookie,您可以确保cookie仅通过HTTP请求发送(脚本无法访问),并且仅通过安全连接(HTTPS)发送。

    更新17.07.16:

    智威汤逊本质上是无国界的。 这意味着他们使自己失效/失效。 通过在令牌的声明中添加SessionID,可以使其处于有状态,因为它的有效性现在不仅仅取决于签名验证和到期日期,还取决于服务器上的会话状态。 然而,好处是你可以很容易地使令牌/会话失效,而这在以前的无状态JWT中是不可能的。


    我会去第二个令牌系统。

    你知道关于ember-auth或者ember-simple-auth吗? 它们都使用基于标记的系统,如ember-simple-auth状态:

    在Ember.js应用程序中实现基于令牌的身份验证的轻量级和不显眼的库。 http://ember-simple-auth.simplabs.com

    他们有会话管理,并且很容易插入到现有的项目中。

    还有一个Ember App Kit示例版本的ember-simple-auth:使用ember-simple-auth进行OAuth2身份验证的ember-app-kit的工作示例。

    链接地址: http://www.djcxy.com/p/21675.html

    上一篇: SPA best practices for authentication and session management

    下一篇: Why is it common to put CSRF prevention tokens in cookies?