Hiểu mã thông báo xác thực Rails


983

Tôi đang gặp một số vấn đề liên quan đến Mã xác thực trong Rails, như tôi đã nhiều lần rồi.

Nhưng tôi thực sự không muốn chỉ giải quyết vấn đề này và tiếp tục. Tôi thực sự muốn hiểu mã thông báo xác thực. Vâng, câu hỏi của tôi là, bạn có một số nguồn thông tin đầy đủ về chủ đề này hoặc bạn sẽ dành thời gian để giải thích chi tiết ở đây?


7
Đồng thời xem: "Tại sao Google Chuẩn bị trong khi (1) phản hồi JSON của họ?" stackoverflow.com/questions/2669690/ cường
Chloe

Câu trả lời:


1463

Chuyện gì xảy ra

Khi người dùng xem biểu mẫu để tạo, cập nhật hoặc hủy tài nguyên, ứng dụng Rails sẽ tạo ngẫu nhiên authenticity_token, lưu trữ mã thông báo này trong phiên và đặt nó vào một trường ẩn trong biểu mẫu. Khi người dùng gửi biểu mẫu, Rails tìm kiếm authenticity_token, so sánh nó với biểu mẫu được lưu trữ trong phiên và nếu chúng khớp với yêu cầu thì được phép tiếp tục.

Tại sao nó xảy ra

Vì mã thông báo xác thực được lưu trữ trong phiên, khách hàng không thể biết giá trị của nó. Điều này ngăn mọi người gửi biểu mẫu đến ứng dụng Rails mà không xem biểu mẫu trong chính ứng dụng đó. Hãy tưởng tượng rằng bạn đang sử dụng dịch vụ A, bạn đã đăng nhập vào dịch vụ và mọi thứ đều ổn. Bây giờ hãy tưởng tượng rằng bạn đã sử dụng dịch vụ B và bạn đã thấy một hình ảnh bạn thích và nhấn vào hình ảnh để xem kích thước lớn hơn của nó. Bây giờ, nếu có một số mã xấu ở dịch vụ B, nó có thể gửi yêu cầu đến dịch vụ A (mà bạn đã đăng nhập) và yêu cầu xóa tài khoản của bạn, bằng cách gửi yêu cầu đến http://serviceA.com/close_account. Đây là những gì được gọi là CSRF (Giả mạo yêu cầu trang web chéo) .

Nếu dịch vụ A đang sử dụng mã thông báo xác thực, vectơ tấn công này không còn được áp dụng nữa, vì yêu cầu từ dịch vụ B sẽ không chứa mã thông báo xác thực chính xác và sẽ không được phép tiếp tục.

Tài liệu API mô tả chi tiết về thẻ meta:

Bảo vệ CSRF được bật bằng protect_from_forgeryphương thức kiểm tra mã thông báo và đặt lại phiên nếu nó không khớp với những gì được mong đợi. Một cuộc gọi đến phương thức này được tạo cho các ứng dụng Rails mới theo mặc định. Tham số mã thông báo được đặt tên authenticity_tokentheo mặc định. Tên và giá trị của mã thông báo này phải được thêm vào mọi bố cục biểu hiện các biểu mẫu bằng cách đưa csrf_meta_tagsvào phần đầu HTML.

Ghi chú

Hãy ghi nhớ, Rails chỉ xác minh các phương thức không phải là idempotent (POST, PUT / PATCH và DELETE). Yêu cầu GET không được kiểm tra mã thông báo xác thực. Tại sao? bởi vì đặc tả HTTP nói rằng các yêu cầu GET là idempotent và không nên tạo, thay đổi hoặc hủy tài nguyên tại máy chủ và yêu cầu phải là idempotent (nếu bạn chạy cùng một lệnh nhiều lần, bạn sẽ nhận được cùng một kết quả mỗi lần).

