Dịch vụ web RESTful - làm thế nào để xác thực các yêu cầu từ các dịch vụ khác?


117

Tôi đang thiết kế một dịch vụ web RESTful cần được người dùng truy cập, mà còn các ứng dụng và dịch vụ web khác. Tất cả các yêu cầu đến cần phải được xác thực. Tất cả các giao tiếp diễn ra trên HTTPS. Xác thực người dùng sẽ hoạt động dựa trên mã thông báo xác thực, có được bằng cách ĐĂNG tên người dùng và mật khẩu (qua kết nối SSL) với tài nguyên / phiên do dịch vụ cung cấp.

Trong trường hợp khách hàng sử dụng dịch vụ web, không có người dùng cuối đằng sau dịch vụ khách hàng. Các yêu cầu được bắt đầu bởi các tác vụ theo lịch trình, sự kiện hoặc một số thao tác máy tính khác. Danh sách các dịch vụ kết nối được biết trước (rõ ràng, tôi đoán vậy). Làm cách nào để xác thực những yêu cầu này đến từ các dịch vụ (web) khác? Tôi muốn quá trình xác thực dễ dàng nhất có thể để thực hiện cho các dịch vụ đó, nhưng không phải trả giá bằng bảo mật. Điều gì sẽ là tiêu chuẩn và thực tiễn tốt nhất cho một kịch bản như thế này?

Các tùy chọn mà tôi có thể nghĩ ra (hoặc đã được đề xuất cho tôi):

  1. Yêu cầu các dịch vụ khách hàng sử dụng tên người dùng và mật khẩu "giả" và xác thực chúng giống như người dùng. Tôi không thích tùy chọn này - nó không cảm thấy đúng.

  2. Chỉ định id ứng dụng vĩnh viễn cho dịch vụ khách hàng, có thể là khóa ứng dụng. Theo như tôi đã hiểu thì điều này cũng giống như có tên người dùng + mật khẩu. Với id và khóa này, tôi có thể xác thực từng yêu cầu hoặc tạo mã thông báo xác thực để xác thực các yêu cầu tiếp theo. Dù bằng cách nào, tôi không thích tùy chọn này, bởi vì bất kỳ ai có thể nắm giữ id ứng dụng và khóa đều có thể mạo danh khách hàng.

  3. Tôi có thể thêm một kiểm tra địa chỉ IP vào tùy chọn trước đó. Điều này sẽ làm cho việc thực hiện các yêu cầu giả mạo trở nên khó khăn hơn.

  4. Giấy chứng nhận khách hàng. Thiết lập quyền chứng chỉ của riêng tôi, tạo chứng chỉ gốc và tạo chứng chỉ ứng dụng khách cho các dịch vụ khách. Tuy nhiên, có một số vấn đề xuất hiện: a) làm thế nào để tôi vẫn cho phép người dùng xác thực mà không cần chứng chỉ và b) kịch bản này phức tạp đến mức nào khi thực hiện theo quan điểm dịch vụ khách hàng?

  5. Một cái gì đó khác - phải có giải pháp khác ngoài đó?

Dịch vụ của tôi sẽ chạy trên Java, nhưng tôi cố tình bỏ qua thông tin về việc xây dựng khung cụ thể nào, bởi vì tôi quan tâm nhiều hơn đến các nguyên tắc cơ bản và không quan tâm nhiều đến các chi tiết triển khai - Tôi giả sử giải pháp tốt nhất cho việc này sẽ có thể thực hiện bất kể khuôn khổ cơ bản. Tuy nhiên, tôi hơi thiếu kinh nghiệm với chủ đề này, vì vậy những lời khuyên và ví dụ cụ thể về việc triển khai thực tế (như thư viện, bài viết hữu ích của bên thứ ba, v.v.) cũng sẽ được đánh giá cao.


