Đặt lại mật khẩu RESTful


107

Cách thích hợp để cấu trúc tài nguyên RESTful để đặt lại mật khẩu là gì?

Tài nguyên này có nghĩa là một công cụ đặt lại mật khẩu cho những người bị mất hoặc quên mật khẩu của họ. Nó làm mất hiệu lực mật khẩu cũ của họ và gửi mật khẩu qua email cho họ.

Hai lựa chọn mà tôi có là:

POST /reset_password/{user_name}

hoặc là...

POST /reset_password
   -Username passed through request body

Tôi khá chắc chắn rằng yêu cầu phải là một BÀI ĐĂNG. Tôi kém tự tin rằng tôi đã chọn một cái tên thích hợp. Và tôi không chắc liệu user_name có được chuyển qua URL hay nội dung yêu cầu hay không.

Câu trả lời:


54

CẬP NHẬT: (thêm bình luận bên dưới)

Tôi sẽ đi cho một cái gì đó như thế này:

POST /users/:user_id/reset_password

Bạn có một tập hợp người dùng, trong đó người dùng duy nhất được chỉ định bởi {user_name}. Sau đó, bạn sẽ chỉ định hành động để hoạt động, trong trường hợp này là reset_password. Nó giống như nói "Tạo ( POST) một reset_passwordhành động mới cho {user_name}".


Câu trả lời trước:

Tôi sẽ đi cho một cái gì đó như thế này:

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

Bạn sẽ có hai bộ sưu tập, một bộ sưu tập người dùng và một bộ sưu tập thuộc tính cho mỗi người dùng. Người dùng được chỉ định bởi :user_idthuộc tính và thuộc tính được chỉ định bởi password. Các PUThoạt động cập nhật các thành viên giải quyết của bộ sưu tập.


10
Tôi đồng ý với giải pháp cập nhật (POST) của bạn. Các yêu cầu PUT nên không có giá trị (nghĩa là các yêu cầu lặp lại không được ảnh hưởng đến kết quả). Đây không phải là trường hợp với các yêu cầu POST.
Ross McFarlane,

16
Tôi sẽ thay đổi reset_password thành password_reset
Richard Knop

9
Cố lên các bạn ... điều này về cơ bản không cho phép BẤT CỨ AI đặt lại mật khẩu của ai đó sao? Như, nếu điều này dành cho ai đó quên mật khẩu hiện tại, người dùng bị ảnh hưởng không thể được xác thực bằng mật khẩu hiện tại. Vì vậy, về cơ bản, điều này có nghĩa là API này không thể chấp nhận bất kỳ mật khẩu nào - do đó cho phép bất kỳ ai đặt lại mật khẩu của ai đó và nếu API trả lại mật khẩu đó, thậm chí có thể nắm giữ bất kỳ mật khẩu nào của người dùng đã biết ??? Hoặc tôi thiếu một cái gì đó
transient_loop

39
Vấn đề với / user / {id} / password và tương tự là bạn có thể không biết "id" của người dùng. Bạn sẽ biết "tên người dùng" hoặc "email" hoặc "điện thoại" của họ, nhưng không biết "id".
coolaj86

17
Lỗ hổng cơ bản của cách tiếp cận này là nó giả định rằng bạn đã biết id người dùng. Điều này sẽ đúng trong một số trường hợp nhưng làm thế nào để bạn làm điều đó khi tên người dùng hoặc id người dùng không được biết nếu người dùng chỉ cần chỉ định một email để thiết lập lại.
Alappin

94

Người dùng chưa được xác thực

Chúng tôi thực hiện PUTyêu cầu trên một api/v1/account/passwordđiểm cuối và yêu cầu một tham số với email tài khoản tương ứng để xác định tài khoản mà người dùng muốn đặt lại (cập nhật) mật khẩu:

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

Lưu ý: Như @DougDomeny đã đề cập trong nhận xét của mình, việc chuyển email dưới dạng chuỗi truy vấn trong url là một rủi ro bảo mật. Các tham số GET không được tiết lộ khi sử dụng https(và bạn nên luôn sử dụng httpskết nối thích hợp cho các yêu cầu như vậy) nhưng có những rủi ro bảo mật khác liên quan. Bạn có thể đọc thêm về chủ đề này trong bài đăng trên blog này ở đây .