Ngoài ra, việc thực hiện thực tế phức tạp hơn một chút như được xác định ngay từ đầu, đảm bảo an ninh tốt hơn. Rails không phát hành cùng một mã thông báo được lưu trữ với mọi hình thức. Nó cũng không tạo và lưu trữ một mã thông báo khác nhau mỗi lần. Nó tạo và lưu trữ một hàm băm mật mã trong một phiên và phát hành các mã thông báo mã hóa mới, có thể được khớp với mã được lưu trữ, mỗi khi một trang được hiển thị. Xem request_forgery_protection.rb .

Những bài học

Sử dụng authenticity_tokenđể bảo vệ các phương thức không cần thiết của bạn (POST, PUT / PATCH và DELETE). Đồng thời đảm bảo không cho phép bất kỳ yêu cầu GET nào có khả năng sửa đổi tài nguyên trên máy chủ.


EDIT: Kiểm tra nhận xét của @erturne về các yêu cầu GET là không cần thiết. Ông giải thích nó theo cách tốt hơn tôi đã làm ở đây.


25
@Faisal, có thể sau đó, kẻ tấn công chỉ cần đọc / nắm bắt phần tử 'ẩn' của biểu mẫu cho Dịch vụ A và nhận mã thông báo duy nhất đó được tạo cho người dùng - với điều kiện là họ đã có quyền truy cập vào phiên do người dùng bắt đầu cho dịch vụ A?
marcamillion

11
@marcamillion: Nếu ai đó chiếm quyền điều khiển phiên của bạn tại dịch vụ A, thì mã thông báo xác thực sẽ không bảo vệ bạn. Kẻ không tặc sẽ có thể gửi yêu cầu và nó sẽ được phép tiến hành.
Faisal

12
@zabba: Rails tăng một ngoại lệ ActionControll :: UnlimitedAuthenticityToken nếu một biểu mẫu được gửi mà không có mã thông báo thích hợp. Bạn có thể cứu_ từ ngoại lệ và làm bất cứ điều gì bạn muốn.
Faisal

5
lại "Cũng đảm bảo không thực hiện bất kỳ yêu cầu GET nào có khả năng sửa đổi tài nguyên trên máy chủ." - điều này bao gồm việc không sử dụng match () trong các tuyến có khả năng cho phép các yêu cầu GET để điều khiển các hành động dự định chỉ nhận POST
Steven Soroka

102
"... và yêu cầu phải là idempotent (nếu bạn chạy cùng một lệnh nhiều lần, bạn sẽ nhận được kết quả giống nhau mỗi lần)." Chỉ cần một sự làm rõ tinh tế ở đây. An toàn có nghĩa là không có tác dụng phụ. Idempotent có nghĩa là tác dụng phụ tương tự cho dù dịch vụ được gọi bao nhiêu lần. Tất cả các dịch vụ an toàn vốn là bình thường vì không có tác dụng phụ. Gọi GET trên tài nguyên thời gian hiện tại nhiều lần sẽ trả về một kết quả khác nhau mỗi lần, nhưng nó an toàn (và do đó không có tác dụng).
vào

137

Mã thông báo xác thực được thiết kế để bạn biết biểu mẫu của mình đang được gửi từ trang web của bạn. Nó được tạo ra từ máy mà nó chạy với một mã định danh duy nhất mà chỉ máy của bạn có thể biết, do đó giúp ngăn chặn các cuộc tấn công giả mạo yêu cầu chéo trang.

Nếu bạn chỉ gặp khó khăn với đường ray từ chối quyền truy cập tập lệnh AJAX của mình, bạn có thể sử dụng

<%= form_authenticity_token %>

để tạo mã thông báo chính xác khi bạn đang tạo biểu mẫu của mình.

Bạn có thể đọc thêm về nó trong tài liệu .


88

CSRF là gì?

Mã xác thực là một biện pháp đối phó với giả mạo yêu cầu chéo trang web (CSRF). CSRF là gì, bạn hỏi?

Đó là một cách mà kẻ tấn công có thể có khả năng chiếm quyền điều khiển phiên mà không cần biết mã thông báo phiên.