Nếu tôi có thể đề xuất, hãy làm quen với các dịch vụ trang web lớn và chọn và chọn những gì bạn thích. Người dùng của bạn cũng sẽ thấy có những điểm tương đồng với các thực tiễn tốt nhất của các dịch vụ RESTful khác.
Yzmir Ramirez

Tìm thấy một câu hỏi khác (gần hai tuổi) chạm vào một chủ đề tương tự: stackoverflow.com/questions/1138831/
mẹo

Hệ điều hành nào là dịch vụ (cả web và các dịch vụ khác) được lưu trữ trên? Có phải họ đang chạy trên các máy chủ là một phần của cùng một cơ sở hạ tầng?
Anders Abel

Hệ điều hành có thể khác nhau: Win, * nix, v.v. Và các dịch vụ khách hàng có thể có hoặc không nằm trong cùng cơ sở hạ tầng với dịch vụ của tôi.
Tommi

Câu trả lời:


34

Bất kỳ giải pháp cho vấn đề này đều nắm bắt được một bí mật chung. Tôi cũng không thích tùy chọn tên người dùng và mật khẩu được mã hóa cứng nhưng nó có lợi ích là khá đơn giản. Chứng chỉ khách hàng cũng tốt nhưng nó có thực sự khác biệt nhiều không? Có một chứng chỉ trên máy chủ và một trên máy khách. Ưu điểm chính của nó là khó vũ lực hơn. Hy vọng rằng bạn đã có các biện pháp bảo vệ khác để bảo vệ chống lại điều đó.

Tôi không nghĩ rằng điểm A của bạn đối với giải pháp chứng chỉ ứng dụng khách khó giải quyết. Bạn chỉ cần sử dụng một chi nhánh. if (client side certificat) { check it } else { http basic auth }Tôi không phải là chuyên gia java và tôi chưa bao giờ làm việc với nó để làm chứng chỉ phía máy khách. Tuy nhiên, Google nhanh chóng dẫn chúng tôi đến hướng dẫn này có vẻ đúng với con hẻm của bạn.

Bất chấp tất cả các cuộc thảo luận "tốt nhất" này, hãy để tôi chỉ ra rằng có một triết lý khác nói rằng "ít mã hơn, kém thông minh hơn là tốt hơn". (Cá nhân tôi giữ triết lý này). Các giải pháp chứng chỉ ứng dụng khách có vẻ như rất nhiều mã.

Tôi biết bạn đã bày tỏ câu hỏi về OAuth, nhưng đề xuất OAuth2 bao gồm một giải pháp cho vấn đề của bạn được gọi là " mã thông báo mang " phải được sử dụng cùng với SSL. Tôi nghĩ, để đơn giản, tôi sẽ chọn người dùng / pass được mã hóa cứng (một ứng dụng cho mỗi ứng dụng để chúng có thể bị thu hồi riêng lẻ) hoặc mã thông báo mang rất giống nhau.


27
Chứng chỉ khách hàng KHÔNG phải là một bí mật chung. Đó là lý do tại sao chúng tồn tại. Máy khách có khóa riêng và máy chủ có khóa chung. Khách hàng không bao giờ chia sẻ bí mật của mình và khóa chung không phải là bí mật.
Tim

5
Liên kết hướng dẫn không dẫn đến một bài viết hướng dẫn mà đến một số trang chỉ mục Java trên trang web của Oracle ...
Marjan Venema

2
@MarjanVenema Chà, đó là vì bạn đang thử liên kết +2 năm sau khi newz2000 trả lời, nhưng bạn luôn có thể dùng thử WayBack Machine: web.archive.org/web/20110826004236/http://java.sun.com/ tựa
Fábio Duque Silva

