Tạo API cho ứng dụng di động - Xác thực và Ủy quyền


189

Tổng quat

Tôi đang tìm cách tạo API (REST) ​​cho ứng dụng của mình. Mục đích ban đầu / chính sẽ được sử dụng cho các ứng dụng di động (iPhone, Android, Symbian, v.v.). Tôi đã xem xét các cơ chế khác nhau để xác thực và ủy quyền cho các API dựa trên web (bằng cách nghiên cứu các triển khai khác). Tôi đã tập trung vào hầu hết các khái niệm cơ bản nhưng vẫn đang tìm kiếm hướng dẫn trong một vài lĩnh vực. Điều cuối cùng tôi muốn làm là phát minh lại bánh xe, nhưng tôi không tìm thấy bất kỳ giải pháp tiêu chuẩn nào phù hợp với tiêu chí của mình (tuy nhiên tiêu chí của tôi bị nhầm lẫn nên cũng thoải mái phê bình điều đó). Ngoài ra, tôi muốn API giống nhau cho tất cả các nền tảng / ứng dụng tiêu thụ nó.

oAuth

Tôi sẽ tiếp tục và đưa ra phản đối của mình cho oAuth vì tôi biết đó có thể sẽ là giải pháp đầu tiên được đưa ra. Đối với các ứng dụng di động (hoặc cụ thể hơn là các ứng dụng không phải là web), việc rời khỏi ứng dụng (để truy cập trình duyệt web) có vẻ sai. Ngoài ra, không có cách nào (tôi biết) để trình duyệt trả lại cuộc gọi lại cho ứng dụng (đặc biệt là đa nền tảng). Tôi biết một vài ứng dụng làm điều đó, nhưng nó chỉ cảm thấy sai và làm hỏng ứng dụng UX.

Yêu cầu

  1. Người dùng nhập tên người dùng / mật khẩu vào ứng dụng.
  2. Mỗi cuộc gọi API được xác định bởi ứng dụng gọi điện.
  3. Chi phí được giữ ở mức tối thiểu và khía cạnh xác thực là trực quan cho các nhà phát triển.
  4. Cơ chế này an toàn cho cả người dùng cuối (thông tin đăng nhập của họ không bị lộ) cũng như nhà phát triển (thông tin đăng nhập ứng dụng của họ không bị lộ).
  5. Nếu có thể, không yêu cầu https (không có nghĩa là yêu cầu khó).

Suy nghĩ hiện tại của tôi về việc thực hiện

Một nhà phát triển bên ngoài sẽ yêu cầu một tài khoản API. Họ sẽ nhận được một apikey và apisecret. Mỗi yêu cầu sẽ yêu cầu tối thiểu ba tham số.

  • apikey - được trao cho nhà phát triển khi đăng ký
  • dấu thời gian - nhân đôi như một định danh duy nhất cho mỗi tin nhắn cho một apikey nhất định
  • hàm băm - hàm băm của dấu thời gian + apisecret

Apikey được yêu cầu để xác định ứng dụng đưa ra yêu cầu. Dấu thời gian hoạt động tương tự như oauth_nonce và tránh / giảm nhẹ các cuộc tấn công phát lại. Hàm băm đảm bảo rằng yêu cầu đó thực sự được đưa ra từ chủ sở hữu của apikey đã cho.

Đối với các yêu cầu được xác thực (những yêu cầu được thực hiện thay mặt cho người dùng), tôi vẫn chưa quyết định giữa việc đi với tuyến access_token hoặc kết hợp băm tên người dùng và mật khẩu. Dù bằng cách nào, tại một số điểm, một yêu cầu kết hợp tên người dùng / mật khẩu sẽ được yêu cầu. Vì vậy, khi có, một hàm băm của một vài thông tin (apikey, apisecret, dấu thời gian) + mật khẩu sẽ được sử dụng. Tôi thích phản hồi về khía cạnh này. FYI, họ sẽ phải băm mật khẩu trước, vì tôi không lưu trữ mật khẩu trong hệ thống của mình mà không băm.

Phần kết luận

FYI, đây không phải là yêu cầu về cách xây dựng / cấu trúc API nói chung chỉ là cách xử lý xác thực và ủy quyền chỉ trong một ứng dụng.

