Mengapa biasa menempatkan token pencegahan CSRF dalam cookie?

284

Saya mencoba memahami seluruh masalah dengan CSRF dan cara yang tepat untuk mencegahnya. (Sumber yang telah saya baca, pahami, dan setujui dengan: Lembar Cegah Pencegahan CSRF OWASP , Pertanyaan tentang CSRF .)

Seperti yang saya pahami, kerentanan sekitar CSRF diperkenalkan oleh asumsi bahwa (dari sudut pandang server web) cookie sesi yang valid dalam permintaan HTTP masuk mencerminkan keinginan pengguna yang diautentikasi. Tetapi semua cookie untuk domain asal secara ajaib dilampirkan ke permintaan oleh browser, jadi benar-benar semua server dapat menyimpulkan dari kehadiran cookie sesi yang valid dalam permintaan adalah bahwa permintaan tersebut berasal dari browser yang memiliki sesi terotentikasi; lebih lanjut tidak dapat menganggap apa pun tentang kodeberjalan di browser itu, atau apakah itu benar-benar mencerminkan keinginan pengguna. Cara untuk mencegah ini adalah dengan memasukkan informasi otentikasi tambahan ("token CSRF") dalam permintaan, yang dilakukan dengan beberapa cara selain penanganan cookie otomatis browser. Secara longgar, cookie sesi mengotentikasi pengguna / browser dan token CSRF mengotentikasi kode yang berjalan di browser.

Singkatnya, jika Anda menggunakan cookie sesi untuk mengautentikasi pengguna aplikasi web Anda, Anda juga harus menambahkan token CSRF untuk setiap respons, dan memerlukan token CSRF yang cocok di setiap permintaan (yang bermutasi). Token CSRF kemudian melakukan bolak-balik dari server ke browser kembali ke server, membuktikan ke server bahwa halaman yang membuat permintaan disetujui oleh (dihasilkan oleh, bahkan) server itu.

Ke pertanyaan saya, yaitu tentang metode transportasi spesifik yang digunakan untuk token CSRF pada perjalanan pulang pergi itu.

Tampaknya umum (misalnya dalam AngularJS , Django , Rails ) untuk mengirim token CSRF dari server ke klien sebagai cookie (yaitu dalam header Set-Cookie), dan kemudian memiliki Javascript di klien mengikisnya keluar dari cookie dan melampirkannya sebagai header XSRF-TOKEN yang terpisah untuk mengirim kembali ke server.

(Metode alternatif adalah metode yang direkomendasikan oleh misalnya Express , di mana token CSRF yang dihasilkan oleh server dimasukkan dalam badan respons melalui ekspansi templat sisi-server, dilampirkan langsung ke kode / markup yang akan memasoknya kembali ke server, misalnya sebagai input formulir tersembunyi. Contoh itu adalah web yang lebih 1.0-ish cara melakukan sesuatu, tetapi akan menggeneralisasi baik untuk klien yang lebih berat JS.)

Mengapa Set-Cookie sangat umum digunakan sebagai transportasi hilir untuk token CSRF / mengapa ini ide yang bagus? Saya membayangkan penulis dari semua kerangka kerja ini mempertimbangkan pilihan mereka dengan hati-hati dan tidak salah. Tetapi pada pandangan pertama, menggunakan cookie untuk mengatasi apa yang pada dasarnya pembatasan desain pada cookie tampaknya bodoh. Bahkan, jika Anda menggunakan cookie sebagai transportasi pulang pergi (Set-Cookie: header hilir untuk server untuk memberitahu browser token CSRF, dan Cookie: header hulu untuk browser untuk mengembalikannya ke server) Anda akan memperkenalkan kembali kerentanan Anda sedang mencoba untuk memperbaikinya.