1
@MarjanVenema: Tôi xin lỗi, nhưng bạn đang mong đợi newz2000 đến đây và cập nhật liên kết sau khi nó chết? Giống như bạn đã nói, đó là liên kết thối, vì vậy nó xảy ra sớm hay muộn. Hoặc bạn cố gắng truy cập vào kho lưu trữ để xem những gì tác giả đã nhìn thấy tại thời điểm đó, hoặc bạn tìm thấy liên kết mới và đóng góp tích cực. Tôi không thấy nhận xét của bạn đã giúp bất cứ ai. Nhưng ở đây, hãy theo liên kết sau: oracle.com/technetwork/articles/javase/, (hãy cẩn thận rằng cuối cùng nó cũng sẽ bị thối rữa)
Fábio Duque Silva

2
@ FábioSilva: Không. Tôi không mong đợi anh ta làm điều đó. Tôi đã đọc kho lưu trữ nhưng tôi không có thời gian để tìm kiếm một liên kết mới, vì vậy tôi đã làm điều tốt nhất tiếp theo: đưa ra một nhận xét rằng nó đã chết để người khác trong cộng đồng có thể tìm vị trí mới và cập nhật bài đăng. Vì bạn rõ ràng đã có thời gian để tìm liên kết mới, tại sao bạn không cập nhật liên kết trong bài thay vì đưa nó vào một bình luận bám chặt lấy tôi?
Marjan Venema

36

Sau khi đọc câu hỏi của bạn, tôi sẽ nói, tạo mã thông báo đặc biệt để thực hiện yêu cầu. Mã thông báo này sẽ tồn tại trong thời gian cụ thể (giả sử trong một ngày).

Dưới đây là một ví dụ từ để tạo mã thông báo xác thực:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

ví dụ: ngày 3 tháng 6 năm 2011

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

sau đó nối với mật khẩu người dùng, ví dụ "my4wgieP4ssword!"

11630my4wesomeP4ssword!

Sau đó thực hiện MD5 của chuỗi đó:

05a9d022d621b64096160683f3afe804

Khi nào bạn gọi một yêu cầu, luôn sử dụng mã thông báo này,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

Mã thông báo này luôn là duy nhất mỗi ngày, vì vậy tôi đoán loại bảo vệ này là quá đủ để luôn bảo vệ dịch vụ của bạn.

Hy vọng giúp

:)


1
Tôi thực sự thích cách bạn nối thêm mã thông báo bảo mật trong mọi yêu cầu nhưng điều gì xảy ra với điều này khi lập trình viên đã tạo 100 trang jsp và sau đó t0 thực hiện bảo mật trong 100 trang được tạo trước đó cũng như các trang sẽ được tạo. Trong trường hợp đó, việc thêm mã thông báo vào mỗi yêu cầu không phải là một lựa chọn chính xác. Dù sao +1 cho kỹ thuật của bạn. :)
Ankur Verma

4
Điều gì xảy ra nếu đồng hồ không được đồng bộ hóa? Trong trường hợp này, khách hàng có tạo mã thông báo sai không? Ngay cả khi cả hai tạo ra datetime trong UTC, đồng hồ của họ vẫn có thể khác nhau dẫn đến Cửa sổ thời gian mỗi ngày, khi mã thông báo không hoạt động?
NickG

@NickG, tôi đã gặp vấn đề này trước đây, cách duy nhất để bảo mật vấn đề này bằng cách yêu cầu thời gian của máy chủ. Điều này sẽ giết chết 99% vấn đề của UTC. Tất nhiên nhược điểm là thêm một cuộc gọi đến máy chủ.
kororo

nhưng tôi vẫn có thể sử dụng dịch vụ web của bạn bằng cách sử dụng mã thông báo này trong một ngày phải không? tôi không thấy điều này sẽ giúp ích như thế nào
Mina Gabriel

@MinaGabriel bạn có thể thêm nhiều khung thời gian hơn trong việc tạo mã thông báo. (phút * 10) + (giờ * 100) + (ngày * 1000) + (tháng * 10000) + (năm (2 chữ số cuối) * 100000)
kororo

11

