Xác thực REST và hiển thị khóa API


93

Tôi đã đọc về REST và có rất nhiều câu hỏi trên SO về nó, cũng như trên nhiều trang web và blog khác. Mặc dù tôi chưa bao giờ thấy câu hỏi cụ thể này được hỏi ... vì một số lý do, tôi không thể xoay quanh khái niệm này ...

Nếu tôi đang xây dựng một API RESTful và tôi muốn bảo mật nó, một trong những phương pháp tôi đã thấy là sử dụng mã thông báo bảo mật. Khi tôi sử dụng các API khác, có một mã thông báo và một bí mật được chia sẻ ... có lý. Điều tôi không hiểu là, các yêu cầu đối với hoạt động dịch vụ nghỉ ngơi đang được thực hiện thông qua javascript (XHR / Ajax), điều gì là ngăn ai đó phát hiện ra điều đó bằng một cái gì đó đơn giản như FireBug (hoặc "nguồn xem" trong trình duyệt) và sao chép khóa API và sau đó mạo danh người đó bằng khóa và bí mật?


một trong những phương pháp mà tôi đã thấy là sử dụng mã thông báo bảo mật , thực sự có rất nhiều phương pháp. Có bạn một ví dụ nhỏ. Tôi có thể nghĩ rằng bạn sẽ nhầm lẫn giữa "REST" so với "chỉ cung cấp một API javascript cho người dùng đã đăng ký" (ví dụ: google maps).
PeterMmm

1
Kể từ khi bạn hỏi gần 2 năm trước: bản thân bạn rốt cuộc đã sử dụng cái gì?
Arjan

Tôi thực sự không sử dụng bất cứ thứ gì, tôi chỉ cố gắng xoay sở để tạo ra các khái niệm. Nhận xét của PeterMmm ở trên có lẽ đúng ... vẫn chưa có nhu cầu thực hiện bất kỳ điều gì trong số này, nhưng tôi muốn bản thân mình tốt hơn ... cảm ơn đã theo dõi.
tjans

Câu trả lời:


22

api secret không được thông qua một cách rõ ràng, bí mật được sử dụng để tạo dấu hiệu của yêu cầu hiện tại, ở phía máy chủ, máy chủ tạo dấu hiệu theo cùng một quy trình, nếu hai dấu hiệu khớp nhau thì yêu cầu được xác thực thành công - vì vậy chỉ được thông qua yêu cầu, không phải bí mật.


9
Vì vậy, nếu đó chỉ là dấu hiệu đã được thông qua ... thì điều đó vẫn chưa được hiển thị trong javascript ... vì vậy nếu tôi đặt một bức ảnh nhấp nháy trên trang web của mình thông qua API của họ (được gọi bằng javascript) và bạn truy cập trang của tôi, thì không ' t tôi để lộ khóa API của mình cho bất kỳ ai truy cập trang của tôi?
tjans

6
Tôi không nghĩ rằng tôi đang hỏi câu hỏi của mình một cách chính xác ... có lẽ một phần lý do tôi đã không tìm thấy những gì tôi đang tìm kiếm ngay từ đầu. khi tôi thực hiện cuộc gọi ajax của mình, giả sử bằng cách sử dụng jquery, tôi phải nhúng khóa api vào lệnh gọi ajax để nó được chuyển đến máy chủ ... tại thời điểm đó, ai đó có thể nhìn thấy khóa API. Nếu tôi hiểu sai điều đó, làm cách nào để khóa API được gửi cùng với yêu cầu nếu nó không được nhúng vào tập lệnh ứng dụng khách?
tjans

4
để kết luận: mọi người sẽ được chỉ định một cặp apikey + apisecret trước khi sử dụng openapi / restapi, apikey + sign sẽ được chuyển đến máy chủ để đảm bảo máy chủ biết ai đang đưa ra yêu cầu, apisecret sẽ không bao giờ được chuyển đến máy chủ để bảo mật .
James.Xu

