Memahami Token Keaslian Rel

983

Saya mengalami beberapa masalah tentang Token Keaslian di Rails, seperti yang saya miliki berkali-kali sekarang.

Tapi saya benar-benar tidak ingin menyelesaikan masalah ini dan teruskan. Saya benar-benar ingin memahami token Keaslian. Nah, pertanyaan saya adalah, apakah Anda memiliki sumber informasi lengkap tentang hal ini atau akankah Anda menghabiskan waktu untuk menjelaskan secara terperinci di sini?

Ricardo Acras
sumber
7
Lihat juga: "Mengapa Google Menempatkan sementara (1) ke respons JSON mereka?" stackoverflow.com/questions/2669690/…
Chloe

Jawaban:

1463

Apa yang terjadi

Saat pengguna melihat formulir untuk membuat, memperbarui, atau menghancurkan sumber daya, aplikasi Rails membuat acak authenticity_token, menyimpan token ini di sesi, dan menempatkannya di bidang tersembunyi di formulir. Ketika pengguna mengirimkan formulir, Rails mencari authenticity_token, membandingkannya dengan yang disimpan dalam sesi, dan jika mereka cocok dengan permintaan diizinkan untuk melanjutkan.

Kenapa itu terjadi?

Karena token keaslian disimpan dalam sesi, klien tidak dapat mengetahui nilainya. Ini mencegah orang mengirimkan formulir ke aplikasi Rails tanpa melihat formulir di dalam aplikasi itu sendiri. Bayangkan Anda menggunakan layanan A, Anda masuk ke layanan dan semuanya baik-baik saja. Sekarang bayangkan Anda pergi menggunakan layanan B, dan Anda melihat gambar yang Anda suka, dan menekan pada gambar untuk melihat ukuran yang lebih besar. Sekarang, jika ada kode jahat di layanan B, mungkin mengirim permintaan ke layanan A (yang Anda masuki), dan meminta untuk menghapus akun Anda, dengan mengirimkan permintaan ke http://serviceA.com/close_account. Inilah yang dikenal sebagai CSRF (Pemalsuan Permintaan Situs Lintas) .

Jika layanan A menggunakan token keaslian, vektor serangan ini tidak lagi berlaku, karena permintaan dari layanan B tidak akan berisi token keaslian yang benar, dan tidak akan diizinkan untuk melanjutkan.

API API menjelaskan detail tentang tag meta:

Perlindungan CSRF dihidupkan dengan protect_from_forgerymetode, yang memeriksa token dan me-reset sesi jika tidak sesuai dengan yang diharapkan. Panggilan ke metode ini dihasilkan untuk aplikasi Rails baru secara default. Parameter token dinamai authenticity_tokensecara default. Nama dan nilai token ini harus ditambahkan ke setiap tata letak yang membuat formulir dengan memasukkan csrf_meta_tagsdi kepala HTML.

Catatan

Perlu diingat, Rails hanya memverifikasi bukan metode idempoten (POST, PUT / PATCH dan DELETE). Permintaan GET tidak diperiksa untuk token keaslian. Mengapa? karena spesifikasi HTTP menyatakan bahwa permintaan GET adalah idempoten dan tidak boleh membuat, mengubah, atau menghancurkan sumber daya di server, dan permintaan tersebut harus idempoten (jika Anda menjalankan perintah yang sama beberapa kali, Anda harus mendapatkan hasil yang sama setiap kali).

Implementasi sebenarnya juga sedikit lebih rumit seperti yang didefinisikan di awal, memastikan keamanan yang lebih baik. Rails tidak mengeluarkan token tersimpan yang sama dengan setiap bentuk. Tidak juga menghasilkan dan menyimpan token yang berbeda setiap saat. Ini menghasilkan dan menyimpan hash kriptografi dalam sesi dan mengeluarkan token kriptografi baru, yang dapat dicocokkan dengan yang disimpan, setiap kali halaman diberikan. Lihat request_forgery_protection.rb .

Pelajaran

Gunakan authenticity_tokenuntuk melindungi metode tidak idempoten Anda (POST, PUT / PATCH, dan DELETE). Pastikan juga untuk tidak mengizinkan permintaan GET yang berpotensi memodifikasi sumber daya di server.