Có một số cách tiếp cận khác nhau mà bạn có thể thực hiện.

  1. Những người theo chủ nghĩa RESTful sẽ muốn bạn sử dụng xác thực BASIC và gửi thông tin đăng nhập cho mỗi yêu cầu. Lý do của họ là không ai được lưu trữ bất kỳ nhà nước.

  2. Dịch vụ khách hàng có thể lưu trữ cookie, duy trì ID phiên. Cá nhân tôi không thấy điều này gây khó chịu như một số người theo chủ nghĩa thuần túy mà tôi nghe thấy - việc xác thực hết lần này đến lần khác có thể tốn kém. Dường như bạn không quá thích ý tưởng này.

  3. Từ mô tả của bạn, có vẻ như bạn có thể quan tâm đến OAuth2 Trải nghiệm của tôi cho đến nay, từ những gì tôi đã thấy, đó là loại khó hiểu và loại cạnh chảy máu. Có những triển khai ngoài kia, nhưng chúng rất ít. Trong Java, tôi hiểu rằng nó đã được tích hợp vào các mô-đun bảo mật của Spring3 . ( Hướng dẫn của họ được viết rất hay.) Tôi đã chờ xem liệu sẽ có phần mở rộng trong Restlet hay không , nhưng cho đến nay, mặc dù nó đã được đề xuất và có thể nằm trong lồng ấp, nhưng nó vẫn chưa được kết hợp đầy đủ.


Tôi không có gì chống lại tùy chọn 2 - Tôi nghĩ đó là giải pháp tốt trong ứng dụng RESTful - nhưng dịch vụ khách hàng nhận mã thông báo ở đâu ngay từ đầu? Làm thế nào để họ xác thực lần đầu tiên? Có thể tôi đang nghĩ sai, nhưng có vẻ kỳ lạ là dịch vụ khách hàng sẽ cần phải có tên người dùng và mật khẩu riêng cho việc này.
Tommi

Nếu người dùng cuối là người dùng của bạn, thì dịch vụ trung gian có thể chuyển thông tin đăng nhập của họ cho bạn theo yêu cầu đầu tiên và bạn có thể trả lại cookie hoặc mã thông báo khác.
jwismar

Tương tự, trong kịch bản OAuth, người dùng cuối đang ủy quyền cho dịch vụ trung gian mà họ truy cập vào dịch vụ web của bạn.
jwismar

Dường như có một sự hiểu lầm - không có người dùng cuối nào đằng sau dịch vụ khách hàng . Tôi đã cập nhật câu hỏi của tôi để giải thích tình hình tốt hơn.
Tommi

1
Tôi chỉ muốn thêm rằng tùy chọn số 1 được liệt kê ở trên chỉ nên được thực hiện qua HTTPS.
mr-sk

3

Tôi tin rằng cách tiếp cận:

  1. Yêu cầu đầu tiên, khách hàng gửi id / mật mã
  2. Trao đổi id / pass cho mã thông báo duy nhất
  3. Xác thực mã thông báo trên mỗi yêu cầu tiếp theo cho đến khi hết hạn

là khá chuẩn, bất kể bạn thực hiện như thế nào và các chi tiết kỹ thuật cụ thể khác.

Nếu bạn thực sự muốn đẩy phong bì, có lẽ bạn có thể coi khóa https của khách hàng ở trạng thái tạm thời không hợp lệ cho đến khi thông tin xác thực được xác thực, giới hạn thông tin nếu chúng không bao giờ và cấp quyền truy cập khi chúng được xác thực, dựa trên hết hạn.

Hi vọng điêu nay co ich


3

Theo như cách tiếp cận chứng chỉ ứng dụng khách, sẽ không quá khó để thực hiện trong khi vẫn cho phép người dùng không có chứng chỉ ứng dụng khách.

Nếu trên thực tế bạn đã tạo ra Cơ quan chứng nhận tự ký của riêng mình và cấp chứng nhận khách hàng cho mỗi dịch vụ khách hàng, bạn sẽ có một cách dễ dàng để xác thực các dịch vụ đó.

