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:
Khi của chúng tôi get-csrf-token.js?apiKey=abc123
được yêu cầu:
Tra cứu khóa abc123
trong cơ sở dữ liệu và nhận danh sách các miền hợp lệ cho khóa đó.
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ó.
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.)
Đặ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.domain
trướ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.com
thành chỉ example.com
, hoặc myblog.wordpress.com
thành wordpress.com
, hoặc trong một số trình duyệt thậm chí bbc.co.uk
thà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.
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.)
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:
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.
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.
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ợ.)
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.
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.
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ệ và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ý.
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.