Chuyển email trong phần thân yêu cầu sẽ là một giải pháp thay thế an toàn hơn so với việc chuyển nó dưới dạng tham số GET:

PUT : /api/v1/account/password

Nội dung yêu cầu:

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

Phản hồi có nghĩa là phản hồi 202được chấp nhận :

Yêu cầu đã được chấp nhận để xử lý, nhưng quá trình xử lý vẫn chưa hoàn tất. Yêu cầu cuối cùng có thể được thực hiện hoặc không, vì nó có thể không được phép khi quá trình xử lý thực sự diễn ra. Không có cơ sở nào để gửi lại mã trạng thái từ một hoạt động không đồng bộ như thao tác này.

Người dùng sẽ nhận được email tại email@example.comvà việc xử lý yêu cầu cập nhật phụ thuộc vào các hành động được thực hiện với liên kết từ email.

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

Việc mở liên kết từ email này sẽ chuyển hướng đến biểu mẫu đặt lại mật khẩu trên ứng dụng giao diện người dùng sử dụng mã thông báo đặt lại mật khẩu từ liên kết làm đầu vào cho trường nhập ẩn (mã thông báo là một phần của liên kết dưới dạng chuỗi truy vấn). Một trường nhập khác cho phép người dùng đặt mật khẩu mới. Đầu vào thứ hai để xác nhận mật khẩu mới sẽ được sử dụng để xác thực trên giao diện người dùng (để tránh lỗi chính tả).

Lưu ý: Trong email, chúng tôi cũng có thể đề cập rằng trong trường hợp người dùng không khởi tạo bất kỳ thiết lập lại mật khẩu nào, họ có thể bỏ qua email và tiếp tục sử dụng ứng dụng bình thường với mật khẩu hiện tại của mình

Khi biểu mẫu được gửi với mật khẩu mới và mã thông báo làm đầu vào, quá trình đặt lại mật khẩu sẽ diễn ra. Dữ liệu biểu mẫu sẽ được gửi lại cùng với một PUTyêu cầu nhưng lần này bao gồm cả mã thông báo và chúng tôi sẽ thay thế mật khẩu tài nguyên bằng một giá trị mới:

PUT : /api/v1/account/password

Nội dung yêu cầu:

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

Phản hồi sẽ là phản hồi 204không có nội dung

Máy chủ đã hoàn thành yêu cầu nhưng không cần trả lại phần thân thực thể và có thể muốn trả về thông tin cập nhật. Phản hồi CÓ THỂ bao gồm siêu thông tin mới hoặc cập nhật ở dạng tiêu đề thực thể, nếu có thì NÊN được liên kết với biến thể được yêu cầu.

Người dùng xác thực

Đối với những người dùng đã xác thực muốn thay đổi mật khẩu của họ, PUT yêu cầu có thể được thực hiện ngay lập tức mà không cần email (tài khoản mà chúng tôi đang cập nhật mật khẩu được máy chủ biết). Trong trường hợp đó, biểu mẫu sẽ gửi hai trường:

PUT : /api/v1/account/password

Nội dung yêu cầu:

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

Trong đoạn đầu tiên của bạn, bạn nói PUT nhưng ví dụ bên dưới nói DELETE. Cái nào là chính xác?
jpierson

Điều này làm lộ địa chỉ email trên URL, điều này sẽ an toàn hơn cho dữ liệu JSON.
Doug Domeny

@DougDomeny Có, gửi email dưới dạng dữ liệu json có lẽ sẽ tốt hơn. Tôi đã thêm điều này vào câu trả lời như một tùy chọn thay thế an toàn hơn, nếu không thì giải pháp có thể giống nhau.
Héo

@Wilt: Đây sẽ không phải là một hoạt động PATCH? PUT yêu cầu gửi toàn bộ tài nguyên
10

1
@jitenshah Điểm tốt. Khi viết cái này, tôi nghĩ PUT sẽ tốt hơn, nhưng tôi không nhớ chính xác tại sao. Tôi đồng ý với lý do của bạn rằng bản vá có thể phù hợp hơn cho trường hợp này.
Héo

18

Hãy để uber-RESTful trong một giây. Tại sao không sử dụng hành động DELETE cho mật khẩu để kích hoạt đặt lại? Có lý, phải không? Rốt cuộc, bạn đang loại bỏ một cách hiệu quả mật khẩu hiện có để chuyển sang một mật khẩu khác.