Kịch bản :

  • Truy cập trang web của ngân hàng của bạn, đăng nhập.
  • Sau đó truy cập trang web của kẻ tấn công (ví dụ: quảng cáo được tài trợ từ một tổ chức không đáng tin cậy).
  • Trang của kẻ tấn công bao gồm biểu mẫu có cùng các trường với biểu mẫu "Chuyển tiền" của ngân hàng.
  • Kẻ tấn công biết thông tin tài khoản của bạn và có các trường mẫu điền sẵn để chuyển tiền từ tài khoản của bạn sang tài khoản của kẻ tấn công.
  • Trang của Attacker bao gồm Javascript gửi biểu mẫu đến ngân hàng của bạn.
  • Khi biểu mẫu được gửi, trình duyệt bao gồm cookie của bạn cho trang web ngân hàng, bao gồm mã thông báo phiên.
  • Chuyển tiền vào tài khoản của kẻ tấn công.
  • Biểu mẫu có thể ở dạng iframe vô hình, vì vậy bạn không bao giờ biết cuộc tấn công xảy ra.
  • Điều này được gọi là giả mạo yêu cầu chéo trang web (CSRF).

Giải pháp CSRF :

  • Máy chủ có thể đánh dấu các biểu mẫu xuất phát từ chính máy chủ
  • Mỗi biểu mẫu phải chứa mã thông báo xác thực bổ sung dưới dạng trường ẩn.
  • Mã thông báo phải không thể đoán trước (kẻ tấn công không thể đoán được).
  • Máy chủ cung cấp mã thông báo hợp lệ trong các biểu mẫu trong các trang của nó.
  • Máy chủ kiểm tra mã thông báo khi biểu mẫu được đăng, từ chối biểu mẫu mà không có mã thông báo phù hợp.
  • Mã thông báo ví dụ: mã định danh phiên được mã hóa bằng khóa bí mật của máy chủ.
  • Rails tự động tạo các mã thông báo như vậy: xem trường nhập xác thực_token ở mọi dạng.

1
Đây là một phiên bản của lời giải thích tương tự ít chính xác hơn nhưng cũng không trừu tượng hơn: stackoverflow.com/a/33829607/2810305
Lutz Prechelt

Tôi không chắc chắn, nhưng các trình duyệt hiện đại có cho phép gửi các yêu cầu không cần thiết (POST / PUT / DELETE) sang tên miền khác không? Tôi đoán, phải có sự bảo vệ chống lại những thứ như vậy trong chính trình duyệt
splitByZero

45

Ví dụ tấn công tối thiểu sẽ được ngăn chặn: CSRF

Trên trang web của evil.comtôi, tôi thuyết phục bạn gửi mẫu sau:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="to"      value="ciro"></p>
  <p><input type="hidden" name="ammount" value="100"></p>
  <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>

Nếu bạn đã đăng nhập vào ngân hàng của mình thông qua cookie phiên, thì cookie sẽ được gửi và việc chuyển tiền sẽ được thực hiện mà bạn không hề biết.

Đó là mã thông báo CSRF được sử dụng:

  • với phản hồi GET trả về biểu mẫu, Rails gửi một tham số ẩn ngẫu nhiên rất dài
  • khi trình duyệt thực hiện yêu cầu POST, nó sẽ gửi tham số và máy chủ sẽ chỉ chấp nhận nếu phù hợp

Vì vậy, biểu mẫu trên một trình duyệt xác thực sẽ trông như sau:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
  <p><input type="hidden" name="to"                 value="ciro"></p>
  <p><input type="hidden" name="ammount"            value="100"></p>
  <p><button type="submit">Send 100$ to Ciro.</button></p>
</form>

Do đó, cuộc tấn công của tôi sẽ thất bại, vì nó không gửi authenticity_token tham số, và không có cách nào tôi có thể đoán được vì đây là một con số ngẫu nhiên rất lớn.

Kỹ thuật ngăn chặn này được gọi là Mẫu mã thông báo đồng bộ hóa .

