【问题标题】:Looking for feedback on my REST-style API authentication design and two-factor authentication寻找有关我的 REST 样式 API 身份验证设计和两因素身份验证的反馈
【发布时间】:2013-03-14 19:43:34
【问题描述】:

认证

身份验证将基于签名。将使用以下方式生成签名:

HMAC_SHA256(SHA1(secret_key) + '#' + request_data + '#' + utc_timestamp)

utc_timestamp 也将包含在 X-Timestamp 标头或使用 _timestamp 参数的 URL 中。 request_data 将包含所有参数(URL 和 POST)和 API 相关的标头。所有这些数据都将使用=& 进行小写、排序和连接。

通常,REST API 将支持三种身份验证模式:

  1. API 密钥 - 签名将包括与特定 API 密钥关联的特殊密钥。将使用X-API-Key 标头或_api_key URL 参数和X-API-Signature 标头或_signature URL 参数。

  2. 用户凭据 - 签名将使用密码作为密钥。将使用X-API-Signature 标头或_signature URL 参数和staff_name URL 参数。

  3. Session - 签名将使用用户的密码或特殊生成的代码(下面有更多详细信息)... 将使用 X-Session-Signature 标头或_session_signature URL 参数和X-Session-ID 标头或_session_id URL 参数。

请注意,规范允许同时使用 User credentialsSession(即它们的标头和参数不冲突)。

会话

免责声明:是的,我知道 - 会话不是 RESTful,因为它们是有状态的......但是,我们的产品需要会话来实现某些功能,并且为了简单起见,我们希望维护一个 API/协议 -所以我不想进入 RESTful 辩论。我更愿意认为,会话只是一种资源,您在使用 API 时需要对其进行维护。

会话将只是一个资源:/api/v1/session

所以,标准程序:

  1. 要创建会话,客户端应用程序需要使用 员工凭据 身份验证发送 POST 请求,如上所述:POST /api/v1/session&staff_name=s-andy

  2. 服务器将回复201 Created,并在响应正文中传递会话ID。

  3. 之后客户端应用程序将使用此会话 ID 和员工密码来访问 API。

在收到具有特定会话 ID 的请求后,服务器将更新相应会话资源的最后访问时间。

但我们的用户也可以选择使用双重身份验证。在登录后的用户界面中,这些用户将被要求输入验证码,该验证码将出现在他们的移动设备上。在设计身份验证时,我认为拥有一些特殊的“秘密”而不是会话的用户密码会很棒。然后我恍然大悟——为什么不使用这个验证码

所以双因素身份验证的流程是:

  1. 客户端应用程序使用员工凭据发送 POST 请求。

  2. 服务器发起验证码生成和传递,并返回202 Accepted和会话ID,但会话尚未验证。

  3. 在 30 秒内,客户端应用程序使用 X-Session-ID 标头或_session_id URL 参数中的会话 ID 和 验证码 发送 any 请求> 作为生成签名的秘密。

  4. 收到此类请求后,服务器会更新会话,使其经过验证,并将验证码(也称为一次性密码)保存为该会话的密钥。

  5. 之后客户端应用程序将使用 session id验证码(作为密钥)访问 API。

  6. 当会话超时并因此被删除,或者当用户删除会话(即执行注销)时,会话 ID 和“一次性密码”变得不可用。

我想利用这个专家社区作为这个双因素身份验证想法的传声筒;你能看到我看不到的任何陷阱吗?