Saya menyadari bahwa kerangka kerja di atas tidak menggunakan cookie untuk seluruh perjalanan pulang pergi untuk token CSRF; mereka menggunakan Set-Cookie downstream, lalu sesuatu yang lain (misalnya header X-CSRF-Token) upstream, dan ini tidak menutup kerentanan. Tetapi bahkan menggunakan Set-Cookie sebagai transportasi hilir berpotensi menyesatkan dan berbahaya; browser sekarang akan melampirkan token CSRF untuk setiap permintaan termasuk permintaan XSRF berbahaya asli; paling-paling itu membuat permintaan lebih besar dari yang seharusnya dan paling buruk beberapa kode server yang bermaksud baik tetapi salah arah mungkin benar-benar mencoba menggunakannya, yang akan sangat buruk. Dan lebih jauh lagi, karena penerima token CSRF yang sebenarnya dimaksudkan adalah Javascript sisi klien, itu artinya cookie ini tidak dapat dilindungi hanya dengan http.

metamatt
sumber
Ini pertanyaan yang bagus mengenai tempat yang tepat.
kta

Jawaban:

263

Alasan yang bagus, yang telah Anda sentuh, adalah bahwa setelah cookie CSRF diterima, maka cookie tersebut akan tersedia untuk digunakan di seluruh aplikasi dalam skrip klien untuk digunakan dalam bentuk reguler dan POS AJAX. Ini akan masuk akal dalam aplikasi berat JavaScript seperti yang dipekerjakan oleh AngularJS (menggunakan AngularJS tidak mengharuskan aplikasi tersebut menjadi aplikasi satu halaman, jadi akan berguna jika negara perlu mengalir di antara permintaan halaman berbeda di mana nilai CSRF biasanya tidak dapat bertahan di browser).

Pertimbangkan skenario dan proses berikut dalam aplikasi tipikal untuk beberapa pro dan kontra dari setiap pendekatan yang Anda jelaskan. Ini didasarkan pada Pola Token Sinkronisasi .

Meminta Pendekatan Badan

  1. Pengguna berhasil masuk.
  2. Server mengeluarkan cookie auth.
  3. Pengguna mengklik untuk menavigasi ke formulir.
  4. Jika belum dibuat untuk sesi ini, server membuat token CSRF, menyimpannya terhadap sesi pengguna dan mengeluarkannya ke bidang tersembunyi.
  5. Formulir pengiriman pengguna.
  6. Server memeriksa bidang yang tersembunyi cocok dengan token yang disimpan sesi.

Keuntungan:

  • Mudah diimplementasikan.
  • Bekerja dengan AJAX.
  • Bekerja dengan formulir.
  • Cookie sebenarnya hanya HTTP .

Kekurangan:

  • Semua formulir harus menampilkan bidang tersembunyi dalam HTML.
  • POS AJAX apa pun juga harus menyertakan nilainya.
  • Halaman tersebut harus tahu sebelumnya bahwa ia memerlukan token CSRF sehingga ia dapat memasukkannya ke dalam konten halaman sehingga semua halaman harus mengandung nilai token di suatu tempat, yang bisa memakan waktu lama untuk diterapkan pada situs besar.

Header HTTP Khusus (hilir)

  1. Pengguna berhasil masuk.
  2. Server mengeluarkan cookie auth.
  3. Pengguna mengklik untuk menavigasi ke formulir.
  4. Halaman dimuat di browser, lalu permintaan AJAX dibuat untuk mengambil token CSRF.
  5. Server menghasilkan token CSRF (jika belum dibuat untuk sesi), menyimpannya terhadap sesi pengguna dan mengeluarkannya ke header.
  6. Formulir pengiriman pengguna (token dikirim melalui bidang tersembunyi).
  7. Server memeriksa bidang yang tersembunyi cocok dengan token yang disimpan sesi.

Keuntungan:

  • Bekerja dengan AJAX.
  • Cookie hanya bisa HTTP .

Kekurangan:

  • Tidak berfungsi tanpa permintaan AJAX untuk mendapatkan nilai header.
  • Semua formulir harus memiliki nilai tambah ke HTML-nya secara dinamis.
  • POS AJAX apa pun juga harus menyertakan nilainya.
  • Halaman harus membuat permintaan AJAX terlebih dahulu untuk mendapatkan token CSRF, jadi itu akan berarti perjalanan pulang pergi tambahan setiap kali.
  • Mungkin juga hanya menampilkan token ke halaman yang akan menyimpan permintaan tambahan.