Chính sách xuất xứ giống nhau

Nhưng điều gì sẽ xảy ra nếu kẻ tấn công thực hiện hai yêu cầu với JavaScript, một yêu cầu đọc mã thông báo và yêu cầu thứ hai để thực hiện chuyển tiền?

Chỉ riêng mẫu mã thông báo đồng bộ hóa là không đủ để ngăn chặn điều đó!

Đây là nơi Chính sách nguồn gốc tương tự được giải cứu, như tôi đã giải thích tại: /security/8264/why-is-the-same-origin-policy-so-important/72569# 72569

Cách Rails gửi mã thông báo

Được bảo hiểm tại: Rails: Làm thế nào csrf_meta_tag hoạt động?

Về cơ bản:

  • Người trợ giúp HTML muốn form_tagthêm trường ẩn vào biểu mẫu cho bạn nếu đó không phải là biểu mẫu GET

  • AJAX được xử lý tự động bởi jquery-ujs , đọc mã thông báo từ các metathành phần được thêm vào tiêu đề của bạn bằng cách csrf_meta_tags(hiện trong mẫu mặc định) và thêm nó vào bất kỳ yêu cầu nào được đưa ra.

    uJS cũng cố gắng cập nhật mã thông báo dưới dạng trong các đoạn được lưu trữ đã lỗi thời.

Phương pháp phòng ngừa khác


Cảm ơn bạn, nhưng quan điểm của bạn về việc dựa vào chính sách xuất xứ tương tự để không thể chỉ đọc mã thông báo CSRF trước tiên có vẻ thiếu sót. Vì vậy, trước tiên bạn nói rằng bạn có thể POST đến một nguồn gốc khác nhưng không thể đọc từ nó, có vẻ lạ nhưng tôi đoán đó là chính xác, nhưng bạn có thể tiêm thẻ hình ảnh hoặc tập lệnh bằng cách truy cập trang và liên kết trình xử lý để phân tích phản hồi và có được không?
bjm88

@ bjm88 tiêm script ở đâu? Trên trang web của bạn, hoặc trên trang web bị tấn công? Nếu trang web bị tấn công, cho phép tiêm script là một lỗ hổng bảo mật nổi tiếng và làm hiệu quả trang web. Mỗi trang web phải chống lại nó thông qua vệ sinh đầu vào. Đối với hình ảnh, tôi không thấy cách chúng có thể được sử dụng cho một cuộc tấn công. Trên trang web tấn công: bạn có thể sửa đổi trình duyệt của mình để cho phép đọc và do đó tự động cầm đồ theo ý muốn :-) nhưng các trình duyệt phong nha ngăn chặn nó theo mặc định, hãy dùng thử.
Ciro Santilli 冠状 病毒 审查 事件

43

Mã thông báo xác thực được sử dụng để ngăn chặn các cuộc tấn công giả mạo yêu cầu chéo trang web (CSRF). Để hiểu mã thông báo xác thực, trước tiên bạn phải hiểu các cuộc tấn công CSRF.

CSRF

Giả sử bạn là tác giả của bank.com. Bạn có một biểu mẫu trên trang web của mình được sử dụng để chuyển tiền sang một tài khoản khác với yêu cầu NHẬN:

nhập mô tả hình ảnh ở đây

Một hacker có thể gửi yêu cầu HTTP đến máy chủ GET /transfer?amount=$1000000&account-to=999999, phải không?

nhập mô tả hình ảnh ở đây

Sai lầm. Các tin tặc tấn công sẽ không hoạt động. Các máy chủ về cơ bản sẽ nghĩ gì?

Huh? Anh chàng này đang cố gắng để bắt đầu một sự chuyển giao. Đó không phải là chủ sở hữu của tài khoản, đó là điều chắc chắn.

Làm thế nào để máy chủ biết điều này? Bởi vì không cósession_id cookie xác thực người yêu cầu.