EDIT: Periksa komentar oleh @erturne tentang permintaan GET menjadi idempoten. Dia menjelaskannya dengan cara yang lebih baik daripada yang saya lakukan di sini.

Faisal
sumber
25
@Faisal, mungkinkah bagi penyerang untuk hanya membaca / menangkap elemen 'tersembunyi' dari formulir untuk Layanan A dan mendapatkan token unik yang dihasilkan untuk pengguna - mengingat bahwa mereka mendapatkan akses ke sesi yang dimulai oleh pengguna untuk Layanan A?
marcamillion
11
@marcamillion: Jika seseorang membajak sesi Anda di layanan A, maka token keaslian tidak akan melindungi Anda. Pembajak akan dapat mengajukan permintaan dan itu akan diizinkan untuk melanjutkan.
Faisal
12
@zabba: Rails memunculkan ActionController :: Pengecualian InvalidAuthenticityToken jika formulir dikirimkan tanpa token yang tepat. Anda dapat rescue_from pengecualian dan melakukan pemrosesan apa pun yang Anda inginkan.
Faisal
5
re "Juga pastikan untuk tidak membuat permintaan GET yang berpotensi memodifikasi sumber daya di server." - ini termasuk tidak menggunakan kecocokan () pada rute yang berpotensi memungkinkan GET permintaan untuk tindakan pengontrol yang dimaksudkan untuk hanya menerima POST
Steven Soroka
102
"... dan permintaan harus idempoten (jika Anda menjalankan perintah yang sama beberapa kali, Anda harus mendapatkan hasil yang sama setiap kali)." Hanya klarifikasi halus di sini. Aman berarti tidak ada efek samping. Idempoten berarti efek samping yang sama tidak peduli berapa kali suatu layanan dipanggil. Semua layanan yang aman pada dasarnya idempoten karena tidak ada efek samping. Memanggil GET pada sumber daya waktu saat ini beberapa kali akan menghasilkan hasil yang berbeda setiap kali, tetapi aman (dan karenanya idempoten).
erturne
137

Token keaslian dirancang agar Anda tahu formulir Anda dikirimkan dari situs web Anda. Ini dihasilkan dari mesin yang menjalankannya dengan pengenal unik yang hanya diketahui oleh mesin Anda, sehingga membantu mencegah serangan pemalsuan permintaan lintas situs.

Jika Anda hanya mengalami kesulitan dengan rel yang menolak akses skrip AJAX Anda, Anda dapat menggunakannya

<%= form_authenticity_token %>

untuk menghasilkan token yang benar ketika Anda membuat formulir Anda.

Anda dapat membaca lebih lanjut tentang itu di dokumentasi .

Topher Fangio
sumber
88

Apa itu CSRF?

Token Keaslian adalah tindakan balasan untuk Pemalsuan Permintaan Lintas Situs (CSRF). Apa itu CSRF, Anda bertanya?

Ini adalah cara penyerang berpotensi membajak sesi tanpa mengetahui token sesi.

Skenario :

  • Kunjungi situs bank Anda, masuk.
  • Kemudian kunjungi situs penyerang (mis. Iklan sponsor dari organisasi yang tidak dipercaya).
  • Halaman penyerang mencakup formulir dengan bidang yang sama dengan formulir "Transfer Dana" bank.
  • Penyerang mengetahui info akun Anda, dan memiliki bidang formulir yang sudah diisi sebelumnya untuk mentransfer uang dari akun Anda ke akun penyerang.
  • Halaman penyerang termasuk Javascript yang mengirimkan formulir ke bank Anda.
  • Ketika formulir dikirimkan, browser menyertakan cookie Anda untuk situs bank, termasuk token sesi.
  • Bank mentransfer uang ke rekening penyerang.
  • Bentuknya bisa dalam iframe yang tidak terlihat, sehingga Anda tidak pernah tahu serangan itu terjadi.
  • Ini disebut Pemalsuan Permintaan Lintas Situs (CSRF).