Header HTTP Khusus (hulu)

  1. Pengguna berhasil masuk.
  2. Server mengeluarkan cookie auth.
  3. Pengguna mengklik untuk menavigasi ke formulir.
  4. Jika belum dibuat untuk sesi ini, server membuat token CSRF, menyimpannya terhadap sesi pengguna dan menampilkannya dalam konten halaman di suatu tempat.
  5. Pengguna mengirimkan formulir melalui AJAX (token dikirim melalui tajuk).
  6. Server memeriksa token pertandingan yang cocok dengan header kustom.

Keuntungan:

  • Bekerja dengan AJAX.
  • Cookie hanya bisa HTTP .

Kekurangan:

  • Tidak bekerja dengan formulir.
  • Semua POST AJAX harus menyertakan header.

Header HTTP Khusus (hulu & hilir)

  1. Pengguna berhasil masuk.
  2. Server mengeluarkan cookie auth.
  3. Pengguna mengklik untuk menavigasi ke formulir.
  4. Halaman dimuat di browser, lalu permintaan AJAX dibuat untuk mengambil token CSRF.
  5. Server menghasilkan token CSRF (jika belum dibuat untuk sesi), menyimpannya terhadap sesi pengguna dan mengeluarkannya ke header.
  6. Pengguna mengirimkan formulir melalui AJAX (token dikirim melalui tajuk).
  7. Server memeriksa token pertandingan yang cocok dengan header kustom.

Keuntungan:

  • Bekerja dengan AJAX.
  • Cookie hanya bisa HTTP .

Kekurangan:

  • Tidak bekerja dengan formulir.
  • Semua POST AJAX juga harus menyertakan nilainya.
  • Halaman harus membuat permintaan AJAX terlebih dahulu untuk mendapatkan token CRSF, jadi itu berarti perjalanan pulang pergi ekstra setiap kali.

Set-Cookie

  1. Pengguna berhasil masuk.
  2. Server mengeluarkan cookie auth.
  3. Pengguna mengklik untuk menavigasi ke formulir.
  4. Server menghasilkan token CSRF, menyimpannya terhadap sesi pengguna dan mengeluarkannya ke cookie.
  5. Pengguna mengirimkan formulir melalui AJAX atau melalui formulir HTML.
  6. Server memeriksa tajuk khusus (atau bidang formulir tersembunyi) cocok dengan token yang disimpan sesi.
  7. Cookie tersedia di browser untuk digunakan dalam AJAX tambahan dan permintaan formulir tanpa permintaan tambahan ke server untuk mengambil token CSRF.

Keuntungan:

  • Mudah diimplementasikan.
  • Bekerja dengan AJAX.
  • Bekerja dengan formulir.
  • Tidak perlu meminta permintaan AJAX untuk mendapatkan nilai cookie. Permintaan HTTP apa pun dapat mengambilnya dan dapat ditambahkan ke semua formulir / permintaan AJAX melalui JavaScript.
  • Setelah token CSRF diambil, karena disimpan dalam cookie, nilainya dapat digunakan kembali tanpa permintaan tambahan.

Kekurangan:

  • Semua formulir harus memiliki nilai tambah ke HTML-nya secara dinamis.
  • POS AJAX apa pun juga harus menyertakan nilainya.
  • Cookie akan dikirimkan untuk setiap permintaan (yaitu semua GET untuk gambar, CSS, JS, dll, yang tidak terlibat dalam proses CSRF) meningkatkan ukuran permintaan.
  • Cookie tidak hanya HTTP .