Khi bạn đăng nhập bằng tên người dùng và mật khẩu, máy chủ sẽ đặt session_idcookie trên trình duyệt của bạn. Bằng cách đó, bạn không phải xác thực từng yêu cầu bằng tên người dùng và mật khẩu của mình. Khi trình duyệt của bạn gửi session_idcookie, máy chủ sẽ biết:

Ồ, đó là John Doe. Anh ấy đã đăng nhập thành công 2,5 phút trước. Anh ấy tốt để đi.

Một hacker có thể nghĩ:

Hừm. Một yêu cầu HTTP bình thường sẽ không hoạt động, nhưng nếu tôi có thể chạm tay vào session_idcookie đó , tôi sẽ rất tuyệt vời.

Trình duyệt người dùng có một loạt các cookie được đặt cho bank.comtên miền. Mỗi khi người dùng đưa ra yêu cầu cho bank.comtên miền, tất cả các cookie sẽ được gửi cùng. Bao gồm cảsession_id cookie.

Vì vậy, nếu một hacker có thể có được bạn thực hiện yêu cầu GET chuyển tiền vào tài khoản của mình, anh ta sẽ thành công. Làm thế nào anh ta có thể lừa bạn làm như vậy? Với giả mạo yêu cầu trang web chéo.

Nó thực sự đơn giản. Các hacker chỉ có thể khiến bạn truy cập trang web của mình. Trên trang web của mình, anh ta có thể có thẻ hình ảnh sau:

<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">

Khi trình duyệt người dùng bắt gặp thẻ hình ảnh đó, nó sẽ tạo một yêu cầu GET cho url đó. Và vì yêu cầu đến từ trình duyệt của anh ấy, nên nó sẽ gửi cùng với tất cả các cookie được liên kết bank.com. Nếu người dùng gần đây đã đăng nhập vào bank.com... session_idcookie sẽ được đặt và máy chủ sẽ nghĩ rằng người dùng có nghĩa là chuyển 1.000.000 đô la vào tài khoản 999999!

nhập mô tả hình ảnh ở đây

Chà, đừng truy cập các trang web nguy hiểm và bạn sẽ ổn thôi.

Điều đó là không đủ. Điều gì nếu một ai đó đăng hình ảnh đó lên Facebook và nó xuất hiện trên tường của bạn? Điều gì xảy ra nếu nó được đưa vào một trang web bạn đang truy cập bằng một cuộc tấn công XSS?

Nó không quá tệ. Chỉ các yêu cầu GET là dễ bị tổn thương.

Không đúng. Một hình thức gửi yêu cầu POST có thể được tạo động. Dưới đây là ví dụ từ Hướng dẫn Rails về Bảo mật :

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

Mã xác thực

Khi bạn ApplicationControllercó thứ này:

protect_from_forgery with: :exception

Điều này:

<%= form_tag do %>
  Form contents
<% end %>

Được tổng hợp vào đây:

<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

Cụ thể, những điều sau đây được tạo ra:

<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />

Để bảo vệ chống lại các cuộc tấn công CSRF, nếu Rails không thấy mã thông báo xác thực được gửi cùng với yêu cầu, nó sẽ không xem xét yêu cầu đó an toàn.

Làm thế nào một kẻ tấn công được cho là biết mã thông báo này là gì? Một giá trị khác nhau được tạo ngẫu nhiên mỗi khi biểu mẫu được tạo:

nhập mô tả hình ảnh ở đây

Một cuộc tấn công Cross Site Scripting (XSS) - đó là cách. Nhưng đó là một lỗ hổng khác nhau cho một ngày khác nhau.


39

Các Authenticity Tokenlà phương pháp để đường ray ngăn chặn 'cross-site yêu cầu giả mạo tấn công (CSRF hay XSRF) .

Nói một cách đơn giản, đảm bảo rằng các yêu cầu PUT / POST / DELETE (phương thức có thể sửa đổi nội dung) cho ứng dụng web của bạn được tạo từ trình duyệt của khách hàng chứ không phải từ bên thứ ba (kẻ tấn công) có quyền truy cập vào cookie được tạo về phía khách hàng