Solusi CSRF :

  • Server dapat menandai formulir yang berasal dari server itu sendiri
  • Setiap formulir harus berisi token otentikasi tambahan sebagai bidang tersembunyi.
  • Token harus tidak dapat diprediksi (penyerang tidak dapat menebaknya).
  • Server menyediakan token yang valid dalam formulir di halaman-halamannya.
  • Server memeriksa token ketika formulir diposting, menolak formulir tanpa token yang tepat.
  • Token contoh: pengidentifikasi sesi dienkripsi dengan kunci rahasia server.
  • Rails secara otomatis menghasilkan token tersebut: lihat bidang input authenticity_token di setiap formulir.
Rose Perrone
sumber
1
Berikut adalah versi penjelasan yang sama yang kurang tepat tetapi juga kurang abstrak: stackoverflow.com/a/33829607/2810305
Lutz Prechelt
Saya tidak yakin, tetapi, apakah browser modern memperbolehkan pengiriman bukan permintaan idempoten (POST / PUT / DELETE) ke domain lain? Saya kira, harus ada perlindungan terhadap hal-hal seperti itu di browser itu sendiri
divideByZero
45

Contoh serangan minimal yang akan dicegah: CSRF

Di situs web saya, evil.comsaya meyakinkan Anda untuk mengirimkan formulir berikut:

<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>

Jika Anda masuk ke bank melalui cookie sesi, maka cookie akan dikirim dan transfer akan dilakukan tanpa Anda menyadarinya.

Itulah saat token CSRF ikut berperan:

  • dengan respons GET yang mengembalikan formulir, Rails mengirimkan parameter tersembunyi acak yang sangat panjang
  • ketika browser membuat permintaan POST, itu akan mengirim parameter bersama, dan server hanya akan menerimanya jika cocok

Jadi bentuk pada browser yang otentik akan terlihat seperti:

<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>

Dengan demikian, serangan saya akan gagal, karena tidak mengirim authenticity_tokenparameter, dan tidak mungkin saya bisa menebaknya karena ini adalah angka acak yang sangat besar.

Teknik pencegahan ini disebut Pola Sinkronisasi Token .

Kebijakan Asal yang Sama

Tetapi bagaimana jika penyerang membuat dua permintaan dengan JavaScript, satu untuk membaca token, dan yang kedua untuk melakukan transfer?

Pola token sinkronisasi saja tidak cukup untuk mencegahnya!

Di sinilah Kebijakan Same Origin datang untuk menyelamatkan, seperti yang telah saya jelaskan di: /security/8264/why-is-the-same-origin-policy-so-important/72569# 72569

Bagaimana Rails mengirim token

Tercakup di: Rails: Bagaimana cara csrf_meta_tag Bekerja?

Pada dasarnya:

  • Pembantu HTML suka form_tagmenambahkan bidang tersembunyi ke formulir untuk Anda jika itu bukan formulir GET

  • AJAX ditangani secara otomatis oleh jquery-ujs , yang membaca token dari metaelemen yang ditambahkan ke header Anda dengan csrf_meta_tags(hadir dalam templat default), dan menambahkannya ke setiap permintaan yang dibuat.

    uJS juga mencoba memperbarui token dalam bentuk dalam fragmen cache yang ketinggalan zaman.

Pendekatan pencegahan lainnya

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
Terima kasih, tetapi poin Anda tentang mengandalkan kebijakan asal yang sama untuk tidak bisa hanya membaca token CSRF pertama tampaknya cacat. Jadi pertama-tama Anda mengatakan Anda bisa POST ke asal yang berbeda tetapi tidak bisa membacanya, tampaknya aneh tapi saya rasa itu benar, tetapi Anda bisa menyuntikkan gambar atau tag skrip dengan membuka halaman dan menautkan penangan ke respons parse dan mengerti ya?
bjm88
@ bjm88 menyuntikkan skrip di mana? Di situs Anda, atau di situs yang diserang? Jika diserang situs, memungkinkan injeksi skrip adalah kelemahan keamanan yang terkenal, dan secara efektif menggadaikan situs web. Setiap situs web harus berjuang melalui sanitasi input. Untuk gambar, saya tidak melihat bagaimana mereka dapat digunakan untuk serangan. Di situs penyerang: Anda dapat memodifikasi peramban Anda untuk memungkinkan pembacaan, dan karenanya secara otomatis menggadaikan Anda sendiri :-) tetapi browser yang layak mencegahnya secara default, cobalah.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
43

