Đặt mật khẩu trong cuộc gọi API REST


31

Giả sử tôi có API REST cũng được sử dụng để đặt / đặt lại mật khẩu. Chúng ta cũng giả sử rằng điều này hoạt động trên các kết nối HTTPS. Có lý do chính đáng nào để không đặt mật khẩu đó vào đường dẫn cuộc gọi không, hãy nói rằng tôi sẽ mã hóa nó trong BASE64?

Một ví dụ sẽ là đặt lại mật khẩu như thế này:

http://www.example.com/user/joe/resetpassword/OLDPASSWD/NEWPASSWD

Tôi hiểu BASE64 không có mã hóa, nhưng tôi chỉ muốn bảo vệ mật khẩu để lướt vai trong trường hợp này.


61
Bạn đang đề xuất tác dụng phụ trên GET? Đó là một vi phạm giao thức ngay tại đó.
Esben Skov Pedersen

27
Đây không thực sự là REST vì resetpassword/OLDPASSWD/NEWPASSWDkhông phải là tài nguyên. Đó là một sự mời gọi của một quá trình. Bạn không cần phải nhét mọi thứ vào một URL.
usr

5
@Esben: ai nói đó là NHẬN? OP không bao giờ nói điều đó.
dagnelies

3
Đúng vậy, anh không hỏi. Nhưng bình luận của anh ấy cho câu trả lời của Netch nói rằng "Đoán xem tôi sẽ phải sử dụng POST sau tất cả", vì vậy chúng tôi có thể cho rằng ban đầu anh ấy dự định / hỏi về GET. Mà, như Esben chỉ ra là A Bad Thing. NHẬN chỉ nên đọc.
Mawg

4
Bài viết sâu sắc này giải thích nhiều cạm bẫy của các cơ chế đặt lại mật khẩu có thể có thể giúp hiểu rõ hơn về trường hợp này.
9000

Câu trả lời:


76

Một máy chủ tốt sẽ ghi lại tất cả các yêu cầu được gửi tới nó, bao gồm URL (thường, không có phần biến sau '?'), IP nguồn, thời gian thực hiện ... Bạn có thực sự muốn nhật ký này (có khả năng được đọc bởi một nhóm quản trị viên) thông tin cực kỳ an toàn như mật khẩu? Base64 không phải là một nút chặn đối với họ.


42
Đây không phải là lý do lớn nhất để sử dụng POST. Đó là một lý do bảo mật. Nhưng như Esben đã lưu ý các ý kiến, việc thay đổi trạng thái bằng GET là vi phạm dịch vụ Nghỉ ngơi như vậy
Pinoniq

2
@BartFriederichs: và lịch sử trình duyệt sẽ ghi nhớ URL. Và thử một loạt mật khẩu ẩn danh bằng cách tạo một trang web có liên kết cho tất cả mật khẩu bạn muốn thử và để Googlebot thực hiện các yêu cầu thực tế ...
RemcoGerlich

2
"Nhưng như Esben đã lưu ý các bình luận, thay đổi trạng thái bằng GET là vi phạm dịch vụ Nghỉ ngơi như vậy" Tôi cũng nhận thấy bình luận đó, nhưng tôi không thấy ai nói rằng đây là yêu cầu NHẬN. Rốt cuộc, bạn có thể nhúng thông tin vào một URI và vẫn POST nó. Tuy nhiên, nó không thực sự RESTful vì URI không thực sự đặt tên tài nguyên.
Joshua Taylor

Xin chào, câu trả lời hay, nhưng tôi không đồng ý với "không có phần biến sau '?'" ... có rất nhiều phần lưu trữ URL đầy đủ !!!
dagnelies 1/12/2015

2
"Thay đổi trạng thái bằng GET là vi phạm dịch vụ Nghỉ ngơi như vậy" - Chúng ta đừng quá bị cuốn vào REST. Thay đổi trạng thái bằng GET là vi phạm HTTP .
Dan Ellis

69

Những gì bạn đang đề xuất là không an toàn cũng không phải RESTful.

@Netch đã đề cập đến vấn đề với nhật ký, nhưng cũng có một vấn đề khác là bạn đang hiển thị mật khẩu được gửi bởi HTTP, khiến việc nắm bắt mật khẩu với bất kỳ loại tấn công sniffer hoặc trung gian nào cũng không đáng kể.

Khi bạn thực hiện một yêu cầu GET bằng REST, các yếu tố khác nhau trong URL đại diện cho các yếu tố hạt mịn hơn. URL của bạn đọc giống như bạn đang trả về một phần NEWPASSWD của OLDPASSWD, đó là một phần của mật khẩu đặt lại. Điều đó không làm cho bất kỳ ý nghĩa ngữ nghĩa. GET không nên được sử dụng để lưu dữ liệu.