7
Vì vậy, tuyên bố của @ James.Xu rằng 'bí mật được sử dụng để tạo ra một dấu hiệu của yêu cầu hiện tại' là SAI! Bởi vì khách hàng không biết bí mật, bởi vì sẽ không an toàn nếu gửi nó cho anh ta (và làm sao anh ta biết được điều đó?) 'Bí mật' về mặt kỹ thuật là 'khóa riêng tư' CHỈ ĐƯỢC SỬ DỤNG bởi MÁY CHỦ (bởi vì không ai khác biết điều đó) để tạo ra một dấu hiệu được so sánh với dấu hiệu của khách hàng. Vì vậy, câu hỏi: Loại dữ liệu nào đang được kết hợp với 'khóa api' mà không ai khác biết ngoài máy khách và máy chủ? Ký = api_key + gì?
ACs

1
Bạn nói đúng, @ACs. Ngay cả khi cả hai máy chủ (trang web và API của bên thứ 3) biết cùng một bí mật, người ta không thể tính toán một số chữ ký trên máy chủ trang web và sau đó đưa kết quả đó vào HTML / JavaScript, sau đó làm cho trình duyệt chuyển nó đến API. Làm như vậy, bất kỳ máy chủ nào khác cũng có thể yêu cầu HTML đó từ máy chủ web đầu tiên, lấy chữ ký từ phản hồi và sử dụng chữ ký đó trong HTML trên trang web của riêng họ. (Tôi thực sự nghĩ rằng bài viết ở trên không trả lời các câu hỏi về cách một khóa API công cộng trong HTML có thể được an toàn.)
Arjan

61

Chúng tôi tiết lộ một API mà đối tác chỉ có thể sử dụng trên các miền mà họ đã đăng ký với chúng tôi. Nội dung của nó một phần là công khai (nhưng tốt nhất là chỉ hiển thị trên các miền mà chúng tôi biết), nhưng chủ yếu là riêng tư đối với người dùng của chúng tôi. Vì thế:

  • Để xác định những gì được hiển thị, người dùng của chúng tôi phải đăng nhập với chúng tôi, nhưng điều này được xử lý riêng.

  • Để xác định nơi dữ liệu được hiển thị, khóa API công khai được sử dụng để giới hạn quyền truy cập vào các miền mà chúng tôi biết và trên hết là để đảm bảo dữ liệu người dùng riêng tư không dễ bị tấn công bởi CSRF .

