2012-03-19 29 views
106

Tôi đang phát triển API REST yêu cầu xác thực. Bởi vì bản thân xác thực xảy ra thông qua một dịch vụ web bên ngoài qua HTTP, tôi đã lý luận rằng chúng tôi sẽ phân phát các thẻ để tránh lặp đi lặp lại việc gọi dịch vụ xác thực. Điều này mang lại cho tôi gọn gàng với câu hỏi đầu tiên của tôi:Xác thực dựa trên mã thông báo REST API

Điều này có thực sự tốt hơn là yêu cầu khách hàng sử dụng HTTP Basic Auth trên mỗi yêu cầu và lưu vào bộ nhớ cache đến máy chủ dịch vụ xác thực không?

Giải pháp xác thực cơ bản có lợi thế là không yêu cầu toàn bộ chuyến đi khứ hồi tới máy chủ trước khi yêu cầu nội dung có thể bắt đầu. Các thẻ có khả năng linh hoạt hơn trong phạm vi (tức là chỉ cấp quyền đối với các tài nguyên hoặc hành động cụ thể), nhưng điều đó có vẻ phù hợp hơn với ngữ cảnh OAuth so với trường hợp sử dụng đơn giản của tôi.

Hiện nay thẻ được mua như thế này:

curl -X POST localhost/token --data "api_key=81169d80... 
            &verifier=2f5ae51a... 
            &timestamp=1234567 
            &user=foo 
            &pass=bar" 

Các api_key, timestampverifier được yêu cầu của tất cả các yêu cầu. Các "xác minh" được trả về bởi:

sha1(timestamp + api_key + shared_secret) 

Ý định của tôi là để chỉ cho phép các cuộc gọi từ các bên đã biết, và để ngăn chặn các cuộc gọi không bị tái sử dụng đúng nguyên văn.

Điều này có đủ tốt không? Underkill? Overkill?

Với một thẻ trong tay, khách hàng có thể có được các nguồn lực:

curl localhost/posts?api_key=81169d80... 
        &verifier=81169d80... 
        &token=9fUyas64... 
        &timestamp=1234567 

Đối với các cuộc gọi đơn giản nhất có thể, điều này dường như loại khủng khiếp tiết. Xem xét các shared_secret sẽ gió lên được nhúng trong (tối thiểu) một ứng dụng iOS, từ đó tôi sẽ giả sử nó có thể được trích xuất, điều này thậm chí cung cấp bất cứ điều gì ngoài một cảm giác sai về an ninh?

+2

Thay vì sử dụng sha1 (timestamp + API_KEY + shard_secret), bạn nên sử dụng HMAC (shared_secret, timpestamp + API_KEY) cho một bảo mật tốt hơn băm http://en.wikipedia.org/wiki/Hash- based_message_authentication_code –

+0

@ MiguelA.Carrasco Và dường như là sự đồng thuận vào năm 2017 rằng bCrypt là công cụ băm mới. –

Trả lời

87

Hãy để tôi tách lên tất cả mọi thứ và giải quyết các cách tiếp cận từng vấn đề trong sự cô lập:

Xác thực

Đối với chứng thực, baseauth có ưu điểm là nó là một giải pháp trưởng thành trên tầng giao thức. Điều này có nghĩa là rất nhiều sự cố "có thể cắt lên sau" đã được giải quyết cho bạn. Ví dụ, với BaseAuth, các tác nhân người dùng biết mật khẩu là một mật khẩu để họ không lưu trữ nó.

Auth máy chủ tải

Nếu bạn tha cho một mã thông báo cho người sử dụng thay vì bộ nhớ đệm xác thực trên máy chủ của bạn, bạn vẫn làm điều tương tự: Caching thông tin xác thực. Sự khác biệt duy nhất là bạn đang chuyển trách nhiệm cho bộ nhớ đệm cho người dùng. Điều này có vẻ như lao động không cần thiết cho người dùng không có lợi nhuận, vì vậy tôi khuyên bạn nên xử lý minh bạch này trên máy chủ của bạn như bạn đề nghị.

truyền an