Bạn nên làm một cái gì đó như thế này:

POST https://www.example.com/user/joe/resetpassword/
{oldpasswd:[data], newpasswd:[data]}

POST vì bạn đang viết dữ liệu và https vì bạn không muốn nó đánh hơi.

(Đây thực sự là bảo mật thanh thấp. Tối thiểu tuyệt đối bạn nên làm.)


2
Điều này có nghĩa là băm mật khẩu ở phía máy khách? Đây có phải là đề nghị?
Rowan Freeman

1
Lưu ý: điều này sẽ không cho phép thực thi các yêu cầu về mật khẩu (độ dài, v.v.). Đó có thể không phải là một vấn đề trong trường hợp của bạn, mặc dù đó là một thực tiễn bảo mật thường xuyên và đôi khi được yêu cầu bởi một số thực thể.
Paul Draper

8
Bạn không tạo bản ghi mới nhưng đang cập nhật bản ghi hiện có (thông thường), vì vậy đây phải là PUT thay vì POST.

4
Đây không phải là rất RESTful. resetpassword không phải là tài nguyên chứ đừng nói đến tài nguyên phụ. Tuy nhiên, /user/joe/passwordtốt hơn một chút nhưng không tối ưu.
whirlwin

12
@CamilStaps Không, bạn không thể sử dụng PUT, vì PUTlà idempotent. Nhưng khi mật khẩu đã được thay đổi từ secretđể supersecretthành công, sau đó yêu cầu cùng sẽ thất bại lần thứ hai, vì vậy POSTlà đúng ở đây. Tất nhiên, như @whirlwin đã nói, tài nguyên này không được đặt tên tốt.
Residuum

60

Đề án đề xuất có vấn đề trong một số lĩnh vực.

Bảo vệ

Đường dẫn URL thường xuyên được ghi lại; Đặt mật khẩu chưa xóa trong đường dẫn là thực hành kém.

HTTP

Thông tin xác thực / ủy quyền sẽ xuất hiện trong tiêu đề Ủy quyền. Hoặc có khả năng, đối với các công cụ dựa trên trình duyệt, tiêu đề Cookie.

NGHỈ NGƠI

Các động từ như resetpasswordtrong URL của bạn nói chung là một dấu hiệu rõ ràng của mô hình chuyển trạng thái không đại diện. Một URL nên đại diện cho một tài nguyên. Có nghĩa là gì để NHẬN resetpassword? Hoặc XÓA?

API

Đề án này yêu cầu luôn luôn biết mật khẩu trước đó. Bạn có thể sẽ muốn cho phép nhiều trường hợp hơn; ví dụ mật khẩu bị mất


Bạn có thể sử dụng xác thực Cơ bản hoặc Tiêu hóa , đó là các sơ đồ được hiểu rõ.

PUT /user/joe/password HTTP/1.0
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: text/plain
Host: www.example.com

NEWPASSWD

Nó không đưa thông tin cực kỳ nhạy cảm vào đường dẫn và nó tuân theo các quy ước HTTP và REST.

Nếu bạn cần cho phép một số chế độ ủy quyền khác (ví dụ: một số mã thông báo được gửi qua kênh được xác minh để đặt lại mật khẩu), bạn chỉ cần sử dụng tiêu đề Ủy quyền khác mà không phải thay đổi bất kỳ điều gì khác.


4

Ngoài bảo mật, vấn đề với điều này là nó không phải là một cách tiếp cận rất RESTful.

OLDPASSWDNEWPASSWDkhông đại diện cho bất cứ điều gì trong hệ thống phân cấp tài nguyên của bạn và thậm chí tệ hơn, hoạt động không phải là bình thường.

Vì vậy, bạn chỉ có thể sử dụng POSTlàm động từ của mình và bạn không nên bao gồm hai mật khẩu trong đường dẫn tài nguyên của mình.


1
Bạn không tạo bản ghi mới nhưng đang cập nhật bản ghi hiện có (thông thường), vì vậy đây phải là PUT thay vì POST.

2
@CamilStaps Nếu chỉ cần đặt mật khẩu, nó có thể. Nhưng vì có lẽ mật khẩu cũ cũng cần được xác minh, nên nó làm cho hoạt động không bình thường, và do đó PUTbị loại là một động từ. Nó có thể được điều chỉnh để làm việc với PUTnhưng ở dạng hiện tại thì không.
biziclop

OLDPASSWD là thông tin xác thực và hoàn toàn không nên có trong URL.

Không nhất thiết, thông thường là yêu cầu rõ ràng mật khẩu cũ trên đầu xác thực.
biziclop