Token keaslian digunakan untuk mencegah serangan Pemalsuan Permintaan Situs-Lintas (CSRF). Untuk memahami token keaslian, Anda harus terlebih dahulu memahami serangan CSRF.

CSRF

Misalkan Anda adalah penulis bank.com. Anda memiliki formulir di situs Anda yang digunakan untuk mentransfer uang ke akun lain dengan permintaan GET:

masukkan deskripsi gambar di sini

Seorang hacker hanya bisa mengirim permintaan HTTP ke server dengan mengatakan GET /transfer?amount=$1000000&account-to=999999, bukan?

masukkan deskripsi gambar di sini

Salah. Serangan peretas tidak akan berhasil. Server pada dasarnya akan berpikir?

Hah? Siapa pria ini yang mencoba melakukan transfer. Bukan pemilik akun, itu sudah pasti.

Bagaimana server mengetahui hal ini? Karena tidak ada session_idcookie yang mengotentikasi pemohon.

Saat Anda masuk dengan nama pengguna dan kata sandi, server menetapkan session_idcookie di browser Anda. Dengan begitu, Anda tidak perlu mengautentikasi setiap permintaan dengan nama pengguna dan kata sandi Anda. Saat browser Anda mengirimkan session_idcookie, server tahu:

Oh, itu John Doe. Dia berhasil masuk 2,5 menit yang lalu. Dia baik untuk pergi.

Seorang hacker mungkin berpikir:

Hmm. Permintaan HTTP normal tidak akan berfungsi, tetapi jika saya bisa mendapatkan session_idcookie itu, saya akan menjadi emas.

Browser pengguna memiliki banyak cookie yang ditetapkan untuk bank.comdomain. Setiap kali pengguna mengajukan permintaan ke bank.comdomain, semua cookie dikirimkan. Termasuk session_idcookie.

Jadi jika seorang hacker dapat membuat Anda membuat permintaan GET yang mentransfer uang ke akunnya, ia akan berhasil. Bagaimana dia bisa menipu Anda untuk melakukannya? Dengan Pemalsuan Permintaan Lintas Situs.

Sebenarnya cukup sederhana. Peretas hanya bisa membuat Anda mengunjungi situs webnya. Di situs webnya, ia dapat memiliki tag gambar berikut:

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

Ketika browser pengguna menemukan tag gambar itu, itu akan membuat permintaan GET ke url itu. Dan karena permintaan berasal dari perambannya, ia akan mengirim semua cookie yang terkait dengannya bank.com. Jika pengguna baru saja masuk ke bank.com... session_idcookie akan ditetapkan, dan server akan berpikir bahwa pengguna bermaksud untuk mentransfer $ 1.000.000 ke akun 999999!

masukkan deskripsi gambar di sini

Yah, jangan mengunjungi situs berbahaya dan Anda akan baik-baik saja.

Itu tidak cukup. Bagaimana jika seseorang memposting gambar itu ke Facebook dan itu muncul di dinding Anda? Bagaimana jika itu disuntikkan ke situs yang Anda kunjungi dengan serangan XSS?

Itu tidak terlalu buruk. Hanya permintaan GET yang rentan.

Tidak benar. Formulir yang mengirimkan permintaan POST dapat dibuat secara dinamis. Berikut adalah contoh dari Panduan Rails tentang Keamanan :

<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>

Token Keaslian

Ketika Anda ApplicationControllermemiliki ini:

protect_from_forgery with: :exception

Ini:

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

Dikompilasi menjadi ini:

<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>

Secara khusus, berikut ini dihasilkan:

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

Untuk melindungi dari serangan CSRF, jika Rails tidak melihat token keaslian yang dikirimkan bersama dengan permintaan, itu tidak akan menganggap permintaan itu aman.