34

Authenticity Tokennó rất quan trọng và trong Rails 3.0+ bạn có thể sử dụng

 <%= token_tag nil %>

để tạo ra

<input name="authenticity_token" type="hidden" value="token_value">

bất cứ nơi nào


Điều này rất hữu ích với tôi. Tôi thực sự đã cố gắng thực hiện XSStrên trang đăng nhập, không phải vì mục đích bất chính, mà là để tạo một phiên mới với tên người dùng được điền sẵn. Bây giờ tôi biết tôi chỉ có thể sử dụng value="token_value".
Michael - Clay Shirky ở đâu

27

Coi chừng cơ chế Mã xác thực có thể dẫn đến các điều kiện cuộc đua nếu bạn có nhiều yêu cầu đồng thời từ cùng một khách hàng. Trong tình huống này, máy chủ của bạn có thể tạo nhiều mã thông báo xác thực khi chỉ có một và khách hàng nhận được mã thông báo trước đó trong một biểu mẫu sẽ không thành công theo yêu cầu tiếp theo vì mã thông báo cookie phiên đã bị ghi đè. Có một bài viết về vấn đề này và một giải pháp không hoàn toàn tầm thường ở đây: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-simes-and-how-to-fix-them/


11

Phương pháp ở đâu authenticity_tokenlà bắt buộc

authenticity_token được yêu cầu trong trường hợp các phương thức idempotent như post, put và xóa, bởi vì các phương thức Idempotent đang ảnh hưởng đến dữ liệu.

Tại sao cần thiết

Nó là cần thiết để ngăn chặn từ hành động xấu xa. xác thực_token được lưu trữ trong phiên, bất cứ khi nào một biểu mẫu được tạo trên các trang web để tạo hoặc cập nhật lên tài nguyên thì mã thông báo xác thực được lưu trữ trong trường ẩn và nó được gửi với biểu mẫu trên máy chủ. Trước khi thực hiện hành động, người dùng đã gửi xác thực_token được kiểm tra chéo với authenticity_tokenlưu trữ trong phiên. Nếu authenticity_tokengiống nhau thì quá trình được tiếp tục nếu không nó không thực hiện hành động.


3
Trên thực tế, nó không phải là ngược lại? GET là idempotent vì lệnh gọi của nó không nên thay đổi trạng thái của hệ thống, trong đó các động từ PUT POST và DELETE KHÔNG phải là động từ idempotent vì chúng thay đổi trạng thái hệ thống. IE: xác thực_token là bắt buộc trong trường hợp phương pháp KHÔNG bình thường.
Jean-Théo

2
@ Jean-Daube, uma: idempotent có nghĩa là nếu thực hiện hai lần, hành động chỉ xảy ra một lần. NHẬN, PUT và XÓA idempotent: w3.org/Prot Protocol / rfc2616 / rfc2616-sec9.html Thuộc tính khóa ở đây không phải là idempotency, nhưng nếu phương thức thay đổi hoặc không phải là dữ liệu, thì có được gọi là "Phương thức an toàn" hay không.
Ciro Santilli 冠状 病毒 审查 事件

6

Xác thực là gì?

Đây là một chuỗi ngẫu nhiên được sử dụng bởi ứng dụng rails để đảm bảo rằng người dùng đang yêu cầu hoặc thực hiện một hành động từ trang ứng dụng, chứ không phải từ một ứng dụng hoặc trang web khác.

Tại sao một xác thực_token là cần thiết?

Để bảo vệ ứng dụng hoặc trang web của bạn khỏi giả mạo yêu cầu chéo trang web.

Làm cách nào để thêm xác thực vào biểu mẫu?

Nếu bạn đang tạo một biểu mẫu bằng cách sử dụng thẻ form_for, thì xác thực sẽ tự động được thêm vào một biểu mẫu khác mà bạn có thể sử dụng <%= csrf_meta_tag %>.

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.