Mã thông báo CSRF là gì? Tầm quan trọng của nó là gì và nó hoạt động như thế nào?


629

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?


13
Đó là một mã thông báo bí mật, dành riêng cho người dùng trong tất cả các lần gửi mẫu và URL hiệu ứng phụ để ngăn các giả mạo yêu cầu trên trang web chéo. Thêm thông tin ở đây: vi.wikipedia.org/wiki/Cross-site_Vquest_forgery
Robert Harvey

1
có vẻ như có một ranh giới tốt giữa việc bảo vệ một câu hỏi và cấm nó vì quá rộng: D
anton1980

2
Từ Bảng gian lận phòng chống giả mạo yêu cầu chéo trang web (CSRF) của OWASP : " CSRF không cần thiết để tạo kịch bản chéo trang web. Tuy nhiên, bất kỳ lỗ hổng kịch bản chéo trang nào cũng có thể được sử dụng để đánh bại tất cả các kỹ thuật giảm thiểu CSRF [...]. Điều này là do tải trọng XSS có thể chỉ cần đọc bất kỳ trang nào trên trang web bằng cách sử dụng XMLHttpRequest [...]. Điều bắt buộc là không có lỗ hổng XSS nào để đảm bảo rằng các phòng thủ CSRF không thể bị phá vỡ. "
toraritte

Câu trả lời:


1497

Giả mạo yêu cầu chéo trang web (CSRF) bằng những từ đơn giản

  • Giả sử bạn hiện đang đăng nhập vào ngân hàng trực tuyến của mình tại www.mybank.com
  • Giả sử chuyển tiền từ mybank.comsẽ 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.)
  • Bạn truy cập www.cute-cat-pictures.org, không biết rằng đó là một trang web độc hại.
  • Nếu chủ sở hữu trang web đó biết hình thức của yêu cầu trên (dễ dàng!) Và đoán chính xác bạn đã đăng nhập 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( 123456số tài khoản Quần đảo Cayman của họ và 10000là số tiền mà trước đây bạn nghĩ rằng bạn rất vui khi sở hữu).
  • Bạn đã truy xuất www.cute-cat-pictures.orgtrang đó , vì vậy trình duyệt của bạn sẽ thực hiện yêu cầu đó.
  • Ngân hàng của bạn không thể nhận ra nguồn gốc của yêu cầu này: Trình duyệt web của bạn sẽ gửi yêu cầu cùng với www.mybank.comcookie 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 :

  • Yêu cầu chuyển được mở rộng với đối số thứ ba : http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Mã thông báo đó là một số ngẫu nhiên khổng lồ, không thể đoán được mybank.comsẽ 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.
  • Kẻ tấn công không thể đoán mã thông báo, không thể thuyết phục trình duyệt web của bạn đầu hàng nó (nếu trình duyệt hoạt động chính xác ...), và vì vậy kẻ tấn công sẽ không thể tạo yêu cầu hợp lệ, vì các yêu cầu với mã thông báo sai (hoặc không có mã thông báo) sẽ bị từ chối 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.orgthô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.


36
Và rõ ràng mã thông báo lý tưởng sẽ được đặt tên là mã thông báo chống -CSRF, nhưng tên có lẽ đủ phức tạp như vậy.
Lutz Prechelt

3
@LutzPrechelt cảm ơn bạn. tại sao javascript không thể có được bất kỳ mã thông báo xác thực nào từ trình duyệt?
BKSpurgeon

72
Sẽ đáng lưu ý rằng tập lệnh từ www.cute-cat-pictures.orgthô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.
SOFe

9
@AugustinRiedinger Nếu kẻ tấn công mở trang web trên máy tính của anh ta - vì họ không có cookie của người dùng đã đăng nhập - họ sẽ không nhận được mã thông báo csrf tương ứng (mỗi mã thông báo csrf chỉ có hiệu lực cho phiên người dùng cụ thể). Nếu kẻ tấn công cố tải trang web chứa mã thông báo trên máy tính của người dùng, với tập lệnh được đặt trong trang web hình ảnh mèo dễ thương, trình duyệt sẽ ngăn anh ta đọc www.mybank.com (và mã thông báo) vì cùng chính sách xuất xứ.
Marcel

13
@LutzPrechelt Tôi nghĩ rằng mã thông báo luôn khác nhau là không đủ, nó phải được ghép với một phiên và máy chủ phải kiểm tra xem mã thông báo mà nó nhận được được tạo cho phiên mà máy chủ nhận dạng bởi cookie đã nhận. Nếu không, tin tặc chỉ có thể truy cập mybank và nhận được một số mã thông báo hợp lệ. Vì vậy, nếu bạn sử dụng mã thông báo mới với mọi hình thức bạn phải lưu nó được ghép nối với sessionid trên máy chủ. Có thể dễ dàng hơn để sử dụng cùng một mã thông báo cho mỗi phiên.
Marcel

222

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.


@DmitryShevchenko Xin chào, cố gắng hiểu phương thức cookie + hình thức nhập này khác với việc chỉ xác nhận người giới thiệu ở phía máy chủ như thế nào? Tất cả các ví dụ mà tôi tìm thấy có liên quan đến một hacker lừa người dùng đăng bài từ trang web của anh ta lên trang web thực tế.
Ethan

