RESTful密码重置


107

构建用于重置密码的RESTful资源的正确方法是什么?

此资源用于丢失或忘记密码的人的密码重置器。它会使他们的旧密码无效,并通过电子邮件向他们发送密码。

我有两个选择:

POST /reset_password/{user_name}

要么...

POST /reset_password
   -Username passed through request body

我很确定请求应该是POST。我不太自信选择了合适的名字。而且我不确定user_name是否应该通过URL或请求正文传递。

Answers:


54

更新:(以下进一步评论)

我会去这样的事情:

POST /users/:user_id/reset_password

您有一个用户集合,其中单个用户由指定{user_name}。然后,您将指定要执行的操作,在这种情况下为reset_password。这就像说“为(创建(POST)一个新reset_password动作){user_name}”。


先前的答案:

我会去这样的事情:

PUT /users/:user_id/attributes/password
    -- The "current password" and the "new password" passed through the body

您将有两个集合,一个用户集合,以及每个用户的一个属性集合。用户由指定,:user_id属性由指定password。该PUT操作将更新集合中已寻址的成员。


10
我同意您更新的(POST)解决方案。PUT请求应该是幂等的(即重复请求不应影响结果)。POST请求不是这种情况。
罗斯·麦克法兰

16
我会将reset_password更改为password_reset
Richard Knop

9
等一下...这实际上是否不允许任何人重设某人的密码?同样,如果这是给忘记当前密码的人的,则无法使用当前密码对受影响的用户进行身份验证。因此,从本质上讲,这意味着该API根本无法接受任何密码-从而使任何人都可以重置某人的密码,并且如果该API返回该密码,甚至可以保留任何已知用户的密码?还是我失去了一些东西
transient_loop

39
/ user / {id} / password之类的问题是您可能不知道用户的“ id”。您会知道他们的“用户名”,“电子邮件”或“电话”,但不知道“ id”。
coolaj86

17
这种方法的根本缺陷在于,它假定您已经知道用户ID。在某些情况下这是正确的,但是如果用户仅需要指定用于重置的电子邮件,那么在不知道用户名或用户ID的情况下该怎么做。
阿拉丁

94

未经身份验证的用户

我们PUTapi/v1/account/password端点上进行请求,并要求使用带有相应帐户电子邮件的参数来标识用户要重置(更新)密码的帐户:

PUT : /api/v1/account/password?email={email@example.com}

注意: 正如@DougDomeny在他的评论中提到的那样,将电子邮件作为URL中的查询字符串传递会带来安全风险。使用时不会暴露GET参数https(您应该始终https对此类请求使用适当的连接),但是还涉及其他安全风险。您可以在此博客文章中阅读有关此主题的更多信息

在请求正文中传递电子邮件将是将其作为GET参数传递的一种更安全的选择:

PUT : /api/v1/account/password

要求正文:

{
    "email": "email@example.com"
}

该响应具有202可接受的响应含义:

该请求已被接受以进行处理,但是处理尚未完成。该请求最终可能会执行,也可能不会最终执行,因为在实际进行处理时可能会不允许该请求。没有从这种异步操作中重新发送状态代码的功能。

用户将在处收到一封电子邮件,email@example.com并根据对电子邮件中的链接所采取的操作来处理更新请求。

https://example.com/password-reset?token=1234567890

打开此电子邮件中的链接将定向到前端应用程序上的重置密码表单,该表单使用链接中的重置密码令牌作为隐藏输入字段的输入(令牌是链接的一部分,作为查询字符串)。另一个输入字段允许用户设置新密码。确认新密码的第二个输入将用于前端的验证(防止输入错误)。

注意: 在电子邮件中,我们还可以提及的是,如果用户未初始化任何密码重置,则他/她可以忽略电子邮件并继续使用当前密码继续使用该应用程序

当使用新密码和令牌作为输入表单时,将执行重置密码过程。表单数据将PUT再次发送请求,但这次包括令牌,我们将用新值替换资源密码:

PUT : /api/v1/account/password

要求正文:

{
    "token":"1234567890",
    "new":"password"
}

响应将为204无内容响应

服务器已完成请求,但不需要返回实体,可能要返回更新的元信息。响应可以包括实体标题形式的新的或更新的元信息,如果存在,则应与所请求的变量相关联。

认证用户

对于想要更改密码的经过身份验证的用户,PUT可以立即执行该请求,而无需发送电子邮件(服务器知道我们要为其更新密码的帐户)。在这种情况下,表单将提交两个字段:

PUT : /api/v1/account/password

要求正文:

{
    "old":"password",
    "new":"password"
}

在第一段中,您说的是PUT,但下面的示例说的是DELETE。哪个准确?
jpierson

这确实在URL上公开了电子邮件地址,这样可以更安全地保存JSON数据。
Doug Domeny '16

@DougDomeny是的,将电子邮件作为json数据发送可能会更好。我将其添加到答案中作为更安全的替代选择,否则解决方案可以相同。
Wilt

@Wilt:这不是PATCH操作吗?PUT需要发送完整的资源
J10

1
@jitenshah好点。撰写本文时,我认为PUT会更好,但我不记得确切的原因。我同意您的观点,认为该修补程序可能更适合这种情况。
威尔特”,

18

让我们获得uber-RESTful一秒钟。为什么不对密码使用DELETE操作来触发重置?有道理,不是吗?毕竟,您实际上是在抛弃现有密码,转而使用另一个密码。

这意味着您可以:

DELETE /users/{user_name}/password

现在,有两个重要警告:

  1. HTTP DELETE应该是幂等的(一个花哨的词,说“如果多次执行,没什么大不了的”)。如果您正在执行标准操作(例如发送“密码重置”电子邮件),那么您将遇到问题。您可以通过使用布尔值“正在重置”标记用户/密码来解决此问题。每次删除时,都检查此标志;如果未设置,可以重置密码并发送电子邮件。(请注意,具有此标志可能还会有其他用途。)

  2. 您不能通过form使用HTTP DELETE,因此您必须进行AJAX调用和/或通过POST隧道化DELETE。


9
有趣的主意。但是我觉得DELETE这里不合适。我猜您将用随机生成的密码代替该密码,因此DELETE可能会引起误解。我更喜欢使用Create (POST) new reset_password action,其中要作用的名词(资源)是“ reset_password操作”。这也适合发送电子邮件,因为该操作封装了所有这些较低级别的详细信息。POST不是幂等的。
丹尼尔·瓦萨洛

我喜欢这个提议。可以通过使用条件请求来处理问题1,即发送ETag + DELETE和If-Match标头的HEAD。如果有人试图删除的密码不再活跃,他将得到412
whiskeysierra

1
我会避免删除。您正在更新,因为同一实体/概念将获得新值。但是实际上,通常现在甚至都没有发生,只有在以后的另一个请求中发送新密码之后(在重置密码邮件之后)-如今,没有人通过邮件发送新密码,而是在新请求中使用以下方式重置密码:给定令牌吧?
antonio.fornie 2015年

11
如果用户在发出重设请求后仍记得自己的密码怎么办?某些尝试重置随机帐户的漫游器怎么办?在这种情况下,应允许用户忽略重设的电子邮件(该电子邮件应如此),这意味着您的系统不应自行删除或更新密码。
Maxime Laval

3
@MaximeLaval这是一个很好的观点。确实,您是在“创建重置请求”,这将是POST。
克雷格·沃克

12

通常,您不想在初始请求上删除或销毁用户的现有密码,因为该密码可能是由无法访问电子邮件的用户(无意或有意)触发的。相反,请更新用户记录上的重置密码令牌,然后通过电子邮件中包含的链接发送该令牌。单击该链接将确认用户已收到令牌并希望更新其密码。理想情况下,这也是时间敏感的。

在这种情况下,RESTful操作将是POST:在PasswordResets控制器上触发create操作。动作本身将更新令牌并发送电子邮件。


9

我实际上是在寻找答案,并不是要提供一个答案-但在REST环境中,“ reset_password”对我来说听起来是错误的,因为它是动词,而不是名词。即使您说您正在执行“重置动作”名词-使用此对齐方式,所有动词都是名词。

同样,有人搜索相同的答案可能不会发生,您可能可以通过安全上下文获取用户名,而根本不必通过url或正文发送用户名,这使我感到紧张。


4
也许reset-password听起来像一个动词,但是你可以很容易地扭转它(password-reset),使之成为名词。而且,如果您使用事件源对应用程序进行建模,或者即使您仅进行了某种形式的审核,也可以说您实际上拥有一个具有此名称的真实实体,甚至可以允许用户或管理员查看GET历史记录(显然会掩盖密码文本)。一点都不让我紧张。至于在服务器端自动获取用户名,您可以,但是您如何处理管理/模拟这样的事情呢?
亚罗诺(Aaronaught)2014年