Khóa API này thực sự hiển thị cho bất kỳ ai, chúng tôi không xác thực đối tác của mình theo bất kỳ cách nào khác và chúng tôi không cần THAM KHẢO . Tuy nhiên, nó vẫn an toàn:

  1. Khi của chúng tôi get-csrf-token.js?apiKey=abc123được yêu cầu:

    1. Tra cứu khóa abc123trong cơ sở dữ liệu và nhận danh sách các miền hợp lệ cho khóa đó.

    2. Tìm cookie xác thực CSRF. Nếu nó không tồn tại, hãy tạo một giá trị ngẫu nhiên an toàn và đặt nó vào cookie phiên chỉ HTTP . Nếu cookie đã tồn tại, hãy lấy giá trị ngẫu nhiên hiện có.

    3. Tạo mã thông báo CSRF từ khóa API và giá trị ngẫu nhiên từ cookie và ký tên . (Thay vì giữ một danh sách các mã thông báo trên máy chủ, chúng tôi đang ký các giá trị. Cả hai giá trị sẽ có thể đọc được trong mã đã ký, điều đó tốt.)

    4. Đặt phản hồi không được lưu vào bộ nhớ cache, thêm cookie và trả về một tập lệnh như:

      var apiConfig = apiConfig || {};
      if(document.domain === 'expected-domain.com' 
            || document.domain === 'www.expected-domain.com') {
      
          apiConfig.csrfToken = 'API key, random value, signature';
      
          // Invoke a callback if the partner wants us to
          if(typeof apiConfig.fnInit !== 'undefined') {
              apiConfig.fnInit();
          }
      } else {
          alert('This site is not authorised for this API key.');
      }
      

    Ghi chú:

    • Những điều trên không ngăn chặn tập lệnh phía máy chủ giả mạo yêu cầu, mà chỉ đảm bảo rằng miền khớp nếu được trình duyệt yêu cầu.

    • Các chính sách có nguồn gốc tương tự cho JavaScript đảm bảo rằng trình duyệt không thể sử dụng XHR (Ajax) để tải và sau đó kiểm tra nguồn JavaScript. Thay vào đó, một trình duyệt thông thường chỉ có thể tải nó bằng cách sử dụng <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">(hoặc một trình duyệt động tương đương) và sau đó sẽ chạy mã. Tất nhiên, máy chủ của bạn không được hỗ trợ Chia sẻ tài nguyên nhiều nguồn gốc cũng như JSONP cho JavaScript được tạo.

    • Tập lệnh trình duyệt có thể thay đổi giá trị của document.domaintrước khi tải tập lệnh trên. Nhưng chính sách nguồn gốc tương tự chỉ cho phép rút ngắn miền bằng cách xóa các tiền tố, như viết lại subdomain.example.comthành chỉ example.com, hoặc myblog.wordpress.comthành wordpress.com, hoặc trong một số trình duyệt thậm chí bbc.co.ukthành co.uk.

    • Nếu tệp JavaScript được tìm nạp bằng một số tập lệnh phía máy chủ thì máy chủ cũng sẽ nhận được cookie. Tuy nhiên, máy chủ của bên thứ ba không thể khiến trình duyệt của người dùng liên kết cookie đó với miền của chúng tôi. Do đó, mã thông báo CSRF và cookie xác thực đã được tìm nạp bằng tập lệnh phía máy chủ, chỉ có thể được sử dụng bởi các lệnh gọi phía máy chủ tiếp theo chứ không phải trong trình duyệt. Tuy nhiên, các lệnh gọi phía máy chủ như vậy sẽ không bao giờ bao gồm cookie của người dùng và do đó chỉ có thể lấy dữ liệu công khai. Đây là dữ liệu tương tự mà một tập lệnh phía máy chủ có thể lấy trực tiếp từ trang web của đối tác.

  2. Khi người dùng đăng nhập, hãy đặt một số cookie người dùng theo bất kỳ cách nào bạn muốn. (Người dùng có thể đã đăng nhập trước khi JavaScript được yêu cầu.)

  3. Tất cả các yêu cầu API tiếp theo đến máy chủ (bao gồm cả yêu cầu GET và JSONP) phải bao gồm mã thông báo CSRF, cookie xác thực CSRF và cookie người dùng (nếu đã đăng nhập). Máy chủ hiện có thể xác định xem yêu cầu có đáng tin cậy hay không:

    1. Sự hiện diện của mã thông báo CSRF hợp lệ đảm bảo JavaScript được tải từ miền mong đợi, nếu được tải bởi trình duyệt.

    2. Sự hiện diện của mã thông báo CSRF mà không có cookie xác thực cho thấy sự giả mạo.

    3. Sự hiện diện của cả mã thông báo CSRF và cookie xác thực CSRF không đảm bảo bất kỳ điều gì: đây có thể là yêu cầu phía máy chủ giả mạo hoặc yêu cầu hợp lệ từ trình duyệt. (Đó không thể là yêu cầu từ một trình duyệt được tạo từ một miền không được hỗ trợ.)

    4. Sự hiện diện của cookie người dùng đảm bảo người dùng đã đăng nhập, nhưng không đảm bảo người dùng là thành viên của đối tác nhất định, cũng như người dùng đang xem đúng trang web.

    5. Sự hiện diện của cookie người dùng mà không có cookie xác thực CSRF cho thấy sự giả mạo.

    6. Sự hiện diện của cookie người dùng đảm bảo yêu cầu hiện tại được thực hiện thông qua trình duyệt. (Giả sử người dùng không nhập thông tin đăng nhập của họ trên một trang web không xác định và giả sử chúng tôi không quan tâm đến việc người dùng sử dụng thông tin đăng nhập của chính họ để thực hiện một số yêu cầu phía máy chủ.) Nếu chúng tôi cũng có cookie xác thực CSRF, thì cookie xác thực CSRF đó là cũng nhận được bằng trình duyệt. Tiếp theo, nếu chúng ta cũng có mã thông báo CSRF với chữ ký hợp lệ số ngẫu nhiên trong cookie xác thực CSRF khớp với số ngẫu nhiên trong mã thông báo CSRF đó, sau đó JavaScript cho mã thông báo đó cũng được nhận trong chính yêu cầu trước đó trong đó cookie CSRF được đặt, do đó cũng sử dụng trình duyệt. Sau đó, điều này cũng ngụ ý rằng mã JavaScript ở trên đã được thực thi trước khi mã thông báo được đặt và tại thời điểm đó miền hợp lệ cho khóa API nhất định.

      Vì vậy: máy chủ hiện có thể sử dụng khóa API một cách an toàn từ mã thông báo đã ký.

    7. Nếu tại bất kỳ thời điểm nào máy chủ không tin tưởng vào yêu cầu, thì lệnh 403 Forbidden sẽ được trả về. Tiện ích có thể đáp ứng điều đó bằng cách hiển thị cảnh báo cho người dùng.

