如何使用REST API进行身份验证? (浏览器+本机客户端)

我正在使用Rails构建一个Web应用程序。 目前,我正在使用HTTP协议设计Devise,这很容易设置,并且运行良好。

该应用程序由一个提供AJAX Web应用程序的URL组成。 其余的可用URL属于REST API。 所以一切和每一个小数据请求都是通过AJAX完成的。

现在我想扩展整个事情来支持本地客户端。 我读了很多关于无状态身份验证,http基本和摘要身份验证,http会话,cookie,xsrf等的内容......现在我觉得我不能拥有安全的应用程序,因为总有办法劫持它的某些部分。

1 .: HTTP会话比。 无状态身份验证令牌

有什么不同? 我不明白。

  • HTTP会话:

  • 客户端请求一个URL(对服务器的第一个请求)
  • 服务器给出正常的响应加上一些唯一的字符串(==会话ID)
  • 客户端必须发送此字符串与每个请求(这是使用HTTP标头自动完成的)
  • 客户端登录 - >服务器记住这个特定的会话ID现在已经登录
  • 客户端访问需要认证的页面 - >没有特别的事情要做,因为会话ID将自动通过HTTP头部发送到服务器
  • 无状态身份验证令牌:

  • 客户请求URL(对服务器的第一个请求)
  • 服务器只是给出正常的响应, 没有任何密钥或令牌或ID
  • (这里没什么特别的)
  • 客户端登录 - >服务器创建一个认证令牌,并将该令牌发送给响应中的客户端
  • 客户端访问需要身份验证的页面 - >客户端必须提交身份验证令牌
  • 对我来说,这两种方式看起来很相似 使用Rails,我还可以选择将会话存储在数据库中...... Devise会对无状态身份验证令牌执行相同的操作。

    2 .:认证方法

    现在我用{"user":{"email":"e@mail.com","password":"p455w0rd"}}使用POST /users/sign_in

    但是还有其他的可能性,比如HTTP基本认证和HTTP摘要认证,还有像oAuth这样的解决方案(对我来说太大了)。

    从我读过的内容来看:

  • 关于sign_in安全性,当前的POST /users/sign_in和HTTP基本认证之间没有区别。 两者都使用明文。
  • 对于sign_out HTTP基本身份验证有一个缺点:退出只能关闭浏览器窗口
  • HTTP摘要认证有一个巨大的优势:它根本不传输密码(只是密码随机生成的字符串的散列)
  • (德语)维基百科说:所有浏览器都不支持HTTP摘要认证。 也许这个信息是老路?
  • 我需要的:

  • 用户名和哈希密码(bcrypt)存储在数据库中。
  • 用户可以更改他的密码并且密码不会以明文形式发送。 (当涉及用户sign_up时,会发生同样的问题)。 可能的解决方案?
  • 当然:使用SSL / TLS
  • 客户端请求一个want_to_change_password_salt并使用它来加密客户端的密码。 但是 (?!)通过这种方式我发送了散列密码的重要部分加上散列密码。 听起来对我不安全?!
  • 3 .: CSRF令牌

    如上所述,现在我只有一个使用REST API的普通AJAX网站。 它具有XSRF保护:网站通过导轨交付,因此嵌入了XSRF令牌。 我使用AJAX阅读并在进行POST时传输它。 然后,Rails返回请求的数据和一个新的XSRF标记,然后将它用于下一次POST

    现在我想更改我的服务器应用程序以使用本机客户端。 本机客户端不会加载HTML页面,因此不会检索CSRF令牌。 所以我想到了以下选项:

  • 创建一个XSRF令牌REST资源。 因此(本地)客户端必须先从此资源请求XSRF令牌,然后才能执行第一次POST
  • 完全禁用XSRF保护。
  • 问题:

  • XSRF保护如何工作(在Rails中)? 服务器如何知道哪个令牌属于哪个客户端? 我能想到的唯一方法就是会话。 这个假设导致:
  • 如果我为了创建完全无状态的REST API而禁用会话,那么XSRF保护将不再起作用。 对?
  • 4:无状态身份验证令牌

    在这里我主要有很多问题:

  • 它是否具有与HTTP会话相同的安全问题? 我的意思是:窃取会话ID与窃取身份验证令牌的效果相同。 对?
  • 身份验证令牌的过期应与HTTP会话一样工作:服务器必须在某处(数据库和会话)存储时间戳,并检查该时间戳。
  • sign_out也是一样的?
  • 会话:销毁服务器上的会话
  • 身份验证令牌:销毁服务器上的令牌
  • 从我读过的内容来看,应该更安全地将认证令牌存储在HTTP头中(就像会话ID一样),因为服务器日志可以包含GET参数,因此可以包含令牌。
  • 它应该只是一个普通的身份验证令牌吗?或者如果客户端还传输其user_id或甚至散列的密码会更好吗? 我在这里读到客户应该发送:
  • user_id
  • expiration_date
  • [ user_idexpiration_dateSECRET_KEY ]的散列(或HMAC?)。 SECRET_KEY基本上是由服务器生成的一个随机字符串。

  • 对于huuge的帖子感到抱歉,但安全至关重要! 而且我不想犯可能暴露私人数据的设计错误。

    谢谢 :)


    这里有一些新的信息和新的问题;-)

    5 .:本地客户

    就本土客户而言,没有(简单)使用会话的方式:

  • 本地客户端不是浏览器

  • 因此它不会轻易处理cookie(并且没有cookie就没有典型的会话处理)

  • 所以有三种可能的选择:

  • 实施本地客户端的会话处理。 这将如下所示:

  • 登录
  • 读取响应的HTTP标题以获取cookie
  • 在本地保存您需要的所有cookie数据(特别是会话内容)
  • 发送此会话ID与您做的每个请求
  • 根本不要使用会话。 从本地客户的角度来看,它几乎与1:1相同。

  • 登录
  • 从HTTP Header或响应主体获取一些身份验证令牌(这是您的应用,尽管取决于您)
  • 本地保存此令牌
  • 每次请求发送该令牌
  • 混合方法。 这基本上意味着服务器必须区分浏览器和本地客户端,然后检查提供的会话ID和会话数据,或者(对于本机客户端)检查提供的身份验证令牌。

  • 6 .:具有无状态的CSRF令牌(=无会话/无Cookie)授权

    CSRF保护可以保护您的用户免受恶意网站的攻击,该网站尝试以您登录的用户的名义对您的API做出一些请求,但是没有您的用户知道。 使用会话时非常简单:

  • 用户在您的API上登录
  • 会话获取已创建
  • 您的用户浏览器将使用此会话ID设置Cookie
  • 用户执行API所做的每个请求都会自动进行身份验证,因为浏览器会将所有Cookie(包括会话ID)以及每个请求发送到您的API
  • 因此,攻击网站只需执行以下操作:

  • 编写一个指向您的API的自定义HTML <form>
  • 让用户以某种方式点击Submit按钮
  • 当然这个表格会是这样的:

    <form action="http://your.api.com/transferMoney" method="post">
      <input type="hidden" name="receiver" value="ownerOfTheEvilSite" />
      <input type="hidden" name="amount" value="1000.00" />
      <input type="submit" value="WIN MONEY!!" />
    </form>
    

    这导致了以下假设

  • CSRF保护仅仅是因为浏览器自动发送cookie。

  • 本机客户端不需要CSRF保护(当然,您的浏览器无法访问您的本机应用程序的身份验证数据(令牌,Cookie,不管),并且您的本机应用程序不会使用浏览器与API通信)

  • 如果你有一个不使用Cookies来验证用户的API设计,那么不可能做CSRF。 因为攻击者必须知道身份验证令牌并将其与恶意请求一起明确发送。

  • 如果您想对应用程序进行安全保护,您当然可以使用CSRF令牌以及您的无状态身份验证机制,但我非常肯定,没有额外的安全增益。


    7 .:选择正确的HTTP方法

    登录/登录和退出/登出:

    切勿使用GET (至少)三个原因:

  • 在大多数情况下,CSRF保护只保护POST,PUT,PATCH和DELETE,因此CSRF可在用户不知情的情况下使用GET请求登录用户

  • GET请求应该改变应用程序的状态。 但是,即使用会话应用程序状态更改登录/注销,因为会话被创建或销毁。

  • 当使用GET请求并将身份验证信息作为URL参数(例如http://your.api.com/login?username=foo&password=bar )发送时,还有另一个问题:服务器日志! 大多数服务器只是记录每个包含所有URL参数的HTTP请求。 这意味着:如果你的服务器遭到黑客入侵,就不需要破解数据库中的密码哈希,他们只需要查看服务器的日志文件。 另外,恶意管理员还可以读取每个用户的登录信息。 解决方案:

  • 使用POST(或任何你喜欢的方法)并在请求体内发送认证信息。 要么:
  • 在HTTP标头中发送认证信息。 因为这些信息通常不会出现在服务器日志文件中。 要么:
  • 查看服务器配置,并告诉它删除名为“password”的每个URL参数(或者模糊处理,因此URL在日志中变为login?username=foo&password=*** )。 但我建议,简单地使用请求主体和POST方法一起使用这种信息。
  • 所以你可以使用例如:

    POST http://your.api.com/authentication登录

    DELETE http://your.api.com/authentication进行注销


    8 .:密码和散列

    身份验证仅适用于某些密钥。 当然这个密钥应该保密。 意即:

  • 永远不要在数据库中以明文形式存储密码。 有几个库可用来保证安全。 在我看来,最好的选择是bcrypt

  • bcrypt :它已经过优化以哈希密码。 它会自动生成一个salt并多次散列密码(轮次)。 另外,生成的哈希字符串包含所需的所有内容:循环次数,盐和哈希。 虽然你只需要存储这一个字符串,并且不需要手工写任何东西。

  • 当然你也可以使用其他强大的哈希库。 但是对于其中的大多数人来说,你必须实施腌制并自己使用超过1轮。 此外,他们不会像bcrypt那样给你一个单一的字符串,尽管你必须管理自己存储轮次,盐和散列,然后重新组合。

  • 轮次 :这只是密码被散列的频率。 当使用5000轮时,散列函数将返回密码散列哈希散列的散列值。 基本上有这样一个原因:它会消耗CPU的功率! 这意味着:当有人试图强制你的散列时,使用5000轮时需要更长的时间5000倍。 对于你的应用程序本身并不重要:如果用户知道他的密码,他不会识别,如果服务器需要0.0004ms或2ms来验证它。

  • 好的密码 :如果密码太简单,最好的散列函数是无用的。 如果可以使用字典进行破解,使用字典进行破解并不重要:它可能需要几个小时的时间才能完成,但是如果可能需要几个月或几年,几个小时的时间又会是多少? 虽然请确保您的用户密码包含通常的建议(低+大写+数字+特殊字符等)。


  • 9 .:通过电线发送加密密码

    如果您不能(或不想)依赖HTTPS,但不希望在登录时以明文方式发送密码,则可以使用非对称密码术(http://en.wikipedia.org/wiki/Public -key_cryptography)。

    该服务器创建密钥对(公钥和私钥)。 公钥被提供给客户,私钥必须保密!

    客户端现在可以使用公钥对数据进行加密,并且只能由私钥所有者(=服务器)解密此数据。

    这不应该使用(!)将密码存储在数据库中,因为如果您的服务器被黑客入侵,黑客将拥有加密的密码用于解密的私钥。 尽管继续使用一些散列算法(如bcrypt)将密码存储在数据库中。 另一个原因是,如果您认为某人破解了您的加密密码,您可以轻松生成一个新的密钥对。

    HTTPS基本上以相同的方式工作。 但是,如果您的应用程序使用HTTPS(建议使用HTTPS),则在安全性方面可能没有太大好处。 但是如上所述,如果您因任何原因无法使用HTTPS,或者不信任它,那么您就可以构建自己的安全连接。

    请记住,真正的HTTPS连接会加密整个(!)连接和所有数据,而不仅仅是密码数据。 它可以从客户端到服务器端以及服务器端到客户端两种方式进行加密。

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

    上一篇: How to do authentication with a REST API right? (Browser + Native clients)

    下一篇: Is it possible to use Digest