Jadi pendekatan cookie cukup dinamis yang menawarkan cara mudah untuk mengambil nilai cookie (permintaan HTTP apa pun) dan menggunakannya (JS dapat menambahkan nilai ke formulir apa pun secara otomatis dan dapat digunakan dalam permintaan AJAX baik sebagai header atau sebagai nilai bentuk). Setelah token CSRF telah diterima untuk sesi ini, tidak perlu untuk memperbaruinya karena penyerang yang menggunakan eksploitasi CSRF tidak memiliki metode untuk mengambil token ini. Jika pengguna jahat mencoba membaca token CSRF pengguna dalam salah satu metode di atas maka ini akan dicegah oleh ), maka token ini tidak akan dikaitkan dengan akun pengguna yang sama karena cookie sesi autentikasi korban akan hilang dari permintaan ( itu akan menjadi penyerang - karena itu tidak akan dikaitkan sisi server dengan sesi korban). Kebijakan Asal yang Sama . Jika pengguna jahat mencoba mengambil sisi server token CSRF (mis. Viacurl

Selain Pola Sinkronisasi Token, ada juga Cookie Kirim GandaMetode pencegahan CSRF, yang tentu saja menggunakan cookie untuk menyimpan jenis token CSRF. Ini lebih mudah untuk diterapkan karena tidak memerlukan keadaan sisi server apa pun untuk token CSRF. Token CSRF sebenarnya bisa menjadi cookie otentikasi standar saat menggunakan metode ini, dan nilai ini dikirimkan melalui cookie seperti biasa dengan permintaan, tetapi nilainya juga diulang dalam bidang atau tajuk tersembunyi, yang tidak dapat direplikasi oleh penyerang sebagai mereka tidak dapat membaca nilai di tempat pertama. Akan disarankan untuk memilih cookie lain, selain cookie otentikasi sehingga cookie otentikasi dapat diamankan dengan ditandai HttpOnly. Jadi ini adalah alasan umum mengapa Anda akan menemukan pencegahan CSRF menggunakan metode berbasis cookie.

SilverlightFox
sumber
7
Saya tidak yakin saya mengerti bagaimana "permintaan AJAX dibuat untuk mengambil token CSRF" (langkah 4 di bagian "tajuk khusus: downstream") dapat dilakukan dengan aman; karena ini adalah permintaan terpisah, server tidak tahu dari siapa itu berasal; bagaimana ia tahu bahwa membocorkan token CSRF itu aman? Menurut saya, jika Anda tidak bisa mengeluarkan token dari memuat halaman awal, Anda kehilangan (yang membuat header respons downstream khusus menjadi nonstarter, sayangnya).
metamatt
6
Karena pemalsu tidak akan memiliki cookie sesi. Mereka mungkin memiliki cookie sesi mereka sendiri, tetapi karena token CSRF dikaitkan dengan sebuah sesi, token CSRF mereka tidak akan cocok dengan milik korban.
SilverlightFox
32
Dalam pemahaman saya tentang serangan CSRF, pemalsu memang memiliki cookie sesi saya . Yah, mereka sebenarnya tidak bisa melihat cookie, tetapi mereka memiliki kemampuan untuk menyediakannya dalam permintaan palsu mereka, karena permintaan datang dari browser saya dan browser saya memasok cookie sesi saya. Jadi dari sudut pandang server, cookie sesi saja tidak dapat membedakan permintaan yang sah dari permintaan yang dipalsukan. Ini sebenarnya serangan yang kami coba cegah. Terima kasih atas kesabaran Anda dalam membicarakan hal ini, terutama jika saya bingung tentang hal ini.
metamatt
8
Mereka memiliki kemampuan untuk memasok cookie autentik, tetapi mereka tidak dapat membaca respons yang berisi token CSRF.
SilverlightFox
8
@ metamatt Maaf untuk necro, tapi saya akan melakukannya untuk orang-orang yang berkeliaran. Dalam pemahaman saya, penyerang biasanya tidak memiliki akses ke respons. CSRF terutama digunakan untuk menyebabkan efek samping , daripada langsung mengumpulkan data. Misalnya, skrip serangan CSRF mungkin memaksa pengguna yang memiliki hak istimewa untuk meningkatkan hak penyerang, menonaktifkan pengaturan keamanan, atau memaksa pengguna paypal yang masuk untuk mengirim transfer ke alamat email tertentu. Dalam kasus-kasus ini tidak ada penyerang yang peduli dengan respons, yang masih dikirim ke browser korban; hanya hasil dari serangan itu.
Jonathanbruder
61

Menggunakan cookie untuk memberikan token CSRF kepada klien tidak memungkinkan serangan yang berhasil karena penyerang tidak dapat membaca nilai cookie dan oleh karena itu tidak bisa meletakkannya di tempat yang memerlukan validasi CSRF di sisi server.

Penyerang akan dapat menyebabkan permintaan ke server dengan cookie token auth dan cookie CSRF di header permintaan. Tetapi server tidak mencari token CSRF sebagai cookie di header permintaan, itu mencari dalam muatan permintaan. Dan bahkan jika penyerang tahu di mana harus meletakkan token CSRF dalam muatan, mereka harus membaca nilainya untuk meletakkannya di sana. Tetapi kebijakan lintas-asal browser mencegah pembacaan nilai cookie apa pun dari situs web target.

Logika yang sama tidak berlaku untuk cookie token auth, karena server mengharapkannya dalam header permintaan dan penyerang tidak harus melakukan sesuatu yang khusus untuk meletakkannya di sana.

Tongfa
sumber
Tentunya, seorang penyerang tidak perlu membaca cookie di tempat pertama. Mereka hanya dapat menyisipkan gambar di situs yang diretas src='bank.com/transfer?to=hacker&amount=1000yang akan diminta browser, lengkap dengan cookie yang terkait untuk situs itu ( bank.com)?
developius
2
CSRF adalah untuk memvalidasi pengguna di sisi klien, dan bukan untuk melindungi situs secara umum dari kompromi sisi server seperti yang Anda sarankan.
Tongfa
2
@developius mengirimkan cookie tidak cukup untuk memenuhi perlindungan CSRF. Cookie berisi token csrf, seperti yang dikirim oleh server. Klien yang sah harus membaca token csrf dari cookie, dan meneruskannya di permintaan di suatu tempat, seperti header atau dalam muatan. Perlindungan CSRF memeriksa bahwa nilai dalam cookie cocok dengan nilai dalam permintaan, jika tidak maka permintaan tersebut ditolak. Oleh karena itu, penyerang tidak perlu membaca cookie.
Will M.
1
Jawaban ini sangat langsung ke pertanyaan poster asli dan sangat jelas. +1 Terima kasih.
java-addict301
@ Longfa - terima kasih, ini membantu saya memahami lebih baik. Apakah saya benar untuk menganggap bahwa Token CSRF TIDAK boleh ditempatkan di header? itu pasti di suatu tempat di dalam tubuh?
zerohedge
10

Tebakan terbaik saya untuk jawabannya: Pertimbangkan 3 opsi ini untuk cara mendapatkan token CSRF dari server ke browser.

  1. Di badan permintaan (bukan header HTTP).
  2. Di header HTTP khusus, bukan Set-Cookie.
  3. Sebagai cookie, di header Set-Cookie.

Saya pikir yang pertama, permintaan body (sementara ditunjukkan oleh tutorial Express yang saya tautkan dalam pertanyaan ), tidak portabel untuk berbagai situasi; tidak semua orang menghasilkan setiap respons HTTP secara dinamis; di mana Anda akhirnya harus meletakkan token dalam respons yang dihasilkan mungkin sangat bervariasi (dalam input formulir tersembunyi; dalam sebuah fragmen kode JS atau variabel yang dapat diakses oleh kode JS lainnya; mungkin bahkan dalam URL meskipun itu tampaknya umumnya tempat yang buruk untuk menempatkan token CSRF). Jadi, meskipun dapat dikerjakan dengan beberapa penyesuaian, # 1 adalah tempat yang sulit untuk melakukan pendekatan satu ukuran untuk semua.

Yang kedua, tajuk khusus, menarik tetapi tidak benar-benar berfungsi, karena sementara JS bisa mendapatkan tajuk untuk XHR yang dipanggil, tajuk itu tidak bisa mendapatkan tajuk untuk halaman tempat diambilnya .

Yang meninggalkan yang ketiga, cookie yang dibawa oleh tajuk Set-Cookie, sebagai pendekatan yang mudah digunakan dalam semua situasi (server siapa pun akan dapat mengatur tajuk cookie per-permintaan, dan tidak peduli apa pun jenisnya). data ada di badan permintaan). Jadi terlepas dari kelemahannya, itu adalah metode termudah untuk diterapkan kerangka kerja secara luas.