Tùy thuộc vào máy chủ web bạn đang sử dụng, cần có một phương pháp để chỉ định xác thực ứng dụng khách sẽ chấp nhận chứng chỉ ứng dụng khách, nhưng không yêu cầu chứng chỉ. Ví dụ: trong Tomcat khi chỉ định trình kết nối https của bạn, bạn có thể đặt 'clientAuth = muốn', thay vì 'true' hoặc 'false'. Sau đó, bạn sẽ đảm bảo thêm chứng chỉ CA tự ký vào cửa hàng tin cậy của mình (theo mặc định tệp cacerts trong JRE bạn đang sử dụng, trừ khi bạn chỉ định một tệp khác trong cấu hình máy chủ web của mình), vì vậy các chứng chỉ tin cậy duy nhất sẽ được cấp CA tự ký của bạn.

Về phía máy chủ, bạn sẽ chỉ cho phép truy cập vào các dịch vụ mà bạn muốn bảo vệ nếu bạn có thể truy xuất chứng chỉ ứng dụng khách từ yêu cầu (không phải null) và vượt qua bất kỳ kiểm tra DN nào nếu bạn muốn bảo mật hơn. Đối với người dùng không có máy khách, họ vẫn có thể truy cập dịch vụ của bạn, nhưng đơn giản là sẽ không có chứng chỉ nào trong yêu cầu.

Theo tôi đây là cách 'an toàn' nhất, nhưng chắc chắn nó có đường cong học tập và chi phí hoạt động, vì vậy có thể không nhất thiết là giải pháp tốt nhất cho nhu cầu của bạn.


3

5. Cái gì khác - phải có giải pháp khác ngoài đó?

Bạn nói đúng, có đấy! Và nó được gọi là JWT (JSON Web Tokens).

Mã thông báo web JSON (JWT) là một tiêu chuẩn mở (RFC 7519) xác định một cách nhỏ gọn và khép kín để truyền thông tin an toàn giữa các bên dưới dạng đối tượng JSON. Thông tin này có thể được xác minh và tin cậy vì nó được ký điện tử. JWT có thể được ký bằng cách sử dụng một bí mật (với thuật toán HMAC) hoặc cặp khóa công khai / riêng bằng RSA.

Tôi rất khuyên bạn nên tìm hiểu về JWTs. Chúng là một giải pháp đơn giản hơn nhiều cho vấn đề khi so sánh với các giải pháp thay thế.

https://jwt.io/int sinhtion /


1

Bạn có thể tạo Phiên trên máy chủ và chia sẻ sessionIdgiữa máy khách và máy chủ với mỗi lệnh gọi REST.

  1. Đầu tiên xác thực yêu cầu REST : /authenticate. Trả về phản hồi (theo định dạng máy khách của bạn) với sessionId: ABCDXXXXXXXXXXXXXX;

  2. Lưu trữ này sessionIdtrong Mapphiên thực tế. Map.put(sessionid, session)hoặc sử dụng SessionListenerđể tạo và hủy khóa cho bạn;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Nhận sessionid với mọi cuộc gọi REST, như URL?jsessionid=ABCDXXXXXXXXXXXXXX(hoặc cách khác);

  4. Lấy lại HttpSessiontừ bản đồ bằng cách sử dụng sessionId;
  5. Xác thực yêu cầu cho phiên đó nếu phiên hoạt động;
  6. Gửi lại phản hồi hoặc thông báo lỗi.

0

Tôi sẽ sử dụng để ứng dụng chuyển hướng người dùng đến trang web của bạn với tham số id ứng dụng, khi người dùng chấp thuận yêu cầu tạo mã thông báo duy nhất được ứng dụng kia sử dụng để xác thực. Bằng cách này, các ứng dụng khác không xử lý thông tin đăng nhập của người dùng và các ứng dụng khác có thể được thêm, xóa và quản lý bởi người dùng. Foursquare và một vài trang web khác xác thực theo cách này và rất dễ thực hiện như các ứng dụng khác.