Bagaimana penyerang bisa tahu apa token ini? Nilai berbeda dihasilkan secara acak setiap kali formulir dihasilkan:

masukkan deskripsi gambar di sini

Serangan Lintas Situs Skrip (XSS) - begitulah. Tapi itu kerentanan yang berbeda untuk hari yang berbeda.

Adam Zerner
sumber
39

Ini Authenticity Tokenadalah metode rel untuk mencegah serangan pemalsuan permintaan lintas situs (CSRF atau XSRF) .

Sederhananya, itu memastikan bahwa PUT / POST / HAPUS (metode yang dapat mengubah konten) permintaan ke aplikasi web Anda dibuat dari browser klien dan bukan dari pihak ketiga (penyerang) yang memiliki akses ke cookie yang dibuat di sisi klien.

dan saya
sumber
34

karena Authenticity Tokensangat penting, dan di Rails 3.0+ Anda dapat menggunakan

 <%= token_tag nil %>

untuk membuat

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

dimana saja

Yuan He
sumber
Ini membantu saya. Sebenarnya saya coba lakukan XSSpada halaman login, bukan untuk tujuan jahat, tetapi untuk membuat sesi baru dengan nama pengguna yang sudah diisi sebelumnya. Sekarang saya tahu saya bisa menggunakannya value="token_value".
Michael - Di mana Clay Shirky
27

Waspadai mekanisme Token Keaslian dapat menyebabkan kondisi ras jika Anda memiliki beberapa permintaan bersamaan dari klien yang sama. Dalam situasi ini server Anda dapat menghasilkan beberapa token keaslian ketika seharusnya hanya ada satu, dan klien yang menerima token sebelumnya dalam bentuk akan gagal pada permintaan berikutnya karena token cookie sesi telah ditimpa. Ada tulisan tentang masalah ini dan solusi yang tidak sepenuhnya sepele di sini: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

jdp
sumber
11

Metode Dimana authenticity_tokendiperlukan

authenticity_token diperlukan dalam kasus metode idempoten seperti posting, meletakkan dan menghapus, karena metode Idempoten mempengaruhi data.

Mengapa Itu Diperlukan

Diperlukan untuk mencegah dari tindakan jahat. authenticity_token disimpan dalam sesi, setiap kali formulir dibuat di halaman web untuk membuat atau memperbarui ke sumber daya maka token keaslian disimpan dalam bidang tersembunyi dan dikirim dengan formulir di server. Sebelum melakukan tindakan, pengguna mengirim authenticity_token diperiksa silang dengan authenticity_tokendisimpan dalam sesi. Jika authenticity_tokensama maka proses dilanjutkan jika tidak melakukan tindakan.

uma
sumber
3
Sebenarnya, bukankah itu kebalikannya? GET idempoten karena panggilannya seharusnya tidak mengubah keadaan sistem, di mana kata kerja PUT POST dan DELETE BUKAN kata kerja idempoten karena mereka mengubah status sistem. IE: authenticity_token diperlukan jika metode BUKAN idempoten.
Jean-Théo
2
@ Jean-Daube, uma: idempoten berarti bahwa jika dilakukan dua kali, tindakan hanya terjadi sekali. GET, PUT dan DELETE adalah idempoten: w3.org/Protocols/rfc2616/rfc2616-sec9.html Properti kunci di sini adalah tidak idempotency, tetapi jika metode perubahan atau tidak data, yang disebut "metode Aman" atau tidak.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
6

Apa itu otentikasi_token?

Ini adalah string acak yang digunakan oleh aplikasi rails untuk memastikan bahwa pengguna meminta atau melakukan tindakan dari halaman aplikasi, bukan dari aplikasi atau situs lain.

Mengapa otentikasi_token diperlukan?

Untuk melindungi aplikasi atau situs Anda dari pemalsuan permintaan lintas situs.

Bagaimana cara menambahkan authentication_token ke formulir?

Jika Anda membuat formulir menggunakan tag form_for, otentikasi_token ditambahkan secara otomatis yang dapat Anda gunakan <%= csrf_meta_tag %>.

Pradeep Sapkota
sumber