Apa itu token CSRF? Apa pentingnya dan bagaimana cara kerjanya?

629

Saya sedang menulis aplikasi (Django, itu terjadi) dan saya hanya ingin tahu apa sebenarnya token "CSRF" itu dan bagaimana melindungi data. Apakah data posting tidak aman jika Anda tidak menggunakan token CSRF?

Shawn
sumber
13
Ini adalah token rahasia khusus pengguna dalam semua pengiriman formulir dan URL efek samping untuk mencegah Pemalsuan Permintaan Lintas Situs. Info lebih lanjut di sini: en.wikipedia.org/wiki/Cross-site_request_forgery
Robert Harvey
1
sepertinya ada garis tipis antara melindungi pertanyaan dan melarangnya karena terlalu luas: D
anton1980
2
Dari OWASP Cross-Site Request Forgery (CSRF) Lembar Pencegahan Penipuan : " Script Lintas Situs tidak diperlukan agar CSRF dapat berfungsi. Namun, setiap kerentanan scripting lintas-situs dapat digunakan untuk mengalahkan semua teknik mitigasi CSRF [...]. Ini karena muatan XSS dapat dengan mudah membaca halaman mana pun di situs menggunakan XMLHttpRequest [...]. Sangat penting bahwa tidak ada kerentanan XSS hadir untuk memastikan bahwa pertahanan CSRF tidak dapat dielakkan. "
toraritte

Jawaban:

1497

Pemalsuan Permintaan Lintas Situs (CSRF) dengan kata-kata sederhana

  • Asumsikan Anda saat ini masuk ke perbankan online Anda di www.mybank.com
  • Asumsikan pengiriman uang dari mybank.comakan menghasilkan permintaan (secara konseptual) formulir http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>. (Nomor akun Anda tidak diperlukan, karena tersirat oleh login Anda.)
  • Anda berkunjung www.cute-cat-pictures.org, tidak tahu bahwa itu adalah situs jahat.
  • Jika pemilik situs itu mengetahui bentuk permintaan di atas (mudah!) Dan menebak dengan benar bahwa Anda masuk mybank.com(membutuhkan keberuntungan!), Mereka dapat memasukkan permintaan seperti di halaman mereka http://www.mybank.com/transfer?to=123456;amount=10000(di mana 123456jumlah akun Kepulauan Cayman mereka dan 10000jumlah yang sebelumnya Anda pikir Anda senang miliki).
  • Anda mengambil www.cute-cat-pictures.orghalaman itu, jadi browser Anda akan membuat permintaan itu.
  • Bank Anda tidak dapat mengenali asal permintaan ini: Peramban web Anda akan mengirimkan permintaan beserta www.mybank.comkuki Anda dan itu akan terlihat sah-sah saja. Itu uangmu!

Ini adalah dunia tanpa token CSRF .

Sekarang untuk yang lebih baik dengan token CSRF :

  • Permintaan transfer diperpanjang dengan argumen ketiga: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Token itu adalah angka acak yang sangat besar dan tidak mungkin untuk ditebak yang mybank.comakan disertakan pada halaman web mereka sendiri ketika mereka menyajikannya kepada Anda. Ini berbeda setiap kali mereka melayani halaman apa pun kepada siapa pun.
  • Penyerang tidak dapat menebak token, tidak dapat meyakinkan browser web Anda untuk menyerahkannya (jika browser berfungsi dengan benar ...), sehingga penyerang tidak akan dapat membuat permintaan yang valid, karena permintaan dengan token yang salah (atau tidak ada token) akan ditolak oleh www.mybank.com.

Hasil: Anda menyimpan 10000unit moneter Anda . Saya sarankan Anda menyumbangkan sebagian dari itu ke Wikipedia.

(Jarak tempuh Anda mungkin beragam.)

Sunting dari komentar yang layak dibaca:

Patut dicatat bahwa skrip dari www.cute-cat-pictures.orgbiasanya tidak memiliki akses ke token anti-CSRF Anda www.mybank.comkarena kontrol akses HTTP. Catatan ini penting bagi sebagian orang yang mengirim header secara tidak masuk akal Access-Control-Allow-Origin: *untuk setiap respons situs web tanpa mengetahui apa tujuannya, hanya karena mereka tidak dapat menggunakan API dari situs web lain.