metamatt
sumber
7
Saya mungkin menyatakan yang sudah jelas, ini berarti bahwa cookie tidak dapat benar httponly?
Foton
1
hanya untuk permintaan ajax (di mana JS perlu mengetahui nilai cookie csrf untuk mengirimnya kembali pada permintaan berikutnya di saluran kedua (baik sebagai data formulir atau header)). Tidak ada alasan untuk meminta token csrf menjadi HttpOnly jika cookie sesi sudah HttpOnly (untuk melindungi terhadap XSS) karena csrf token tidak bernilai dengan sendirinya tanpa sesi yang terkait.
cowbert
2

Selain cookie sesi (yang merupakan standar), saya tidak ingin menggunakan cookie tambahan.

Saya menemukan solusi yang berfungsi untuk saya ketika membangun Aplikasi Web Halaman Tunggal (SPA), dengan banyak permintaan AJAX. Catatan: Saya menggunakan Java sisi server dan sisi klien JQuery, tetapi tidak ada hal ajaib jadi saya pikir prinsip ini dapat diimplementasikan dalam semua bahasa pemrograman populer.

Solusi saya tanpa cookie tambahan sederhana:

Sisi klien

Menyimpan token CSRF yang dikembalikan oleh server setelah login berhasil di variabel global (jika Anda ingin menggunakan penyimpanan web, bukan global itu baik-baik saja tentu saja). Instruksikan JQuery untuk menyediakan header X-CSRF-TOKEN di setiap panggilan AJAX.

