Tôi đang viết một ứng dụng (Django, nó rất hay xảy ra) và tôi chỉ muốn biết ý tưởng thực sự của "mã thông báo CSRF" là gì và cách nó bảo vệ dữ liệu. Dữ liệu bài đăng có an toàn không nếu bạn không sử dụng mã thông báo CSRF?
Tôi đang viết một ứng dụng (Django, nó rất hay xảy ra) và tôi chỉ muốn biết ý tưởng thực sự của "mã thông báo CSRF" là gì và cách nó bảo vệ dữ liệu. Dữ liệu bài đăng có an toàn không nếu bạn không sử dụng mã thông báo CSRF?
Câu trả lời:
www.mybank.com
mybank.com
sẽ dẫn đến một yêu cầu (về mặt khái niệm) hình thức http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>
. (Số tài khoản của bạn không cần thiết, bởi vì nó được ngụ ý bởi thông tin đăng nhập của bạn.)www.cute-cat-pictures.org
, không biết rằng đó là một trang web độc hại.mybank.com
(yêu cầu một số may mắn!), Họ có thể đưa vào trang của họ một yêu cầu như http://www.mybank.com/transfer?to=123456;amount=10000
( 123456
số tài khoản Quần đảo Cayman của họ và 10000
là số tiền mà trước đây bạn nghĩ rằng bạn rất vui khi sở hữu).www.cute-cat-pictures.org
trang đó , vì vậy trình duyệt của bạn sẽ thực hiện yêu cầu đó.www.mybank.com
cookie của bạn và nó sẽ trông hoàn toàn hợp pháp. Có tiền của bạn!Đây là thế giới không có mã thông báo CSRF .
Bây giờ cho phiên bản tốt hơn với mã thông báo CSRF :
http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
.mybank.com
sẽ bao gồm trên trang web của riêng họ khi họ phục vụ nó cho bạn. Nó khác nhau mỗi khi họ phục vụ bất kỳ trang nào cho bất kỳ ai.www.mybank.com
.Kết quả: Bạn giữ các 10000
đơn vị tiền tệ của bạn . Tôi đề nghị bạn nên tặng một số thứ đó cho Wikipedia.
(Số dặm của bạn có thể thay đổi.)
EDIT từ bình luận đáng đọc:
Sẽ đáng lưu ý rằng tập lệnh từ www.cute-cat-pictures.org
thông thường không có quyền truy cập vào mã thông báo chống CSRF của bạn từ www.mybank.com
điều khiển truy cập HTTP. Lưu ý này rất quan trọng đối với một số người gửi tiêu đề Access-Control-Allow-Origin: *
cho mọi phản hồi của trang web một cách vô lý mà không biết nó dùng để làm gì, chỉ vì họ không thể sử dụng API từ một trang web khác.
www.cute-cat-pictures.org
thông thường không có quyền truy cập vào mã thông báo chống CSRF của bạn từ www.mybank.com
điều khiển truy cập HTTP. Lưu ý này rất quan trọng đối với một số người gửi tiêu đề Access-Control-Allow-Origin: *
cho mọi phản hồi của trang web một cách vô lý mà không biết nó dùng để làm gì, chỉ vì họ không thể sử dụng API từ một trang web khác.
Có, dữ liệu bài viết là an toàn. Nhưng nguồn gốc của dữ liệu đó thì không. Bằng cách này, ai đó có thể lừa người dùng bằng JS đăng nhập vào trang web của bạn trong khi duyệt trang web của kẻ tấn công.
Để ngăn chặn điều đó, django sẽ gửi một khóa ngẫu nhiên cả trong cookie và dữ liệu biểu mẫu. Sau đó, khi người dùng POST, nó sẽ kiểm tra xem hai khóa có giống nhau không. Trong trường hợp người dùng bị lừa, trang web của bên thứ 3 không thể lấy cookie của trang web của bạn, do đó gây ra lỗi xác thực.
Trang web tạo ra một mã thông báo duy nhất khi nó tạo trang biểu mẫu. Mã thông báo này là bắt buộc để đăng / nhận dữ liệu trở lại máy chủ.
Vì mã thông báo được tạo bởi trang web của bạn và chỉ được cung cấp khi trang có biểu mẫu được tạo, một số trang web khác không thể bắt chước biểu mẫu của bạn - họ sẽ không có mã thông báo và do đó không thể đăng lên trang web của bạn.
Blog Cloud Under có một lời giải thích tốt về các mã thông báo CSRF.
Hãy tưởng tượng bạn có một trang web giống như một Twitter đơn giản hóa, được lưu trữ trên a.com. Người dùng đã đăng nhập có thể nhập một số văn bản (một tweet) vào một hình thức được gửi đến máy chủ dưới dạng yêu cầu POST và được xuất bản khi họ nhấn nút gửi. Trên máy chủ, người dùng được xác định bởi một cookie chứa ID phiên duy nhất của họ, vì vậy máy chủ của bạn biết ai đã đăng Tweet.
Các hình thức có thể đơn giản như vậy:
<form action="http://a.com/tweet" method="POST"> <input type="text" name="tweet"> <input type="submit"> </form>
Bây giờ hãy tưởng tượng, một kẻ xấu sao chép và dán biểu mẫu này vào trang web độc hại của anh ta, giả sử b.com. Các hình thức vẫn sẽ làm việc. Miễn là người dùng đã đăng nhập vào Twitter của bạn (tức là họ đã có cookie phiên hợp lệ cho a.com), yêu cầu POST sẽ được gửi đến
http://a.com/tweet
và xử lý như bình thường khi người dùng nhấp vào nút gửi.Cho đến nay, đây không phải là một vấn đề lớn miễn là người dùng nhận thức được chính xác biểu mẫu đó là gì, nhưng điều gì sẽ xảy ra nếu kẻ xấu của chúng ta điều chỉnh biểu mẫu như thế này:
<form action="https://example.com/tweet" method="POST"> <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad"> <input type="submit" value="Click to win!"> </form>
Bây giờ, nếu một trong những người dùng của bạn kết thúc trên trang web của kẻ xấu và truy cập vào Nhấp chuột để giành chiến thắng! nút, biểu mẫu được gửi đến trang web của bạn, người dùng được xác định chính xác bởi ID phiên trong cookie và Tweet ẩn được xuất bản.
Nếu kẻ xấu của chúng ta thậm chí còn tệ hơn, anh ta sẽ khiến người dùng vô tội gửi biểu mẫu này ngay khi họ mở trang web của mình bằng JavaScript, thậm chí có thể bị ẩn hoàn toàn trong một iframe vô hình. Điều này về cơ bản là giả mạo yêu cầu chéo trang web.
Một hình thức có thể dễ dàng được gửi từ mọi nơi đến mọi nơi. Nói chung đó là một tính năng phổ biến, nhưng có nhiều trường hợp quan trọng hơn là chỉ cho phép một biểu mẫu được gửi từ tên miền nơi nó thuộc về.
Mọi thứ thậm chí còn tồi tệ hơn nếu ứng dụng web của bạn không phân biệt giữa các yêu cầu POST và GET (ví dụ: trong PHP bằng cách sử dụng $ _REQUEST thay vì $ _POST). Đừng làm vậy! Yêu cầu thay đổi dữ liệu có thể được gửi dễ dàng như
<img src="http://a.com/tweet?tweet=This+is+really+bad">
, được nhúng trong một trang web độc hại hoặc thậm chí là một email.Làm cách nào để đảm bảo một mẫu đơn chỉ có thể được gửi từ trang web của riêng tôi? Đây là nơi mã thông báo CSRF xuất hiện. Mã thông báo CSRF là một chuỗi ngẫu nhiên, khó đoán. Trên trang có biểu mẫu bạn muốn bảo vệ, máy chủ sẽ tạo một chuỗi ngẫu nhiên, mã thông báo CSRF, thêm nó vào biểu mẫu dưới dạng trường ẩn và cũng có thể nhớ nó bằng cách nào đó, bằng cách lưu trữ trong phiên hoặc bằng cách đặt cookie chứa giá trị. Bây giờ mẫu sẽ trông như thế này:
<form action="https://example.com/tweet" method="POST"> <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn"> <input type="text" name="tweet"> <input type="submit"> </form>
Khi người dùng gửi biểu mẫu, máy chủ chỉ cần so sánh giá trị của trường đã đăng csrf-token (tên không quan trọng) với mã thông báo CSRF được máy chủ ghi nhớ. Nếu cả hai chuỗi bằng nhau, máy chủ có thể tiếp tục xử lý biểu mẫu. Nếu không, máy chủ sẽ ngay lập tức dừng xử lý biểu mẫu và phản hồi với một lỗi.
Tại sao điều này làm việc? Có một số lý do tại sao kẻ xấu từ ví dụ của chúng tôi ở trên không thể có được mã thông báo CSRF:
Sao chép mã nguồn tĩnh từ trang của chúng tôi sang một trang web khác sẽ vô ích, bởi vì giá trị của trường ẩn thay đổi theo từng người dùng. Nếu không có trang web của kẻ xấu biết mã thông báo CSRF của người dùng hiện tại, máy chủ của bạn sẽ luôn từ chối yêu cầu POST.
Vì trang độc hại của kẻ xấu được trình duyệt của người dùng của bạn tải từ một tên miền khác (b.com thay vì a.com), kẻ xấu không có cơ hội mã JavaScript, tải nội dung và do đó mã thông báo CSRF hiện tại của người dùng của chúng tôi trang web của bạn. Đó là bởi vì các trình duyệt web không cho phép các yêu cầu AJAX tên miền chéo theo mặc định.
Kẻ xấu cũng không thể truy cập cookie được đặt bởi máy chủ của bạn, vì tên miền sẽ không khớp.
Khi nào tôi nên bảo vệ chống giả mạo yêu cầu chéo trang web? Nếu bạn có thể đảm bảo rằng bạn không trộn lẫn các phương thức GET, POST và các yêu cầu khác như được mô tả ở trên, một khởi đầu tốt sẽ là bảo vệ tất cả các yêu cầu POST theo mặc định.
Bạn không phải bảo vệ các yêu cầu PUT và DELETE, vì như đã giải thích ở trên, một trình duyệt HTML tiêu chuẩn không thể được gửi bởi trình duyệt bằng các phương thức đó.
Mặt khác, JavaScript thực sự có thể tạo ra các loại yêu cầu khác, ví dụ như sử dụng hàm $ .ajax () của jQuery, nhưng hãy nhớ, để các yêu cầu AJAX hoạt động, các miền phải khớp (miễn là bạn không định cấu hình rõ ràng máy chủ web của mình) .
Điều này có nghĩa là, thường thì bạn thậm chí không phải thêm mã thông báo CSRF vào các yêu cầu AJAX, ngay cả khi chúng là các yêu cầu POST, nhưng bạn sẽ phải đảm bảo rằng bạn chỉ bỏ qua kiểm tra CSRF trong ứng dụng web của mình nếu yêu cầu POST thực sự là một Yêu cầu AJAX. Bạn có thể làm điều đó bằng cách tìm kiếm sự hiện diện của một tiêu đề như X-Requested-With, mà các yêu cầu AJAX thường bao gồm. Bạn cũng có thể đặt một tiêu đề tùy chỉnh khác và kiểm tra sự hiện diện của nó ở phía máy chủ. Điều đó an toàn, bởi vì trình duyệt sẽ không thêm tiêu đề tùy chỉnh vào việc gửi biểu mẫu HTML thông thường (xem bên trên), do đó, không có cơ hội nào để Mr Bad Guy mô phỏng hành vi này bằng một biểu mẫu.
Nếu bạn nghi ngờ về các yêu cầu AJAX, vì một số lý do, bạn không thể kiểm tra tiêu đề như X-Requested-With, chỉ cần chuyển mã thông báo CSRF được tạo vào JavaScript của bạn và thêm mã thông báo vào yêu cầu AJAX. Có một số cách để làm điều này; hoặc thêm nó vào tải trọng giống như một biểu mẫu HTML thông thường hoặc thêm tiêu đề tùy chỉnh vào yêu cầu AJAX. Miễn là máy chủ của bạn biết nơi tìm kiếm nó trong một yêu cầu đến và có thể so sánh nó với giá trị ban đầu mà nó nhớ từ phiên hoặc cookie, bạn đã được sắp xếp.
Căn nguyên của tất cả là đảm bảo rằng các yêu cầu đến từ người dùng thực tế của trang web. Mã thông báo csrf được tạo cho các biểu mẫu và Phải được gắn với các phiên của người dùng. Nó được sử dụng để gửi yêu cầu đến máy chủ, trong đó mã thông báo xác thực chúng. Đây là một cách để bảo vệ chống lại csrf, một cách khác sẽ là kiểm tra tiêu đề người giới thiệu.