Lutz Prechelt
sumber
36
Dan jelas token idealnya akan dinamai token anti -CSRF, tapi namanya mungkin cukup rumit.
Lutz Prechelt
3
@ LutzPrechelt terima kasih. mengapa javascript tidak dapat memperoleh token keaslian apa pun dari browser?
BKSpurgeon
72
Patut dicatat bahwa skrip dari www.cute-cat-pictures.orgbiasanya tidak memiliki akses ke token anti-CSRF Anda www.mybank.comkarena kontrol akses HTTP. Catatan ini penting bagi sebagian orang yang mengirim header secara tidak masuk akal Access-Control-Allow-Origin: *untuk setiap respons situs web tanpa mengetahui apa tujuannya, hanya karena mereka tidak dapat menggunakan API dari situs web lain.
SOFe
9
@AugustinRiedinger Jika penyerang membuka halaman web di komputernya - karena mereka tidak memiliki cookie pengguna yang login - mereka tidak akan menerima token csrf yang sesuai (masing-masing token csrf harus valid hanya untuk sesi pengguna tertentu). Jika penyerang mencoba memuat laman web yang berisi token di komputer pengguna, dengan skrip yang ditempatkan di situs gambar cute-cat-pictures, peramban akan mencegahnya membaca www.mybank.com (dan token) karena kebijakan asal yang sama.
Marcel
13
@ LutzPrechelt Saya pikir tidak cukup bahwa token selalu berbeda, harus dipasangkan dengan sesi dan server harus memeriksa bahwa token yang diterimanya dihasilkan untuk sesi yang diidentifikasi oleh server oleh cookie yang diterima. Jika tidak, peretas dapat mengunjungi mybank sendiri dan mendapatkan beberapa token yang valid. Jadi, jika Anda menggunakan token baru dengan setiap formulir, Anda harus menyimpannya dipasangkan dengan sessionid di server. Mungkin lebih mudah menggunakan token yang sama per sesi.
Marcel
222

Ya, data pos aman. Tapi asal usul data itu bukan. Dengan cara ini seseorang dapat menipu pengguna dengan JS untuk masuk ke situs Anda, saat menjelajahi halaman web penyerang.

Untuk mencegah itu, Django akan mengirim kunci acak baik di cookie, dan bentuk data. Kemudian, ketika pengguna POST, itu akan memeriksa apakah dua kunci identik. Jika pengguna ditipu, situs web pihak ketiga tidak bisa mendapatkan cookie situs Anda, sehingga menyebabkan kesalahan auth.

Dmitry Shevchenko
sumber
@DmitryShevchenko Hai, mencoba memahami bagaimana metode cookie + form-input berbeda dari hanya memvalidasi pengarah di sisi server? Semua contoh yang saya temukan terkait dengan peretas yang menipu pengguna untuk mengirim dari situsnya ke situs yang sebenarnya.
Ethan
Oke, saya tahu mengapa referer tidak digunakan. Ini diblokir dalam banyak kasus karena dianggap menyimpan informasi sensitif kadang-kadang. Perusahaan dan kuasanya biasanya melakukannya. Namun, jika HTTPS digunakan, maka ada kemungkinan tidak akan diblokir.
Ethan
4
Sangat mudah untuk mengubah referer, saya tidak akan mengatakan bahwa itu adalah informasi yang dapat diandalkan. Token CSRF, bagaimanapun, dihasilkan dengan menggunakan kunci rahasia server dan biasanya dikaitkan dengan pengguna
Dmitry Shevchenko
1
Saya tidak begitu mengerti mengapa ini merupakan ancaman keamanan. Pengguna akan masuk ke situs lain ... tetapi situs asli tidak memiliki cara untuk mengambil informasi itu. Baik?
Aakil Fernandes
6
Nah, misalkan saya menyuntikkan iframe berbahaya dari " bank.com/transfer?from=x&to=y " di, misalnya, Facebook.com. Jika Anda adalah pelanggan bank.com dan Anda pergi ke Facebook, iframe itu akan memuat halaman bank, dengan cookie Anda (karena browser akan mengirimkannya ke domain yang dikenal) dan melakukan transfer uang. Tanpa kamu tahu apa-apa.
Dmitry Shevchenko
74

Situs menghasilkan token unik ketika membuat halaman formulir. Token ini diperlukan untuk mengirim / mendapatkan data kembali ke server.

Karena token dibuat oleh situs Anda dan disediakan hanya ketika halaman dengan formulir dibuat, beberapa situs lain tidak dapat meniru formulir Anda - mereka tidak akan memiliki token dan karena itu tidak dapat memposting ke situs Anda.