Điều đó có nghĩa là bạn sẽ làm:

DELETE /users/{user_name}/password

Bây giờ, hai lưu ý lớn:

  1. HTTP DELETE được cho là không cần thiết (một từ ưa thích để nói "không có vấn đề gì lớn nếu bạn làm điều đó nhiều lần"). Nếu bạn đang làm những việc thông thường như gửi email "Đặt lại mật khẩu", thì bạn sẽ gặp sự cố. Bạn có thể giải quyết vấn đề này khi gắn thẻ người dùng / mật khẩu bằng cờ boolean "Đang đặt lại". Trên mỗi lần xóa, bạn chọn cờ này; nếu nó chưa được đặt thì bạn có thể đặt lại mật khẩu và gửi email của mình. (Lưu ý rằng có cờ này cũng có thể có các mục đích sử dụng khác.)

  2. Bạn không thể sử dụng HTTP DELETE thông qua một biểu mẫu , vì vậy bạn sẽ phải thực hiện cuộc gọi AJAX và / hoặc chuyển đường hầm DELETE thông qua BÀI ĐĂNG.


9
Ý tưởng thú vị. Tuy nhiên tôi không thấy DELETEphù hợp ở đây. Tôi đoán rằng bạn sẽ thay thế mật khẩu bằng một mật khẩu được tạo ngẫu nhiên, vì vậy DELETEcó thể gây hiểu lầm. Tôi thích hơn Create (POST) new reset_password action, trong đó danh từ (tài nguyên) bạn đang hành động là "hành động reset_password". Điều này cũng phù hợp để gửi email, vì hành động này bao gồm tất cả các chi tiết cấp thấp hơn này. POSTkhông phải là không cố định.
Daniel Vassallo

Tôi thích đề xuất. Vấn đề 1 có thể được xử lý bằng cách sử dụng các yêu cầu có điều kiện, tức là HEAD gửi tiêu đề ETag + DELETE và If-Match. Nếu ai đó cố gắng để xóa một mật khẩu mà không còn hoạt động nữa, ông sẽ nhận được một 412.
whiskeysierra

1
Tôi sẽ tránh XÓA. Bạn đang cập nhật, vì cùng một thực thể / khái niệm sẽ nhận được một giá trị mới. Nhưng trên thực tế, thông thường nó thậm chí không xảy ra ngay bây giờ, mà chỉ sau khi gửi mật khẩu mới trong một yêu cầu khác sau đó (sau thư đặt lại mật khẩu) - Ngày nay không ai gửi mật khẩu mới qua thư, mà là một mã thông báo để đặt lại nó trong một yêu cầu mới với một mã thông báo đã cho, phải không?
antonio.fornie.

11
Điều gì sẽ xảy ra nếu người dùng nhớ mật khẩu của mình ngay sau khi thực hiện yêu cầu đặt lại? Điều gì về một số bot đang cố gắng thiết lập lại các tài khoản ngẫu nhiên? Người dùng phải được phép bỏ qua email đặt lại trong trường hợp này (email phải nói như vậy), có nghĩa là hệ thống của bạn không nên tự xóa hoặc cập nhật mật khẩu.
Maxime Laval

3
@MaximeLaval Đó là một điểm rất tốt. Thực sự, bạn đang "Tạo Yêu cầu Đặt lại", đây sẽ là một BÀI ĐĂNG.
Craig Walker

12

Thông thường, bạn không muốn xóa hoặc hủy mật khẩu hiện có của người dùng theo yêu cầu ban đầu, vì điều này có thể đã được kích hoạt (vô tình hoặc cố ý) bởi người dùng không có quyền truy cập vào email. Thay vào đó, hãy cập nhật mã thông báo đặt lại mật khẩu trên hồ sơ người dùng và gửi mã đó trong một liên kết có trong email. Nhấp vào liên kết sẽ xác nhận người dùng đã nhận được mã thông báo và muốn cập nhật mật khẩu của họ. Tốt nhất, điều này cũng sẽ nhạy cảm với thời gian.

Hành động RESTful trong trường hợp này sẽ là POST: kích hoạt hành động tạo trên bộ điều khiển PasswordResets. Hành động tự nó sẽ cập nhật mã thông báo và gửi email.