Halaman "indeks" utama berisi potongan JavaScript ini:

// Intialize global variable CSRF_TOKEN to empty sting. 
// This variable is set after a succesful login
window.CSRF_TOKEN = '';

// the supplied callback to .ajaxSend() is called before an Ajax request is sent
$( document ).ajaxSend( function( event, jqXHR ) {
    jqXHR.setRequestHeader('X-CSRF-TOKEN', window.CSRF_TOKEN);
}); 

Sisi server

Pada login sukses, buat token CSRF acak (dan cukup panjang), simpan ini di sesi sisi server dan kembalikan ke klien. Saring permintaan masuk (sensitif) tertentu dengan membandingkan nilai header X-CSRF-TOKEN dengan nilai yang disimpan dalam sesi: ini harus cocok.

Panggilan AJAX yang sensitif (data formulir POST dan DAPATKAN data JSON), dan filter sisi server yang menangkapnya, berada di bawah jalur / data layanan / *. Permintaan masuk tidak boleh mengenai filter, jadi ini ada di jalur lain. Permintaan untuk HTML, CSS, JS dan sumber daya gambar juga tidak ada di jalur / dataervice / *, sehingga tidak difilter. Ini tidak mengandung rahasia dan tidak dapat membahayakan, jadi ini baik-baik saja.

@WebFilter(urlPatterns = {"/dataservice/*"})
...
String sessionCSRFToken = req.getSession().getAttribute("CSRFToken") != null ? (String) req.getSession().getAttribute("CSRFToken") : null;
if (sessionCSRFToken == null || req.getHeader("X-CSRF-TOKEN") == null || !req.getHeader("X-CSRF-TOKEN").equals(sessionCSRFToken)) {
    resp.sendError(401);
} else
    chain.doFilter(request, response);
}   
Stephan van Hoof
sumber
Saya pikir Anda ingin CSRF pada permintaan login. Anda tampaknya menggunakan token CSRF juga sebagai token sesi login. Ini juga berfungsi untuk memiliki mereka sebagai token terpisah, dan kemudian Anda dapat menggunakan CSRF pada titik akhir apa pun, apakah pengguna masuk atau tidak.
Tongfa