3

Vấn đề là để tránh mật khẩu văn bản đơn giản trong yêu cầu của bạn. Có hai tùy chọn để đáp ứng các yêu cầu dịch vụ web yên tĩnh.

1. Băm phía khách hàng

  • Tôi đoán bạn đang lưu trữ mật khẩu của mình, ví dụ như hàm băm (mật khẩu + muối)
  • Bạn có thể băm mật khẩu mới với một muối ở phía máy khách
  • Điều đó có nghĩa là: Tạo một muối mới ở phía máy khách, tạo hàm băm, ví dụ: hàm băm (newPassword + newSalt)
  • Gửi hàm băm mới tạo cộng với muối vào dịch vụ web yên tĩnh của bạn
  • Gửi mật khẩu cũ cũng dưới dạng băm (oldPassword + oldSalt)

2. Mã hóa

  • Tạo tài nguyên "khóa một lần" (otk) cho người dùng như / otk / john
  • Tài nguyên này trả về khóa một lần duy nhất ngẫu nhiên an toàn, ví dụ: kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs và ID duy nhất, ví dụ: 9561515
  • Dịch vụ web yên tĩnh của bạn phải lưu trữ otk ngẫu nhiên này để liên lạc an toàn tiếp theo với ID 95636115125
  • Mã hóa mật khẩu mới và cũ của bạn bằng otk, ví dụ AES (vì lý do bảo mật, bạn nên sử dụng hai otks riêng cho mật khẩu cũ và mới)
  • Gửi mật khẩu được mã hóa đến tài nguyên mật khẩu thay đổi của bạn với ID 95636115125
  • Một kết hợp otk và ID chỉ được phép hoạt động một lần, vì vậy bạn phải xóa kết hợp đó sau khi nhập mật khẩu
  • Tùy chọn tốt hơn có thể: Gửi mật khẩu hiện tại / cũ bằng Basic-Auth.

Lưu ý: HTTPS là bắt buộc cho cả hai tùy chọn!


1
Tại sao tôi lại mã hóa kép (sơ đồ mã hóa của bạn với OTK và HTTPS) để trao đổi mật khẩu? Vectơ tấn công ở đây không được HTTPS bao phủ là gì?
Bart Friederichs

1
Mục đích duy nhất là để tránh các bản ghi máy chủ có thể. Một thân yêu cầu HTTP (ví dụ với mật khẩu mới rõ) cũng có thể được ghi lại mặc dù HTTPS được sử dụng. Tấn công khác có thể là việc sử dụng chứng chỉ tự ký đặc biệt được sử dụng cho mục đích nội bộ.
maz258

2

Các tính năng của một hoạt động thiết lập lại mật khẩu là gì?

  1. Nó thay đổi một cái gì đó.
  2. Có một giá trị mà nó được đặt thành.
  3. Chỉ một số người được phép làm điều đó (người dùng, quản trị viên hoặc có thể với các quy tắc khác nhau về cách có thể làm như vậy).

Điểm 1 ở đây có nghĩa là bạn không thể sử dụng GET, bạn phải POST một cái gì đó đại diện cho hoạt động thay đổi mật khẩu thành URI đại diện cho tài nguyên xử lý các thay đổi mật khẩu hoặc PUT một cái gì đó thể hiện mật khẩu mới cho URI đại diện cho mật khẩu hoặc đại diện cho một cái gì đó (ví dụ: người dùng) trong đó mật khẩu là một tính năng.

Nói chung, chúng tôi sẽ ĐĂNG, không chỉ vì nó có thể gây khó xử khi PUT một cái gì đó mà sau này chúng tôi không thể NHẬN được và tất nhiên chúng tôi không thể NHẬN mật khẩu.

Do đó, Điểm 2 sẽ là dữ liệu đại diện cho mật khẩu mới, trong những gì được POST.

Điểm 3 có nghĩa là chúng tôi sẽ cần phải ủy quyền cho yêu cầu, điều đó có nghĩa là nếu người dùng là người dùng hiện tại, chúng tôi sẽ cần chứng minh mật khẩu hiện tại cho chúng tôi (mặc dù không nhất thiết phải nhận mật khẩu hiện tại, ví dụ như thử thách dựa trên hàm băm đã được sử dụng để chứng minh kiến ​​thức về nó mà không gửi nó).

Do đó, URI phải là một cái gì đó như <http://example.net/changeCurrentUserPassword>hoặc <http://example.net/users/joe/changePassword>.

Chúng tôi có thể quyết định rằng chúng tôi muốn nhận mật khẩu hiện tại trong dữ liệu POST cũng như trong cơ chế ủy quyền chung đang được sử dụng.

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.