tkone
sumber
10
Bisakah pengguna mengambil output token di dalam sumber, mengambil cookie yang dikirim kepada mereka dan kemudian dari situs pihak ke-3 mengirimkan?
Jack Marchetti
9
@JackMarchetti ya. tetapi itu akan mahal karena setiap kali Anda ingin mengirimkan formulir dari situs pihak ke-3 Anda harus memuat halaman dan menguraikan token. Token CSRF harus idealnya digabungkan dengan bentuk keamanan lain jika Anda khawatir dengan vektor serangan ini
tkone
4
Saya memiliki pertanyaan yang sama dengan @JackMarchetti, yang tidak jelas adalah - jika token CSRF berubah pada setiap login. Jika tetap sama, apa yang akan mencegah penyerang pertama kali masuk, meraih token permintaan, dan kemudian memasukkan token itu dalam serangan?
Paul Preibisch
7
@PaulPreibisch harus berubah pada setiap pemuatan halaman - bukan pada setiap login. Dengan cara ini penyerang harus meminta halaman setiap kali mereka ingin mengirimkan formulir. Membuatnya jauh lebih sulit.
tkone
9
@tkone, Itu tidak benar-benar membuatnya jauh lebih sulit. Jika hanya menggandakan jumlah usaha dan waktu. Itu tidak menambahkan segala macam proses penghalang. Triknya adalah mengaitkan token CSRF ke cookie khusus domain, dan mengirimkan cookie ini beserta formulirnya. Cookie dan data post form harus dikirim ke server berdasarkan permintaan POST. Cara ini akan membutuhkan serangan Pembajakan Cookie untuk dapat meniru permintaan yang sah.
Pedro Cordeiro
56

Blog Cloud Under memiliki penjelasan yang baik tentang token CSRF.

Bayangkan Anda memiliki situs web seperti Twitter yang disederhanakan, yang dihosting di a.com. Pengguna yang sudah masuk dapat memasukkan beberapa teks (tweet) ke dalam formulir yang dikirim ke server sebagai permintaan POST dan diterbitkan ketika mereka menekan tombol kirim. Di server pengguna diidentifikasi oleh cookie yang berisi ID sesi unik mereka, sehingga server Anda tahu siapa yang memposting Tweet.

Bentuknya bisa sesederhana itu:

 <form action="http://a.com/tweet" method="POST">
   <input type="text" name="tweet">
   <input type="submit">
 </form> 

Sekarang bayangkan, orang jahat menyalin dan menempelkan formulir ini ke situs web jahatnya, katakanlah b.com. Formulir masih akan berfungsi. Selama pengguna masuk ke Twitter Anda (yaitu mereka punya cookie sesi yang valid untuk a.com), permintaan POST akan dikirim ke http://a.com/tweetdan diproses seperti biasa ketika pengguna mengklik tombol kirim.

Sejauh ini ini bukan masalah besar selama pengguna dibuat sadar tentang apa sebenarnya bentuknya, tetapi bagaimana jika penjahat kita mengubah bentuk seperti ini:

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

Sekarang, jika salah satu pengguna Anda berakhir di situs web orang jahat dan klik "Klik untuk menang!" tombol, formulir dikirimkan ke situs web Anda, pengguna diidentifikasi dengan benar oleh ID sesi dalam cookie dan Tweet tersembunyi dipublikasikan.

Jika penjahat kita bahkan lebih buruk, dia akan membuat pengguna yang tidak bersalah mengirimkan formulir ini segera setelah mereka membuka halaman webnya menggunakan JavaScript, bahkan mungkin benar-benar disembunyikan dalam iframe yang tidak terlihat. Ini pada dasarnya adalah pemalsuan permintaan lintas situs.

Formulir dapat dengan mudah dikirimkan dari mana saja ke mana saja. Secara umum itu adalah fitur yang umum, tetapi ada banyak lagi kasus di mana penting untuk hanya mengizinkan formulir dikirimkan dari domain yang menjadi miliknya.

Lebih buruk lagi jika aplikasi web Anda tidak membedakan antara permintaan POST dan GET (mis. Dalam PHP dengan menggunakan $ _REQUEST alih-alih $ _POST). Jangan lakukan itu! Permintaan pengubah data dapat diajukan semudah <img src="http://a.com/tweet?tweet=This+is+really+bad">, disematkan di situs web jahat atau bahkan email.

Bagaimana cara memastikan formulir hanya dapat dikirimkan dari situs web saya sendiri? Di sinilah token CSRF masuk. Token CSRF adalah string acak yang sulit ditebak. Pada halaman dengan formulir yang ingin Anda lindungi, server akan menghasilkan string acak, token CSRF, menambahkannya ke formulir sebagai bidang tersembunyi dan juga mengingatnya entah bagaimana, baik dengan menyimpannya di sesi atau dengan mengatur cookie mengandung nilai. Sekarang bentuknya akan terlihat seperti ini:

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

Ketika pengguna mengirimkan formulir, server hanya perlu membandingkan nilai dari bidang yang diposting csrf-token (nama tidak masalah) dengan token CSRF yang diingat oleh server. Jika kedua string sama, server dapat melanjutkan untuk memproses formulir. Kalau tidak, server harus segera berhenti memproses formulir dan merespons dengan kesalahan.

Mengapa ini bekerja? Ada beberapa alasan mengapa orang jahat dari contoh kami di atas tidak dapat memperoleh token CSRF:

Menyalin kode sumber statis dari halaman kami ke situs web yang berbeda tidak akan berguna, karena nilai bidang tersembunyi berubah dengan setiap pengguna. Tanpa situs web orang jahat itu mengetahui token CSRF pengguna saat ini server Anda akan selalu menolak permintaan POST.

Karena halaman jahat orang jahat dimuat oleh browser pengguna Anda dari domain yang berbeda (b.com, bukan a.com), penjahat tidak memiliki kesempatan untuk kode JavaScript, yang memuat konten dan karenanya CSRF pengguna kami saat ini diambil dari situs Anda. Itu karena browser web tidak mengizinkan permintaan AJAX lintas domain secara default.

Orang jahat juga tidak dapat mengakses cookie yang ditetapkan oleh server Anda, karena domain tidak akan cocok.

Kapan saya harus melindungi terhadap pemalsuan permintaan lintas situs? Jika Anda dapat memastikan bahwa Anda tidak mencampuradukkan GET, POST, dan metode permintaan lainnya seperti yang dijelaskan di atas, langkah awal yang baik adalah melindungi semua permintaan POST secara default.

Anda tidak harus melindungi permintaan PUT dan DELETE, karena seperti yang dijelaskan di atas, formulir HTML standar tidak dapat dikirimkan oleh browser menggunakan metode tersebut.

Sebaliknya, JavaScript memang dapat membuat jenis permintaan lain, mis. Menggunakan fungsi $ .ajax () jQuery, tetapi ingat, agar permintaan AJAX berfungsi, domain harus cocok (selama Anda tidak mengonfigurasi server web Anda secara eksplisit jika tidak) .

Ini berarti, seringkali Anda bahkan tidak perlu menambahkan token CSRF ke permintaan AJAX, bahkan jika itu adalah permintaan POST, tetapi Anda harus memastikan bahwa Anda hanya memintas pemeriksaan CSRF di aplikasi web Anda jika permintaan POST sebenarnya adalah Permintaan AJAX. Anda dapat melakukannya dengan mencari keberadaan header seperti X-Requested-With, yang biasanya diminta oleh AJAX. Anda juga dapat mengatur tajuk khusus lain dan memeriksa keberadaannya di sisi server. Itu aman, karena peramban tidak akan menambahkan header khusus ke pengiriman formulir HTML biasa (lihat di atas), jadi tidak ada kesempatan bagi Tuan Orang Jahat untuk mensimulasikan perilaku ini dengan formulir.

Jika Anda ragu tentang permintaan AJAX, karena karena alasan tertentu Anda tidak dapat memeriksa header seperti X-Requested-With, cukup kirimkan token CSRF yang dihasilkan ke JavaScript Anda dan tambahkan token ke permintaan AJAX. Ada beberapa cara untuk melakukan ini; baik menambahkannya ke payload seperti bentuk HTML biasa, atau menambahkan header khusus ke permintaan AJAX. Selama server Anda tahu di mana mencarinya dalam permintaan masuk dan dapat membandingkannya dengan nilai asli yang diingatnya dari sesi atau cookie, Anda disortir.

Dan
sumber
Terima kasih atas info terperinci. Selama permintaan posting, situs harus mengirim token csrf ke server, jadi kapan klien akan mengirim token csrf ini ke server? Apakah saat membuat permintaan opsi preflight? Tolong jelaskan bagian ini ..
Sm Srikanth
@Dan Kenapa b.com dapat mengakses cookie dari situs lain a.com?
zakir
8

Akar dari semua itu adalah untuk memastikan bahwa permintaan datang dari pengguna situs yang sebenarnya. Token csrf dibuat untuk formulir dan harus dikaitkan dengan sesi pengguna. Ini digunakan untuk mengirim permintaan ke server, di mana token memvalidasinya. Ini adalah salah satu cara melindungi terhadap csrf, yang lain akan memeriksa tajuk pengarah.

dengan senang hati
sumber
7
Jangan mengandalkan header referer, itu bisa dengan mudah dipalsukan.
kag
3
Ini jawaban yang benar! Token HARUS diikat ke sesi di server. Membandingkan Cookie + Formulir data seperti jawaban terbanyak menyarankan ini sepenuhnya salah. Komponen-komponen ini keduanya merupakan bagian dari permintaan, yang dibangun oleh klien.
Lee Davis
3
Sebenarnya tidak. Token HARUS diikat ke setiap PERMINTAAN ke Server. Jika Anda hanya mengikatnya dengan sesi, maka Anda berisiko mengambil seseorang mencuri token sesi dan mengirimkan permintaan dengan token itu. Jadi untuk keamanan maksimal token harus diikat ke setiap http requiest.
chrisl08