1
在REST中使用动词没有错。只要在适当的地方使用它。我认为这更多的是控制器而不是资源,并且reset-password设法很好地描述了它的作用。
安德斯·奥斯曼(AndersÖstman)2015年

6

我认为更好的主意是:

DELETE /api/v1/account/password    - To reset the current password (in case user forget the password)
POST   /api/v1/account/password    - To create new password (if user has reset the password)
PUT    /api/v1/account/{userId}/password    - To update the password (if user knows is old password and new password)

关于提供数据:

  • 重设当前密码

    • 电子邮件应在正文中给出。
  • 创建新密码(重置后)

    • 正文中应提供新密码,激活码和emailID。
  • 更新密码(对于已登录用户)

    • 旧密码,正文中应提及新密码。
    • 参数中的UserId。
    • 标头中的身份验证令牌。

2
如其他答案中所述,“ DELETE / api / v1 / account / password”是一个坏主意,因为任何人都可以重设任何人的密码。
maximedupre

我们需要一个注册的电子邮件ID来重置密码。除非我们正在运行一个类似Facebook的网站并且以任何方式收集了大量的电子邮件ID,否则了解未知用户的电子邮件ID的机会非常严峻。然后将相应地定义安全策略。您对重设密码有何建议?
codenooker

5

有一些注意事项:

密码重置不是幂等的

密码更改会影响用作凭据的数据,因此,如果在存储的凭据已更改的情况下简单逐字重复请求,则可能会使以后的尝试无效。例如,如果使用临时重置令牌来允许更改(如在忘记密码的情况下所惯用的那样),则在成功更改密码后该令牌应过期,这再次使复制请求的进一步尝试无效。因此,使用RESTful更改密码的方法似乎POST比做得更好PUT

数据加载中的ID或电子邮件可能是多余的

尽管这与REST无关,并且可能有一些特殊用途,但通常不必指定ID或电子邮件地址来重置密码。想一想,为什么您将电子邮件地址作为数据的一部分提供给应该以某种方式通过身份验证的请求?如果用户只是在更改密码,则他们需要进行身份验证(通过用户名:密码,电子邮件:密码或通过标头提供的访问令牌)。因此,我们可以从该步骤访问他们的帐户。如果他们忘记了密码,则会获得一个临时重置令牌(通过电子邮件),可以将它们专门用作执行更改的凭据。在这种情况下,通过令牌进行身份验证应该足以识别其帐户。

考虑到上述所有因素,以下是我认为是RESTful密码更改的正确方案:

Method: POST
url: /v1/account/password
Access Token (via headers): pwd_rst_token_b3xSw4hR8nKWE1d4iE2s7JawT8bCMsT1EvUQ94aI
data load: {"password": "This 1s My very New Passw0rd"}

占位符需要带外信息的说法并不完全正确。特殊的媒体类型可以描述请求或响应的某些元素的语法和语义。因此,对于媒体类型而言,可以定义某个字段中包含的URI可以定义某些数据的占位符,而语义还可以定义编码的用户电子邮件或不包含占位符的用户电子邮件。尊重该媒体类型的客户端和服务器仍将符合RESTful体系结构原则。
罗曼·沃特纳

1
关于POSTPUT RFC 7231只指定一个部分更新可以通过两种资源重叠的数据来实现,但是这是有问题的,如果是这样/v1/account/password就真的弥补实际上是一个很好的资源。由于POST是,如果没有其他的方法是可行的,可以使用,考虑到网络的瑞士军刀- kniff PATCH也可能被用于设置新密码的选择。
罗曼·沃特纳

如果他们不知道密码,请求密码重设的URL怎么办?
丹·卡特

2

如果您决定使用/ users / {id} / password方法,并且坚持您的想法,即请求是其自身的资源,那么我不会更改密码并向其发送新密码。即/ user-password-request /是资源,并且使用PUT,用户信息应在正文中。不过,我不会更改密码,我会向用户发送一封电子邮件,该电子邮件包含指向包含request_guid的页面的链接,该链接可以与对POST / user / {id} / password /?request_guid =的请求一起传递xxxxxx

这将更改密码,并且不允许某人通过请求更改密码来欺骗用户。

另外,如果有未解决的请求,则初始PUT可能会失败。


0

我们更新记录的用户密码PUT / v1 / users / password-使用AccessToken标识用户ID。

交换用户ID是不安全的。Restful API必须使用HTTP标头中接收的AccessToken标识用户。

spring-boot中的示例

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.