Nếu có thể sử dụng một kết nối SSL, đó là tất cả để có nó, kết nối được an toàn *.Để ngăn việc thực thi nhiều lần ngẫu nhiên, bạn có thể lọc nhiều url hoặc yêu cầu người dùng đưa vào một thành phần ngẫu nhiên ("nonce") trong URL.

url = username:[email protected]/api/call/nonce 

Nếu không thể, và thông tin được truyền không bí mật, tôi khuyên bạn nên đảm bảo yêu cầu bằng hàm băm, như bạn đã đề xuất trong phương pháp mã thông báo. Kể từ khi băm cung cấp bảo mật, bạn có thể hướng dẫn người dùng của bạn cung cấp băm làm mật khẩu baseauth. Để cải thiện độ chắc chắn, tôi khuyên bạn nên sử dụng một chuỗi ngẫu nhiên thay vì dấu thời gian như là một "nonce" để ngăn chặn các cuộc tấn công phát lại (hai yêu cầu hợp pháp có thể được thực hiện trong cùng một giây). Thay vì cung cấp riêng biệt "chia sẻ bí mật" và "api chìa khóa" lĩnh vực, bạn chỉ có thể sử dụng phím api như bí mật được chia sẻ, và sau đó sử dụng một muối mà không thay đổi để ngăn chặn các cuộc tấn công bảng cầu vồng. Trường tên người dùng có vẻ như là một nơi tốt để đặt nonce quá, vì nó là một phần của auth. Vì vậy, bây giờ bạn có một cuộc gọi sạch như sau:

nonce = generate_secure_password(length: 16); 
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key); 
url = username:[email protected]/api/call 

Điều này đúng là một chút mất thời gian. Điều này là do bạn không sử dụng giải pháp mức giao thức (như SSL). Vì vậy, có thể là một ý tưởng hay để cung cấp một số loại SDK cho người dùng vì vậy ít nhất họ không phải tự mình trải qua nó. Nếu bạn cần phải làm theo cách này, tôi thấy mức độ bảo mật thích hợp (chỉ cần phải giết).

lưu trữ bí mật an toàn

Nó phụ thuộc những người bạn đang cố gắng để ngăn chặn. Nếu bạn đang ngăn những người có quyền truy cập vào điện thoại của người dùng sử dụng dịch vụ REST của bạn trong tên của người dùng, thì bạn nên tìm một số loại API khóa trên hệ điều hành đích và có SDK (hoặc người triển khai) lưu trữ chìa khóa ở đó. Nếu điều đó là không thể, bạn có thể ít nhất làm cho nó khó hơn một chút để có được bí mật bằng cách mã hóa nó, và lưu trữ dữ liệu được mã hóa và khóa mã hóa ở những nơi riêng biệt.

Nếu bạn đang cố gắng giữ cho các nhà cung cấp phần mềm khác không nhận được khóa API của bạn để ngăn chặn sự phát triển của khách hàng thay thế, chỉ cách tiếp cận mã hóa và lưu trữ riêng biệt gần như hoạt động. Đây là mật mã trắng, và cho đến nay, không ai đã đưa ra một giải pháp thực sự an toàn cho các vấn đề của lớp này. Ít nhất bạn có thể làm là vẫn còn phát hành một khóa duy nhất cho mỗi người dùng để bạn có thể cấm các phím bị lạm dụng.

(*) CHỈNH SỬA:Kết nối SSL should no longer be considered secure mà không cần taking additional steps to verify chúng.

+0

Cảm ơn cmc, tất cả các điểm tốt và thức ăn tuyệt vời cho tư tưởng.Tôi đã kết thúc bằng cách sử dụng một cách tiếp cận token/HMAC tương tự như cách bạn đã thảo luận ở trên, giống như cơ chế xác thực [S3 REST API] (http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html) . – cantlin

+0