Không bắt buộc phải ký vào cookie xác thực CSRF, vì chúng tôi đang so sánh nó với mã thông báo CSRF đã ký. Việc không ký cookie làm cho mỗi yêu cầu HTTP ngắn hơn và xác thực máy chủ nhanh hơn một chút.

Mã thông báo CSRF được tạo có giá trị vô thời hạn, nhưng chỉ kết hợp với cookie xác thực, hiệu quả cho đến khi trình duyệt bị đóng.

Chúng tôi có thể giới hạn thời gian tồn tại của chữ ký mã thông báo. Chúng tôi có thể xóa cookie xác thực CSRF khi người dùng đăng xuất, để đáp ứng khuyến nghị của OWASP . Và để không chia sẻ số ngẫu nhiên cho mỗi người dùng giữa nhiều đối tác, người ta có thể thêm khóa API vào tên cookie. Nhưng ngay cả khi đó, người ta không thể dễ dàng làm mới cookie xác thực CSRF khi mã thông báo mới được yêu cầu, vì người dùng có thể đang duyệt cùng một trang web trong nhiều cửa sổ, chia sẻ một cookie duy nhất (mà khi làm mới, sẽ được cập nhật trong tất cả các cửa sổ, sau đó Mã thông báo JavaScript trong các cửa sổ khác sẽ không còn khớp với cookie đó nữa).

Đối với những người sử dụng OAuth, hãy xem thêm OAuth và Tiện ích phía máy khách , từ đó tôi có ý tưởng về JavaScript. Đối với việc sử dụng API phía máy chủ , trong đó chúng tôi không thể dựa vào mã JavaScript để giới hạn miền, chúng tôi đang sử dụng khóa bí mật thay vì khóa API công khai.


1
Khi sử dụng CORS, có thể người ta có thể mở rộng điều đó một cách an toàn. Thay vì những điều trên, khi xử lý một OPTIONSyêu cầu chuyển trước bằng một số khóa API công khai trong URL, máy chủ có thể cho trình duyệt biết miền nào được phép (hoặc hủy yêu cầu). Hãy lưu ý rằng một số yêu cầu không yêu cầu yêu cầu bay trước hoặc hoàn toàn không sử dụng CORS và CORS cần IE8 +. Nếu một số dự phòng Flash được sử dụng cho IE7, thì có thể một số động crossdomain.xmlcó thể giúp đạt được điều đó. Chúng tôi chưa thử CORS / Flash.
Arjan

10

Câu hỏi này có một câu trả lời được chấp nhận nhưng chỉ để làm rõ, xác thực bí mật được chia sẻ hoạt động như thế này:

  1. Máy khách có khóa công khai, khóa này có thể được chia sẻ với bất kỳ ai, không quan trọng, vì vậy bạn có thể nhúng nó vào javascript. Điều này được sử dụng để xác định người dùng trên máy chủ.
  2. Máy chủ có khóa bí mật và bí mật này PHẢI được bảo vệ. Do đó, xác thực khóa chia sẻ yêu cầu bạn có thể bảo vệ khóa bí mật của mình. Vì vậy, một ứng dụng khách javascript công cộng kết nối trực tiếp với một dịch vụ khác là không thể vì bạn cần một người trung gian máy chủ để bảo vệ bí mật.
  3. Máy chủ ký yêu cầu bằng cách sử dụng một số thuật toán bao gồm khóa bí mật (khóa bí mật giống như muối) và tốt nhất là dấu thời gian sau đó gửi yêu cầu đến dịch vụ. Dấu thời gian là để ngăn chặn các cuộc tấn công "phát lại". Chữ ký của một yêu cầu chỉ có giá trị trong khoảng n giây. Bạn có thể kiểm tra điều đó trên máy chủ bằng cách lấy tiêu đề dấu thời gian phải chứa giá trị của dấu thời gian được bao gồm trong chữ ký. Nếu dấu thời gian đó hết hạn, yêu cầu không thành công.
  4. Dịch vụ nhận được yêu cầu không chỉ chứa chữ ký mà còn chứa tất cả các trường đã được ký bằng văn bản thuần túy.
  5. Sau đó, dịch vụ ký yêu cầu theo cách tương tự bằng cách sử dụng khóa bí mật được chia sẻ và so sánh các chữ ký.