Ok, tôi đã tìm ra lý do tại sao người giới thiệu không được sử dụng. Nó bị chặn trong nhiều trường hợp vì đôi khi nó được coi là giữ thông tin nhạy cảm. Các công ty và proxy của họ thường làm điều đó. Tuy nhiên, nếu HTTPS được sử dụng, thì nhiều khả năng nó sẽ không bị chặn.
Ethan

4
Thật dễ dàng để thay đổi người giới thiệu, tôi sẽ không nói rằng đó là một thông tin đáng tin cậy. Tuy nhiên, mã thông báo CSRF được tạo bằng khóa bí mật của máy chủ và thường được gắn với người dùng
Dmitry Shevchenko

1
Tôi thực sự không hiểu tại sao đây là một mối đe dọa an ninh. Người dùng sẽ được đăng nhập vào một trang web khác ... nhưng trang web ban đầu sẽ không có cách nào để lấy thông tin đó. Đúng?
Aakil Fernandes

6
Chà, giả sử tôi tiêm một iframe độc ​​hại của " bank.com/transfer?from=x&to=y " trong một, giả sử, Facebook.com. Nếu bạn là khách hàng của bank.com và bạn truy cập Facebook, iframe đó sẽ tải trang ngân hàng, với cookie của bạn (vì trình duyệt sẽ gửi chúng cùng với một tên miền đã biết) và thực hiện chuyển tiền. Không có bạn biết bất cứ điều gì.
Dmitry Shevchenko

74

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.


10
Người dùng có thể lấy đầu ra mã thông báo trong nguồn, lấy cookie được gửi cho họ và sau đó từ trang web của bên thứ 3 gửi không?
Jack Marchetti

9
@JackMarchetti có. nhưng sẽ rất tốn kém vì mỗi lần bạn muốn gửi biểu mẫu từ trang web của bên thứ 3, bạn phải tải trang và phân tích mã thông báo. Mã thông báo CSRF nên được kết hợp lý tưởng với các hình thức bảo mật khác nếu bạn quan tâm đến vectơ tấn công này
tkone

4
Tôi có cùng câu hỏi với @JackMarchetti, điều không rõ ràng là - nếu mã thông báo CSRF thay đổi trên mỗi lần đăng nhập. Nếu nó giữ nguyên, điều gì sẽ ngăn kẻ tấn công đăng nhập lần đầu, lấy mã thông báo yêu cầu và sau đó chèn mã thông báo đó vào cuộc tấn công?
Paul Preibisch

7
@PaulPreibisch nó sẽ thay đổi trên mỗi lần tải trang - không phải trên mỗi lần đăng nhập. Bằng cách này, kẻ tấn công sẽ phải yêu cầu trang mỗi lần họ muốn gửi biểu mẫu. Làm cho nó khó khăn hơn nhiều.
tkone

9
@tkone, nó không thực sự làm cho nó khó khăn hơn nhiều. Nếu chỉ tăng gấp đôi số lượng nỗ lực và thời gian. Nó không thêm bất kỳ loại xử lý cấm. Thủ thuật này cũng liên kết mã thông báo CSRF với cookie dành riêng cho tên miền và gửi cookie này cùng với biểu mẫu. Cả cookie và dữ liệu bài mẫu sẽ phải được gửi đến máy chủ theo yêu cầu POST. Cách này sẽ yêu cầu một cuộc tấn công Cookie-Hijacking để có thể mô phỏng một yêu cầu hợp pháp.
Pedro Cordeiro

56

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/tweetvà 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ảm ơn về thông tin chi tiết. Trong yêu cầu bài đăng, trang web phải gửi mã thông báo csrf đến máy chủ, vậy khi nào khách hàng sẽ gửi mã thông báo csrf này đến máy chủ? Có phải trong khi thực hiện các yêu cầu tùy chọn preflight? Xin hãy thảo luận về phần này ..
Sm Srikanth

@Dan Tại sao b.com có ​​thể truy cập cookie của một trang web khác a.com?
zakir

8

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.


7
Không dựa vào tiêu đề người giới thiệu, nó có thể dễ dàng bị làm giả.
kag

3
Đây là câu trả lời chính xác! Mã thông báo PHẢI được gắn với một phiên trên máy chủ. So sánh dữ liệu Cookie + Biểu mẫu giống như câu trả lời được bình chọn nhiều nhất cho thấy là hoàn toàn sai. Các thành phần này đều là một phần của yêu cầu mà khách hàng xây dựng.
Lee Davis

3
Thật ra, không. Mã thông báo PHẢI được gắn với mỗi YÊU CẦU với Máy chủ. Nếu bạn chỉ buộc nó vào phiên, thì bạn sẽ gặp rủi ro ai đó đánh cắp mã thông báo của phiên và gửi yêu cầu với mã thông báo đó. Vì vậy, để an toàn tối đa, mã thông báo phải được gắn với mỗi http cần thiết nhất.
chrisl08
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.