【问题讨论】:

    标签: rest


    【解决方案1】:

    问题是你所说的验证码实际上是一个双因素令牌。标记通常很短(例如 6-7 个字符)。

    所以你会这样做:

    HMAC_SHA256(SHA1(TOKEN) + '#' + request_data + '#' + utc_timestamp)
    

    而且令牌太短。

    我也不明白经过验证的 session_id 和未经验证的 session_id 是什么样子的。

    最大的问题是这种身份验证方案看起来过于复杂。复杂性和安全性大多是对立的。

    我们建议客户 (www.authy.com) 进行双因素身份验证的方式是:

    1. 用户使用其凭据发送 POST 请求。

    2. 服务器验证凭据。如果正确,服务器会生成一个 256 位字符串并将其保存到数据库中,然后将其返回给浏览器。我们称这个字符串为“otp”。

    3. 向用户请求他的双因素令牌。

    4. 在第3步的POST请求中会有:

      hidden_​​field otp -> 在第 2 步从服务器返回
      文本字段双因素令牌 -> 该人从他的移动应用程序中获取

    5. 此时在您的服务器上验证双因素令牌和 otp。如果纠正你 创建会话并重置 otp,使其无法再次使用。

    【讨论】:

    • 这就是为什么我们要使用SHA1 作为令牌 - 这会使其更长。
    • 未经验证的会话是等待提交(使用)二元令牌的会话。如果提供了验证码或不需要验证码,则会话将被验证。
    • 实际上,您的步骤 1-4 与我描述的几乎完全相同。不同之处在于我们不会按原样提交双因素令牌,而是将其用于创建签名。同样在这里,您不涉及如何验证会话 - 我们将使用双因素令牌作为创建签名的秘密来做到这一点。
    • 我不介绍如何验证会话的原因是我建议在验证密码和双因素令牌之前不要生成会话。
    • 你不会暴力破解密码的原因是它需要一个 POST 请求,你实际上可以很容易地限制它们,比如最多 3 次尝试。在这种情况下,该人只会生成不同的 cookie 并将它们指向不同的 GET 资源。所以他们可以绕过你的身份验证流程,我很难限制他们的速率。
    【解决方案2】:

    您的验证码听起来很像google two step verification 的工作方式。如果它对 google 帐户足够安全,那么它可能对您有用。

    最大的问题是如何将验证码传递给用户?这是对称密钥加密系统的经典问题。

    另外,你用什么语言实现这个?你有没有找到任何做这些事情的开源解决方案?例如,在 c# 中,ServiceStack Auth 对凭据和会话执行相同类型的操作。

    【讨论】:

    • 验证码大部分会发送到用户的手机。身份验证将在 PHP 中实现。目前我们正在寻找设计,然后我们将考虑实施细节。
    【解决方案3】:

    问问自己是否真的需要使用会话很重要?你要在会话中存储什么?您是否有任何不代表持久化域对象的客户端上下文数据(例如购物车)?如果是这样,RESTful 服务可能不是最佳选择。如果没有,您可以将其存储在客户端上,而不是将客户端上下文存储在服务器上吗?

    考虑到这一点,我会选择选项 2(用户凭据)。很抱歉针对多个问题发布相同的回复,但其中一种方法如下:

    假设您有一个移动客户端应用程序;首先通过在单独的 Web 表单(不是应用程序或 REST 服务的一部分)中提供他们的电子邮件(用户名)和密码来让用户注册或注册。然后在成功注册后,您会使用用户密钥(这可以是存储在服务器(数据库)上的用户帐户中用于对称加密的共享密钥或用于非对称加密的公钥,其中相应的私钥存储在用户帐户中)进行响应在服务器(数据库)上。这一切都是通过 SSL 使用 Web 表单完成的。

    现在,当用户打开客户端应用程序时,您必须向他们询问他们的凭据,这些凭据将随每个对 RESTful 服务的请求一起发送。他们必须提供他们之前收到的姓名、密码和加密密钥。这只需要做一次。然后,该应用会为每个请求提供一些 http 标头,如下所示:

    AUTHENTICATE> 用户名:timestamp:encrypted{password:timestamp} /AUTHENTICATE>

    请注意,{} 中的密码和时间戳都是使用用户密钥加密的。每个请求都会更新时间戳。

    在执行以下操作的服务器上实施身份验证过滤器:

    首先检查时间戳,如果过期(比如早于 1 秒)发送未经授权的 HTTP 响应代码。如果时间戳有效,请在您的用户帐户数据库中查找用户名。如果未找到,则发送 UNAUTHORIZED HTTP 响应。如果找到用户名,则获取该用户的存储加密密钥(请记住,这可以是共享密钥或用户公钥的私钥)。解密加密的 {password:timestamp}。解密后的密码必须与存储在数据库中的用户密码匹配(密码本身也可以使用另一个密钥在数据库中加密以增加安全性),并且解密的时间戳还必须与上面 AUTHENTICATE 标头中发送的非加密时间戳匹配。如果不是,则发送未经授权的 HTTP 响应代码。如果成功,则请求已通过身份验证,而无需使用 cookie/会话。

    您还可以缓存用户详细信息以避免对每个请求进行数据库查找。此外,您可以使用相同的密钥对响应中发送回客户端的任何敏感数据进行加密,并对其进行标记,以便客户端知道对其进行解密。

    现在,如果有人窥探并拦截了请求,他们将无法重新使用它来获得访问权限,因为时间戳将无效,或者如果他们将未加密的时间戳更新为有效,它将与加密的时间戳不匹配时间戳(在身份验证过滤器解密后)。

    与使用单个应用程序密钥相比,此方法的另一个优点是,您现在可以通过在数据库中的用户帐户上设置到期日期来完全控制谁可以访问您的服务(有效地实施基于订阅的服务)。这很棒,因为起初您可能希望通过试用订阅(例如 1 年免费)获得尽可能多的用户,然后如果他们没有支付延长帐户到期日的费用,则稍后阻止对该用户的访问 :)

    【讨论】:

      猜你喜欢
      • 2013-06-06
      • 2016-06-06
      • 2016-02-21
      • 2019-07-21
      • 1970-01-01
      • 2019-07-21
      • 2016-09-09
      • 1970-01-01
      • 2012-01-20
      相关资源
      最近更新 更多