9

Tôi thực sự đang tìm câu trả lời, không có nghĩa là cung cấp một câu trả lời - nhưng "reset_password" nghe có vẻ sai đối với tôi trong ngữ cảnh REST vì đó là một động từ, không phải một danh từ. Ngay cả khi bạn nói rằng bạn đang thực hiện một danh từ "đặt lại hành động" - sử dụng cách biện minh này, tất cả các động từ đều là danh từ.

Ngoài ra, có thể không xảy ra với ai đó đang tìm kiếm câu trả lời tương tự rằng bạn có thể lấy tên người dùng thông qua ngữ cảnh bảo mật và không phải gửi nó qua url hoặc nội dung, điều này khiến tôi lo lắng.


4
Có lẽ reset-passwordnghe giống như một động từ, nhưng bạn có thể dễ dàng đảo ngược nó ( password-reset) để biến nó thành danh từ. Và nếu bạn đã lập mô hình ứng dụng của mình bằng Nguồn cung cấp sự kiện hoặc ngay cả khi bạn chỉ có bất kỳ loại kiểm tra nào, thì điều đó có nghĩa là bạn thực sự có một thực thể thực với tên này và thậm chí có thể cho phép GET trên đó cho người dùng hoặc quản trị viên xem lịch sử (rõ ràng là che văn bản mật khẩu). Không làm tôi lo lắng chút nào. Và đối với việc chọn tên người dùng tự động ở phía máy chủ - bạn có thể, nhưng sau đó bạn xử lý những việc như quản trị / mạo danh như thế nào?
Aaronaught

1
Không có gì sai khi sử dụng động từ trong REST. Miễn là nó được sử dụng ở những nơi thích hợp. Tôi nghĩ rằng đây là một bộ điều khiển hơn là một pháp sư, và reset-passwordquản lý để mô tả tốt các hiệu ứng của nó.
Anders Östman

6

Tôi nghĩ ý tưởng tốt hơn sẽ là:

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)

Về việc cung cấp dữ liệu:

  • Để đặt lại mật khẩu hiện tại

    • email nên được cung cấp trong nội dung.
  • Để tạo mật khẩu mới (sau khi đặt lại)

    • mật khẩu mới, mã kích hoạt và ID email phải được cung cấp trong nội dung.
  • Để cập nhật mật khẩu (cho người dùng đã đăng nhập)

    • mật khẩu cũ, mật khẩu mới nên được đề cập trong nội dung.
    • UserId trong Params.
    • Mã xác thực trong tiêu đề.

2
Như đã nhận xét trong các câu trả lời khác, "DELETE / api / v1 / account / password" là một ý tưởng tồi, vì bất kỳ ai cũng có thể đặt lại mật khẩu của bất kỳ ai.
tối đa

Chúng tôi cần một ID email đã đăng ký để đặt lại mật khẩu. Cơ hội biết được ID email của người dùng không xác định là rất khó trừ khi chúng ta đang chạy một trang web như Facebook và có hàng tấn ID email được thu thập thông qua bất kỳ phương tiện nào. Sau đó, các chính sách bảo mật sẽ được xác định tương ứng. Đề xuất của bạn để đặt lại mật khẩu cho ai đó là gì?
giải mã

5

Có một số cân nhắc cần thực hiện:

Đặt lại mật khẩu không phải là công cụ quan trọng

Thay đổi mật khẩu ảnh hưởng đến dữ liệu được sử dụng làm thông tin xác thực để thực hiện nó, do đó có thể làm mất hiệu lực các lần thử trong tương lai nếu yêu cầu chỉ được lặp lại nguyên văn trong khi thông tin đăng nhập được lưu trữ đã thay đổi. Ví dụ: nếu mã thông báo đặt lại tạm thời được sử dụng để cho phép thay đổi, theo thông lệ trong tình huống quên mật khẩu, mã thông báo đó sẽ hết hạn sau khi thay đổi mật khẩu thành công, điều này lại vô hiệu hóa các nỗ lực sao chép yêu cầu tiếp theo. Do đó, một cách tiếp cận RESTful để thay đổi mật khẩu dường như là một công việc phù hợp POSThơnPUT .

ID hoặc e-mail trong tải dữ liệu có thể là dư thừa