Suy nghĩ ngẫu nhiên / Câu hỏi thưởng

Đối với các API chỉ yêu cầu apikey như một phần của yêu cầu, làm thế nào để bạn ngăn người khác không phải chủ sở hữu apikey có thể thấy apikey (kể từ khi được gửi rõ ràng) và đưa ra yêu cầu quá mức để đẩy họ vượt quá giới hạn sử dụng? Có lẽ tôi chỉ nghĩ quá nhiều về điều này, nhưng không nên có một cái gì đó để xác thực rằng một yêu cầu đã được xác minh cho chủ sở hữu apikey? Trong trường hợp của tôi, đó là mục đích của apisecret, nó không bao giờ được hiển thị / truyền đi mà không bị băm.

Nói về băm, còn md5 vs hmac-sha1 thì sao? Có thực sự quan trọng khi tất cả các giá trị được băm với dữ liệu đủ dài (ví dụ: apisecret) không?

Trước đây tôi đã từng xem xét việc thêm muối cho mỗi người dùng / hàng vào hàm băm mật khẩu người dùng của mình. Nếu tôi làm điều đó, làm thế nào ứng dụng có thể tạo ra một hàm băm phù hợp mà không biết muối được sử dụng?


1
Hy vọng sẽ nhận được một vài ý kiến ​​/ đề xuất. Là câu hỏi quá mơ hồ / mơ hồ?
jsuggs

6
câu hỏi là hoàn hảo nhưng, thậm chí gần 2 năm sau, việc triển khai oauth dường như rất phức tạp ... tôi đang gặp khó khăn nhất để đạt được chính xác những gì bạn đã thảo luận ở trên. Tôi có một thứ nguyên bổ sung: tôi không muốn sử dụng cặp loginName / mật khẩu - tôi muốn sử dụng xác minh danh tính google trên android / ios (symbian được WWF tuyên bố là "loài gần như tuyệt chủng") và tôi từ chối phát triển windows mobile (bất cứ điều gì họ gọi nó là những ngày này).
tony gil

8
thật nực cười khi nhiều như mọi người đề nghị oauth 2.0 vẫn chưa tìm thấy một hướng dẫn hoặc ví dụ đơn giản, rõ ràng sử dụng tiếng Anh thông dụng để giải thích các bước, yêu cầu, làm và không làm ....
ChuckKelly 16/07/13

2
Hãy đọc tại luồng cụ thể này của OAuth2.0 (Luồng mật khẩu của chủ sở hữu tài nguyên). Không chuyển hướng đến trang web. techblog.hybris.com/2012/06/11/ cường
Franklin

1
Tôi cũng đang tìm kiếm câu trả lời tương tự. Tôi tìm thấy một bài viết tốt được viết gần đây. Tôi hi vọng cái này giúp được. Stormpath.com/blog/the-ultimate-guide-to-mobile-api-securance
Mới đối với Rails

Câu trả lời:


44

Cách tôi nghĩ về việc thực hiện phần đăng nhập của dự án này là:

  1. trước khi đăng nhập người dùng yêu cầu a login_tokentừ máy chủ. Chúng được tạo và lưu trữ trên máy chủ theo yêu cầu và có thể có tuổi thọ giới hạn.

  2. để đăng nhập, ứng dụng sẽ tính toán hàm băm của mật khẩu người dùng, sau đó băm mật khẩu login_tokenđể lấy giá trị, sau đó họ trả lại cả login_tokenhàm băm và kết hợp.

  3. Máy chủ kiểm tra login_tokencái mà nó đã tạo, loại bỏ nó khỏi danh sách login_tokens hợp lệ của nó. Sau đó, máy chủ sẽ kết hợp hàm băm được lưu trữ của mật khẩu người dùng với login_tokenvà đảm bảo rằng nó khớp với mã thông báo kết hợp đã gửi. Nếu nó phù hợp với bạn đã xác thực người dùng của bạn.

Ưu điểm của việc này là bạn không bao giờ lưu trữ mật khẩu của người dùng trên máy chủ, mật khẩu không bao giờ được xóa rõ ràng, hàm băm mật khẩu chỉ được chuyển qua phần rõ ràng khi tạo tài khoản (mặc dù có thể có cách này) an toàn khỏi các cuộc tấn công phát lại khi login_tokenđược gỡ bỏ khỏi DB khi sử dụng.