Nếu bạn cache mã thông báo trên máy chủ, thì cơ bản nó không giống với id phiên cũ tốt? Id phiên là ngắn ngủi và nó cũng được gắn vào bộ nhớ cache nhanh (nếu bạn thực hiện nó) để tránh nhấn DB của bạn trên mọi yêu cầu. True RESTful và thiết kế không trạng thái không nên có phiên, nhưng nếu bạn đang sử dụng một mã thông báo như một ID và sau đó vẫn nhấn DB, sau đó nó sẽ không tốt hơn chỉ sử dụng ID phiên thay vào đó? Ngoài ra, bạn có thể sử dụng mã thông báo web JSON có chứa thông tin được mã hóa hoặc đã ký cho toàn bộ dữ liệu phiên cho thiết kế không trạng thái thực sự. – JustAMartin

15

Một API RESTful tinh khiết nên sử dụng các tính năng tiêu chuẩn giao thức cơ bản:

  1. Đối với HTTP, API RESTful phải tuân thủ tiêu đề chuẩn HTTP hiện có. Thêm tiêu đề HTTP mới vi phạm nguyên tắc REST. Không tái phát minh ra bánh xe, sử dụng tất cả các tính năng tiêu chuẩn trong tiêu chuẩn HTTP/1.1 - bao gồm mã phản hồi trạng thái, tiêu đề, v.v. Các dịch vụ web RESTFul nên tận dụng và dựa vào các tiêu chuẩn HTTP.

  2. Dịch vụ RESTful PHẢI CÓ STATELESS. Bất kỳ thủ thuật nào, chẳng hạn như xác thực dựa trên mã thông báo cố gắng nhớ trạng thái của các yêu cầu REST trước đó trên máy chủ vi phạm nguyên tắc REST. Một lần nữa, đây là một PHẢI; nghĩa là, nếu máy chủ web lưu bất kỳ thông tin liên quan đến yêu cầu/phản hồi ngữ cảnh nào trên máy chủ để cố gắng thiết lập bất kỳ loại phiên nào trên máy chủ, thì dịch vụ web của bạn KHÔNG PHẢI là Không trạng thái. Và nếu nó KHÔNG PHẢI là không trạng thái thì nó KHÔNG PHẢI là RESTFul.

Bottom-line: Để xác thực/ủy quyền, bạn nên sử dụng tiêu đề ủy quyền chuẩn HTTP. Tức là, bạn nên thêm tiêu đề xác thực/xác thực HTTP trong mỗi yêu cầu tiếp theo cần phải được xác thực. REST API phải tuân thủ các tiêu chuẩn của Lược đồ xác thực HTTP. Các chi tiết cụ thể về cách định dạng tiêu đề này được định nghĩa trong tiêu chuẩn RFC 2616 HTTP 1.1 - mục 14.8 Ủy quyền của RFC 2616 và trong Xác thực HTTP RFC 2617: Xác thực truy cập cơ bản và xác thực .

Tôi đã phát triển một dịch vụ RESTful cho ứng dụng Trình quản lý hiệu suất Prime của Cisco. Tìm kiếm Google cho tài liệu REST API mà tôi đã viết cho ứng dụng đó để biết thêm chi tiết về tuân thủ API RESTFul here. Trong triển khai đó, tôi đã chọn sử dụng lược đồ ủy quyền "Cơ bản" HTTP. - hãy xem phiên bản 1.5 hoặc cao hơn của tài liệu REST API đó và tìm kiếm giấy phép trong tài liệu.

+5

_ "Thêm tiêu đề HTTP mới vi phạm nguyên tắc REST" _ Làm thế nào? Và nếu bạn ở đó, bạn có thể rất tốt để giải thích chính xác sự khác biệt (liên quan đến nguyên tắc) giữa mật khẩu hết hạn sau một khoảng thời gian nhất định và mã thông báo hết hạn sau một khoảng thời gian nhất định. – zeroflagL

+0

Trong web, giao thức trạng thái dựa trên việc tạo mã thông báo được trao đổi giữa trình duyệt và máy chủ (thông qua tiêu đề cookie hoặc viết lại URI) trên mọi yêu cầu. Mã thông báo đó được tạo/duy trì ở phía máy chủ; có nghĩa là, mã thông báo có thời gian tồn tại, nó dành riêng cho thời gian tương tác giữa máy khách và máy chủ, v.v. –