Mặc dù điều đó không chống lại REST và có thể có một số mục đích đặc biệt, nhưng thường không cần thiết phải chỉ định ID hoặc địa chỉ email để đặt lại mật khẩu. Hãy thử nghĩ xem, tại sao bạn lại cung cấp địa chỉ email như một phần dữ liệu cho một yêu cầu được cho là phải thông qua xác thực theo cách này hay cách khác? Nếu người dùng chỉ đơn giản là thay đổi mật khẩu của họ, họ cần xác thực để làm như vậy (thông qua tên người dùng: mật khẩu, email: mật khẩu hoặc mã thông báo truy cập được cung cấp qua tiêu đề). Do đó, chúng tôi có quyền truy cập vào tài khoản của họ từ bước đó. Nếu họ quên mật khẩu, họ sẽ được cung cấp mã thông báo đặt lại tạm thời (qua email) mà họ có thể sử dụng cụ thể làm thông tin đăng nhập để thực hiện thay đổi. Và trong trường hợp này, xác thực thông qua mã thông báo sẽ đủ để xác định tài khoản của họ.

Xem xét tất cả những điều ở trên, đây là những gì tôi tin là kế hoạch thích hợp để thay đổi mật khẩu RESTful:

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

Tuyên bố rằng trình giữ chỗ yêu cầu thông tin ngoài dải là không hoàn toàn đúng. Một loại phương tiện đặc biệt có thể mô tả cú pháp và ngữ nghĩa của các phần tử nhất định của một yêu cầu hoặc phản hồi. Do đó, một loại phương tiện có thể xác định rằng một URI chứa trong một trường nhất định có thể xác định trình giữ chỗ cho dữ liệu nhất định và ngữ nghĩa xác định thêm rằng một email người dùng được mã hóa hoặc những gì không nên được đưa vào thay vì trình giữ chỗ. Máy khách và máy chủ tôn trọng loại phương tiện đó sẽ vẫn tuân thủ các nguyên tắc kiến ​​trúc RESTful.
Roman Vottner

1
Liên quan đến POSTso với PUT RFC 7231 chỉ định rằng bản cập nhật một phần có thể đạt được thông qua dữ liệu chồng chéo của hai tài nguyên nhưng có vấn đề nếu điều gì đó tương tự /v1/account/passwordthực sự tạo ra một tài nguyên tốt. Cũng như POSTphương pháp swiss-army-precision của Web có thể được sử dụng nếu không có phương pháp nào khác khả thi, việc cân nhắc PATCHcũng có thể là một lựa chọn để đặt mật khẩu mới.
Roman Vottner

Còn URL để yêu cầu đặt lại mật khẩu khi họ không biết mật khẩu của mình thì sao?
dan carter

2

Tôi sẽ không có thứ gì đó thay đổi mật khẩu và gửi cho họ một mật khẩu mới nếu bạn quyết định sử dụng phương thức / users / {id} / password và kiên định với ý tưởng của bạn rằng yêu cầu là tài nguyên của riêng nó. tức là / user-password-request / là tài nguyên, và đang sử dụng PUT, thông tin người dùng phải nằm trong phần nội dung. Mặc dù vậy, tôi sẽ không thay đổi mật khẩu, tôi sẽ gửi email cho người dùng chứa liên kết đến trang có chứa request_guid, có thể được chuyển cùng với yêu cầu đến POST / user / {id} / password /? Request_guid = xxxxx

Điều đó sẽ thay đổi mật khẩu và nó không cho phép ai đó vòi vĩnh người dùng bằng cách yêu cầu thay đổi mật khẩu.

Cộng với PUT ban đầu có thể không thành công nếu có một yêu cầu chưa thực hiện.


0

Chúng tôi Cập nhật người dùng đã đăng nhập Mật khẩu PUT / v1 / users / password - xác định id người dùng bằng AccessToken.

Việc trao đổi id người dùng không an toàn. API Restful phải xác định người dùng sử dụng AccessToken nhận được trong tiêu đề HTTP.

Ví dụ trong Spring-boot

@putMapping(value="/v1/users/password")
public ResponseEntity<String> updatePassword(@RequestHeader(value="Authorization") String token){
/* find User Using token */
/* Update Password*?
/* Return 200 */
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.