Cảm ơn, tôi đã quên thêm phần về việc băm mật khẩu ở phía ứng dụng. Tôi không lưu trữ mật khẩu người dùng của mình rõ ràng (tôi băm trước khi lưu trữ).
jsuggs

2
Tôi thấy một nhược điểm của phương pháp này: bạn không thể lưu trữ mật khẩu muối trong DB. Nếu kẻ tấn công đặt tay lên DB của bạn, họ sẽ không cần thực hiện bất kỳ giải mã nào. Vì mật khẩu băm là mật khẩu thực trong sơ đồ này.
sigod

2
@sigod Bạn nói đúng. Mặc dù tôi nghĩ rằng có một sự phân đôi cơ bản ở đó - bạn cần phải tin tưởng vào sự vận chuyển của mình, hoặc bạn cần tin vào lưu trữ của mình. Các hệ thống đăng nhập sử dụng mật khẩu muối tin tưởng lớp vận chuyển - và do đó chuyển mật khẩu từ người dùng đến hệ thống xác thực rõ ràng. Trường hợp này không tin tưởng vào lớp vận chuyển (Tôi nghĩ rằng điều này là do nền tảng tôi nhắm mục tiêu có hỗ trợ xấu cho SHTTP). Nếu bạn tin tưởng lớp vận chuyển, bạn có thể thực hiện các giao dịch khác.
Michael Anderson

Tôi có 3 vấn đề: 1) mật khẩu người dùng không bao giờ được lưu trữ trên máy chủ? ở bước 2 bạn đã đề cập rằng nó lưu mật khẩu của người dùng đã băm. 2) Mật khẩu người dùng không bao giờ được thông qua rõ ràng. Nhưng nó thực sự được băm trên máy khách và sau đó được so sánh với mật khẩu được băm trên máy chủ, gần giống như việc chuyển và lưu trữ nó rõ ràng, điểm cần làm là gì? 3) Phương pháp này giả định kẻ tấn công không biết mật khẩu login_token + được băm như thế nào, điều này vi phạm nguyên tắc Kerckhoffs và khiến nó thực sự không an toàn.
Tamer Shlash

14

Đó là rất nhiều câu hỏi trong một, tôi đoán khá nhiều người đã không thể đọc hết từ đầu đến cuối :)

Kinh nghiệm của tôi về xác thực dịch vụ web là mọi người thường áp đảo nó và các vấn đề chỉ giống như bạn gặp phải trên một trang web. Các tùy chọn rất đơn giản có thể sẽ bao gồm https cho bước đăng nhập, trả lại mã thông báo, yêu cầu nó phải được bao gồm trong các yêu cầu trong tương lai. Bạn cũng có thể sử dụng xác thực cơ bản http và chỉ cần chuyển nội dung trong tiêu đề. Để bảo mật hơn, hãy xoay / hết hạn mã thông báo thường xuyên, kiểm tra các yêu cầu đến từ cùng một khối IP (điều này có thể gây lộn xộn khi người dùng di động di chuyển giữa các ô), kết hợp với khóa API hoặc tương tự. Ngoài ra, hãy thực hiện bước "khóa yêu cầu" của oauth (ai đó đã gợi ý điều này trong câu trả lời trước đó và đó là một ý tưởng hay) trước khi xác thực người dùng và sử dụng làm khóa bắt buộc để tạo mã thông báo truy cập.

Một thay thế mà tôi chưa sử dụng nhưng tôi đã nghe nhiều về việc thay thế thân thiện với thiết bị cho oAuth là xAuth . Hãy nhìn vào nó và nếu bạn sử dụng nó thì tôi thực sự thích thú khi nghe ấn tượng của bạn là gì.

Để băm, sha1 tốt hơn một chút nhưng đừng nôn nao về nó - bất cứ điều gì các thiết bị có thể dễ dàng (và nhanh chóng theo nghĩa hiệu suất) thực hiện có thể tốt.

Hy vọng điều đó có thể giúp, chúc may mắn :)