Hmm, tôi không chắc là tôi có thể làm theo lời giải thích hay không. Chúng ta đang nói về người dùng nào? Tôi đang nói về ứng dụng giao tiếp với một ứng dụng khác. Tôi cho rằng bạn hiểu điều này, nhưng tôi vẫn không thể hiểu được. Điều gì xảy ra khi "mã thông báo" này hết hạn, chẳng hạn?
Tommi

Vâng, mã thông báo mà bạn tạo và gửi lại cho ứng dụng khác là mã thông báo liên tục, nó được gắn với người dùng và ứng dụng. Đây là một liên kết đến foursquares docs developer.f xông.com/docs/oauth.html và về cơ bản nó chỉ là oauth2 vì vậy hãy xem xét điều đó để có giải pháp xác thực tốt.
Devin M

Dường như có một sự hiểu lầm - không có người dùng cuối nào đằng sau dịch vụ khách hàng . Tuy nhiên, tài liệu của bạn được liên kết để đề cập ngắn gọn về quyền truy cập của người dùng, do đó, ít nhất nó cũng hữu ích - cảm ơn! Nhưng tôi vẫn không thể tạo thành một bức tranh hoàn chỉnh về cách nó sẽ hoạt động trong thực tế.
Tommi

Chỉ cần tạo khóa cho các ứng dụng sau đó, nếu tất cả những gì bạn đang làm là cho phép truy cập vào các ứng dụng thì một application_id và application_key đơn giản sẽ hoạt động để xác thực. Nếu bạn muốn yêu cầu chúng xác thực bằng mã thông báo, hãy sử dụng các tùy chọn xác thực mã thông báo vì nó sẽ chỉ là một tham số được truyền với yêu cầu url đến ứng dụng của bạn.
Devin M

Nhưng điều này không chính xác giống như kịch bản mã thông báo xác thực tên người dùng + mật khẩu = phiên? Không phải application_id và application_key chỉ là từ đồng nghĩa cho tên người dùng và mật khẩu? :) Sẽ hoàn toàn ổn nếu đây thực sự là tiêu chuẩn thực hành cho một tình huống như thế này - như tôi đã nói, tôi thiếu kinh nghiệm về vấn đề này - nhưng tôi chỉ nghĩ có thể có những lựa chọn khác ...
Tommi

-3

Bên cạnh xác thực, tôi đề nghị bạn nghĩ về bức tranh lớn. Xem xét thực hiện dịch vụ RESTful phụ trợ của bạn mà không cần bất kỳ xác thực nào; sau đó đặt một số xác thực rất đơn giản yêu cầu dịch vụ lớp giữa giữa người dùng cuối và dịch vụ phụ trợ.


Giữa người dùng cuối và dịch vụ phụ trợ? Điều đó có khiến cho các dịch vụ khách hàng không được xác thực hoàn toàn không? Đó không phải là điều tôi muốn. Tất nhiên tôi có thể đặt lớp giữa đó giữa dịch vụ web và dịch vụ khách của mình, nhưng điều đó vẫn để ngỏ câu hỏi: mô hình xác thực thực tế sẽ là gì?
Tommi

Lớp giữa có thể là máy chủ web như Nginx, bạn có thể xác thực ở đó. Mô hình xác thực có thể dựa trên phiên.
Dagang

Như tôi đã cố gắng giải thích trong câu hỏi của mình, tôi không muốn một sơ đồ xác thực dựa trên phiên cho các dịch vụ khách hàng này. (Vui lòng xem các cập nhật của tôi cho câu hỏi.)
Tommi

Tôi đề nghị bạn sử dụng danh sách ip trắng hoặc phạm vi ip thay vì tên và mật khẩu. Thông thường, ip dịch vụ khách hàng ổn định.
Dagang
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.