Đúng vậy, nhưng do thiết kế câu trả lời của bạn không tiếp xúc với khóa API. Tuy nhiên, trong một số API khóa API hiển thị công khai, và đó là những gì các câu hỏi về: "yêu cầu một hoạt động dịch vụ còn lại [...] thực hiện thông qua javascript (XHR / Ajax)" . (Câu trả lời được chấp nhận là sai về điều đó quá, tôi cảm thấy; quan điểm của bạn 2 là rõ ràng về điều đó, tốt.)
Arjan

1

Tôi cho rằng ý bạn là khóa phiên không phải khóa API. Vấn đề đó được kế thừa từ giao thức http và được gọi là chiếm quyền điều khiển phiên . Như trên bất kỳ trang web nào, "giải pháp thay thế" thông thường là thay đổi thành https.

Để chạy dịch vụ REST an toàn, bạn phải bật https và có thể là xác thực ứng dụng khách. Nhưng sau tất cả, điều này nằm ngoài ý tưởng REST. REST không bao giờ nói về bảo mật.


8
Tôi thực sự có nghĩa là chìa khóa. Nếu tôi nhớ không lầm, để sử dụng API, bạn đang chuyển khóa API và bí mật cho dịch vụ còn lại để xác thực, đúng không? Tôi biết một khi nó được chuyển qua dây, nó sẽ được mã hóa bằng SSL, nhưng trước khi nó được gửi đi, điều đó hoàn toàn có thể nhìn thấy bởi mã khách hàng sử dụng nó ...
tjans

1

Những gì bạn muốn làm ở phía máy chủ là tạo một id phiên hết hạn được gửi lại cho máy khách khi đăng nhập hoặc đăng ký. Sau đó, khách hàng có thể sử dụng id phiên đó như một bí mật được chia sẻ để ký các yêu cầu tiếp theo.

Id phiên chỉ được chuyển một lần và id này PHẢI qua SSL.

Xem ví dụ tại đây

Sử dụng nonce và timestamp khi ký yêu cầu để ngăn chặn việc chiếm quyền điều khiển phiên.


1
Nhưng làm thế nào có thể có bất kỳ thông tin đăng nhập nào khi bên thứ ba sử dụng API của bạn? Nếu người dùng sẽ đăng nhập, thì mọi thứ thật dễ dàng: chỉ cần sử dụng một phiên? Nhưng khi các trang web khác cần xác thực API của bạn, điều đó không giúp được gì. (Ngoài ra, điều này có mùi rất giống như quảng cáo blog của bạn.)
Arjan

1

Tôi sẽ cố gắng trả lời câu hỏi trong ngữ cảnh ban đầu của nó. Vì vậy, câu hỏi đặt ra là "Khóa bí mật (API) có an toàn để được đặt trong JavaScript không.

Theo tôi nó rất không an toàn vì nó đánh bại mục đích xác thực giữa các hệ thống. Vì khóa sẽ được hiển thị cho người dùng, người dùng có thể truy xuất thông tin mà họ không được phép. Bởi vì trong một giao tiếp nghỉ ngơi điển hình xác thực chỉ dựa trên Khóa API.

Một giải pháp theo ý kiến ​​của tôi là cuộc gọi JavaScript về cơ bản chuyển yêu cầu đến một thành phần máy chủ nội bộ, người chịu trách nhiệm thực hiện lệnh gọi nghỉ. Thành phần máy chủ nội bộ, giả sử một Servlet sẽ đọc khóa API từ một nguồn được bảo mật như hệ thống tệp dựa trên quyền, chèn vào tiêu đề HTTP và thực hiện lệnh gọi phần còn lại bên ngoài.

Tôi hi vọng cái này giúp đượ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.