+4

Tên người dùng + mật khẩu là mã thông báo (!) Được trao đổi giữa khách hàng và máy chủ theo mọi yêu cầu. Mã thông báo đó được duy trì trên máy chủ và có thời gian hoạt động. Nếu mật khẩu hết hạn, tôi phải mua lại mật khẩu mới. Bạn dường như liên kết "mã thông báo" với "phiên máy chủ", nhưng đó là một kết luận không hợp lệ. Nó thậm chí không liên quan vì nó sẽ là một chi tiết thực hiện. Việc phân loại các mã thông báo của bạn ngoài tên người dùng/mật khẩu là trạng thái hoàn toàn là nhân tạo, imho. – zeroflagL

2

Trong web, giao thức trạng thái dựa trên việc có mã thông báo tạm thời được trao đổi giữa trình duyệt và máy chủ (thông qua tiêu đề cookie hoặc viết lại URI) trên mọi yêu cầu. Mã thông báo đó thường được tạo trên đầu máy chủ và đó là một phần dữ liệu mờ có thời gian nhất định và nó có mục đích duy nhất là xác định tác nhân người dùng web cụ thể. Tức là, mã thông báo là tạm thời và trở thành STATE mà máy chủ web phải duy trì thay mặt cho tác nhân người dùng ứng dụng khách trong suốt thời gian của cuộc trò chuyện đó. Do đó, việc giao tiếp bằng cách sử dụng một mã thông báo theo cách này là STATEFUL. Và nếu cuộc trò chuyện giữa máy khách và máy chủ là STATEFUL thì nó không phải là RESTful.

Tên người dùng/mật khẩu (được gửi trên tiêu đề Cấp quyền) thường được lưu giữ trên cơ sở dữ liệu với mục đích xác định người dùng. Đôi khi người dùng có thể có nghĩa là một ứng dụng khác; tuy nhiên, tên người dùng/mật khẩu là KHÔNG BAO GIỜ nhằm xác định một tác nhân người dùng ứng dụng web cụ thể. Cuộc hội thoại giữa tác nhân web và máy chủ dựa trên việc sử dụng tên người dùng/mật khẩu trong tiêu đề Cấp phép (sau Ủy quyền HTTP cơ bản) là STATELESS vì giao diện người dùng máy chủ web không tạo hoặc duy trì bất kỳ thông tin STATE nào thay mặt cho đại lý người dùng máy khách web cụ thể. Và dựa trên sự hiểu biết của tôi về REST, giao thức nêu rõ rằng cuộc trò chuyện giữa khách hàng và máy chủ phải là STATELESS. Do đó, nếu chúng ta muốn có một dịch vụ RESTful thực, chúng ta nên sử dụng username/password (tham khảo RFC được đề cập trong bài trước) trong tiêu đề Authorization cho mỗi cuộc gọi, KHÔNG phải là một loại mã thông báo (ví dụ: Mã thông báo Session được tạo trong máy chủ web , Mã thông báo OAuth được tạo trong máy chủ ủy quyền, v.v.).

Tôi hiểu rằng một số nhà cung cấp REST được gọi là sử dụng mã thông báo như mã nhận dạng OAuth1 hoặc OAuth2 sẽ được chuyển thành "Ủy quyền: Bearer" trong tiêu đề HTTP. Tuy nhiên, có vẻ như với tôi rằng việc sử dụng các thẻ đó cho các dịch vụ RESTful sẽ vi phạm ý nghĩa STATELESS thực sự mà REST nắm lấy; vì các mã đó là tạm thời phần dữ liệu được tạo/duy trì ở phía máy chủ để xác định một tác nhân người dùng ứng dụng web cụ thể trong khoảng thời gian hợp lệ của cuộc trò chuyện web/máy chủ web đó. Do đó, bất kỳ dịch vụ nào đang sử dụng các mã thông báo OAuth1/2 này sẽ không được gọi là REST nếu chúng ta muốn tuân theo nghĩa TRUE của giao thức STATELESS.

Rubens

Các vấn đề liên quan