Cảm ơn vì sự trả lời. Tôi đã xem xét xAuth và đó có thể là con đường tôi đi để tôi có thể kết thúc cài đặt oAuth, điều này tạo ra một quy trình chuẩn hơn để tương tác với API.
jsuggs

9

Vì vậy, những gì bạn đang theo đuổi là một loại cơ chế xác thực phía máy chủ sẽ xử lý các khía cạnh xác thực và ủy quyền của một ứng dụng di động?

Giả sử đây là trường hợp, sau đó tôi sẽ tiếp cận nó như sau (nhưng chỉ là tôi là nhà phát triển Java nên một anh chàng C # sẽ làm khác đi):

Dịch vụ xác thực và ủy quyền RESTful

  1. Điều này sẽ chỉ hoạt động trên HTTPS để ngăn chặn nghe lén.
  2. Nó sẽ dựa trên sự kết hợp của REST EAS , Spring SecurityCAS (cho một dấu hiệu trên nhiều ứng dụng).
  3. Nó sẽ hoạt động với cả trình duyệt và ứng dụng khách kích hoạt web
  4. Sẽ có giao diện quản lý tài khoản dựa trên web để cho phép người dùng chỉnh sửa chi tiết và quản trị viên (đối với các ứng dụng cụ thể) để thay đổi cấp ủy quyền

Thư viện / ứng dụng bảo mật phía máy khách

  1. Đối với mỗi nền tảng được hỗ trợ (ví dụ: Symbian, Android, iOS, v.v.), hãy tạo một triển khai phù hợp của thư viện bảo mật bằng ngôn ngữ gốc của nền tảng (ví dụ: Java, ObjectiveC, C, v.v.)
  2. Thư viện nên quản lý việc hình thành yêu cầu HTTPS bằng các API có sẵn cho nền tảng đã cho (ví dụ: Java sử dụng URLConnection, v.v.)
  3. Người tiêu dùng của thư viện xác thực và ủy quyền chung ('vì đó là tất cả) sẽ mã hóa đến một giao diện cụ thể và sẽ không vui nếu nó thay đổi, vì vậy hãy đảm bảo rằng nó rất linh hoạt. Thực hiện theo các lựa chọn thiết kế hiện có như Spring Security.

Vì vậy, bây giờ chế độ xem từ 30.000ft đã hoàn tất, bạn sẽ thực hiện nó như thế nào? Chà, không khó để tạo ra một hệ thống xác thực và ủy quyền dựa trên các công nghệ được liệt kê ở phía máy chủ với ứng dụng trình duyệt. Kết hợp với HTTPS, các khung sẽ cung cấp một quy trình bảo mật dựa trên mã thông báo được chia sẻ (thường được trình bày dưới dạng cookie) được tạo bởi quy trình xác thực và được sử dụng bất cứ khi nào người dùng muốn làm gì đó. Mã thông báo này được trình bày bởi máy khách cho máy chủ bất cứ khi nào có yêu cầu diễn ra.

Trong trường hợp ứng dụng di động cục bộ, có vẻ như bạn đang theo một giải pháp thực hiện như sau:

  1. Ứng dụng khách có Danh sách điều khiển truy cập (ACL) được xác định kiểm soát truy cập thời gian chạy đối với các cuộc gọi phương thức. Ví dụ, một người dùng nhất định có thể đọc một bộ sưu tập từ một phương thức, nhưng ACL của họ chỉ cho phép truy cập vào các đối tượng có Q trong tên của họ, vì vậy một số dữ liệu trong bộ sưu tập bị chặn bởi bộ chặn an ninh. Trong Java, điều này rất đơn giản, bạn chỉ cần sử dụng các chú thích Spring Security trên mã gọi và thực hiện quy trình phản hồi ACL phù hợp. Trong các ngôn ngữ khác, bạn chỉ có một mình và có thể sẽ cần cung cấp mã bảo mật soạn sẵn cuộc gọi vào thư viện bảo mật của bạn. Nếu ngôn ngữ hỗ trợ AOP (Lập trình hướng theo khía cạnh) thì hãy sử dụng nó hết mức cho tình huống này.
  2. Thư viện bảo mật lưu trữ danh sách đầy đủ các ủy quyền vào bộ nhớ riêng cho ứng dụng hiện tại để nó không phải duy trì kết nối. Tùy thuộc vào độ dài của phiên đăng nhập, đây có thể là thao tác một lần không bao giờ được lặp lại.

Dù bạn làm gì, đừng cố phát minh ra giao thức bảo mật của riêng bạn hoặc sử dụng bảo mật bằng cách che khuất. Bạn sẽ không bao giờ có thể viết một thuật toán tốt hơn cho thuật toán này hiện có sẵn và miễn phí. Ngoài ra, mọi người tin tưởng các thuật toán nổi tiếng. Vì vậy, nếu bạn nói rằng thư viện bảo mật của bạn cung cấp ủy quyền và xác thực cho các ứng dụng di động cục bộ bằng cách sử dụng kết hợp các mã thông báo được mã hóa SSL, HTTPS, SpringSecurity và AES thì bạn sẽ ngay lập tức có uy tín trên thị trường.

Hy vọng điều này sẽ giúp, và may mắn với liên doanh của bạn. Nếu bạn muốn biết thêm thông tin, hãy cho tôi biết - Tôi đã viết khá nhiều ứng dụng web dựa trên Spring Security, ACL và những thứ tương tự.


Cảm ơn, thông tin tốt. Vài câu hỏi. Đầu tiên, nếu việc nghe lén có thể chấp nhận được (không chắc là nó có / không, ứng dụng của tôi trong tâm trí không có bất kỳ thông tin cá nhân / giá trị thực sự nào, nhưng nếu lý do của tôi sẽ thay đổi) thì HTTPS có thực sự cần thiết không?
jsuggs

Bạn có thể vận hành hệ thống tổng thể bên ngoài HTTPS nếu muốn. HTTPS chỉ để bảo vệ thông tin bí mật, vì vậy tôi cho rằng trong giai đoạn xác thực, bạn sẽ thực hiện điều đó thông qua HTTPS để đảm bảo rằng tên người dùng / mật khẩu / bí mật của bạn được giữ bí mật. Sau khi mã thông báo được chuyển giao trong phản hồi thì các yêu cầu tiếp theo có thể được làm rõ nếu thông tin có trong luồng (yêu cầu xác thực cần lấy) không cần bảo vệ khỏi kẻ nghe trộm.
Gary Rowe

Ngoài ra, mô tả về giao thức xác thực CAS này có thể hữu ích: jasig.org/cas/protatio
Gary Rowe

9

Twitter đã giải quyết vấn đề ứng dụng bên ngoài trong oAuth bằng cách hỗ trợ một biến thể mà họ gọi là xAuth . Thật không may, đã có rất nhiều chương trình khác với tên này nên có thể gây nhầm lẫn khi sắp xếp.

Giao thức oAuth, ngoại trừ nó bỏ qua giai đoạn mã thông báo yêu cầu và chỉ cần ngay lập tức phát hành cặp mã thông báo truy cập khi nhận được tên người dùng và mật khẩu. (Bắt đầu từ bước E tại đây .) Yêu cầu và phản hồi ban đầu này phải được bảo mật- đó là gửi tên người dùng và mật khẩu trong văn bản gốc và nhận lại mã thông báo truy cập và mã thông báo bí mật. Khi cặp mã thông báo truy cập đã được định cấu hình, việc trao đổi mã thông báo ban đầu là thông qua mô hình oAuth hoặc mô hình xAuth không liên quan đến cả máy khách và máy chủ trong phần còn lại của phiên. Điều này có lợi thế là bạn có thể tận dụng cơ sở hạ tầng oAuth hiện có và có cách triển khai gần như tương tự cho các ứng dụng di động / web / máy tính để bàn. Nhược điểm chính là ứng dụng được cấp quyền truy cập vào tên người dùng và mật khẩu của khách hàng, nhưng có vẻ như các yêu cầu của bạn bắt buộc cách tiếp cận này.

Trong mọi trường hợp, tôi muốn đồng ý với trực giác của bạn và của một số người trả lời khác ở đây: đừng cố gắng xây dựng một cái gì đó mới từ đầu. Các giao thức bảo mật có thể dễ dàng bắt đầu nhưng luôn khó thực hiện tốt và chúng càng trở nên phức tạp thì càng ít khả năng các nhà phát triển bên thứ ba của bạn có thể thực hiện để chống lại chúng. Giao thức giả định của bạn rất giống với o (x) Auth - api_key / api_secret, nonce, sha1 băm - nhưng thay vì có thể sử dụng một trong nhiều thư viện hiện có mà các nhà phát triển của bạn sẽ cần phải tự cuộn.


2
Tôi cũng nên chỉ ra rằng có vẻ như điểm cuối 'bỏ qua mã thông báo yêu cầu' sẽ xuất hiện trong oAuth 2, nó được liệt kê trong dự thảo hiện tại là loại cấp quyền truy cập "mật khẩu". Xem phần 4.1.2: tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.2
lantius

Giống như tôi đã đề cập với Lonra, tôi đang tìm hiểu thêm về xAuth và đặc biệt là vì những lý do bạn đã đề cập ở cuối ... các nhà phát triển bạn có thể "tắt kệ" oAuth tools / libs để tương tác với API của tôi, đó là "một điều tốt" .
jsuggs

6

Siêu muộn đến bữa tiệc nhưng tôi muốn đưa ra một số điểm bổ sung để xem xét cho bất cứ ai quan tâm đến vấn đề này. Tôi làm việc cho một công ty thực hiện các giải pháp bảo mật API di động ( Phê duyệt ) vì vậy toàn bộ lĩnh vực này chắc chắn phù hợp với sở thích của tôi.

Để bắt đầu, điều quan trọng nhất cần xem xét khi cố gắng bảo mật API di động là nó đáng giá bao nhiêu đối với bạn . Giải pháp phù hợp cho ngân hàng khác với giải pháp phù hợp cho ai đó chỉ làm mọi việc cho vui.

Trong giải pháp đề xuất, bạn đề cập rằng sẽ yêu cầu tối thiểu ba tham số:

  • apikey - trao cho nhà phát triển lúc đăng ký
  • dấu thời gian - nhân đôi như một định danh duy nhất cho mỗi tin nhắn cho một apikey nhất định
  • hàm băm - hàm băm của dấu thời gian + apisecret

Hàm ý của điều này là đối với một số lệnh gọi API, không yêu cầu tên người dùng / mật khẩu. Điều này có thể hữu ích cho các ứng dụng mà bạn không muốn đăng nhập (chẳng hạn như duyệt trong các cửa hàng trực tuyến).

Đây là một vấn đề hơi khác với xác thực người dùng và giống như xác thực hoặc chứng thực phần mềm. Không có người dùng, nhưng bạn vẫn muốn đảm bảo rằng không có quyền truy cập độc hại vào API của mình. Vì vậy, bạn sử dụng bí mật API của mình để ký lưu lượng truy cập và xác định mã truy cập API là chính hãng. Vấn đề tiềm ẩn với giải pháp này là sau đó bạn phải tiết lộ bí mật bên trong mỗi phiên bản của ứng dụng. Nếu ai đó có thể trích xuất bí mật họ có thể sử dụng API của bạn, mạo danh phần mềm của bạn nhưng làm bất cứ điều gì họ thích.

Để chống lại mối đe dọa đó, có rất nhiều điều bạn có thể làm tùy thuộc vào giá trị của dữ liệu. Obfuscation là một cách đơn giản để làm cho việc giải nén bí mật trở nên khó khăn hơn. Có những công cụ sẽ làm điều đó cho bạn, hơn nữa đối với Android, nhưng bạn vẫn phải có mã tạo ra hàm băm của mình và một cá nhân đủ kỹ năng luôn có thể chỉ cần gọi hàm thực hiện băm trực tiếp.

Một cách khác để giảm thiểu việc sử dụng quá mức API không yêu cầu đăng nhập là điều tiết lưu lượng và có khả năng xác định và chặn các địa chỉ IP đáng ngờ. Lượng nỗ lực bạn muốn thực hiện sẽ phụ thuộc phần lớn vào mức độ ổn định của dữ liệu của bạn.

Ngoài ra, bạn có thể dễ dàng bắt đầu tham gia vào lĩnh vực công việc hàng ngày của tôi. Dù sao, đó là một khía cạnh khác của việc bảo mật API mà tôi nghĩ là quan trọng và muốn gắn cờ.

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.