Rails: Tidak dapat memverifikasi keaslian token CSRF saat membuat permintaan POST

91

Saya ingin membuat POST requestdev lokal saya, seperti ini:

  HTTParty.post('http://localhost:3000/fetch_heroku',
                :body => {:type => 'product'},)

Namun, dari konsol server itu melaporkan

Started POST "/fetch_heroku" for 127.0.0.1 at 2016-02-03 23:33:39 +0800
  ActiveRecord::SchemaMigration Load (0.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by AdminController#fetch_heroku as */*
  Parameters: {"type"=>"product"}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 1ms

Ini pengontrol dan pengaturan rute saya, ini cukup sederhana.

  def fetch_heroku
    if params[:type] == 'product'
      flash[:alert] = 'Fetch Product From Heroku'
      Heroku.get_product
    end
  end

  post 'fetch_heroku' => 'admin#fetch_heroku'

Saya tidak yakin apa yang harus saya lakukan? Untuk mematikan CSRF pasti akan berhasil, tetapi saya pikir itu adalah kesalahan saya saat membuat API semacam itu.

Apakah ada penyiapan lain yang perlu saya lakukan?

cqcn1991
sumber
5
Untuk API secara umum diterima untuk mematikan validasi token CSRF . Saya menggunakan protect_from_forgery with: :null_session.
dcestari

Jawaban:

110

Pemalsuan permintaan lintas situs (CSRF / XSRF) adalah ketika halaman web jahat menipu pengguna agar melakukan permintaan yang tidak dimaksudkan, misalnya dengan menggunakan bookmarklet, iframe, atau hanya dengan membuat halaman yang secara visual cukup mirip untuk menipu pengguna.

The perlindungan Rails CSRF dibuat untuk "klasik" web apps - itu hanya memberikan tingkat jaminan bahwa permintaan berasal dari aplikasi web Anda sendiri. Token CSRF berfungsi seperti rahasia yang hanya diketahui oleh server Anda - Rails menghasilkan token acak dan menyimpannya dalam sesi. Formulir Anda mengirim token melalui input tersembunyi dan Rails memverifikasi bahwa permintaan non GET apa pun menyertakan token yang cocok dengan apa yang disimpan dalam sesi.

Namun API biasanya menurut definisi lintas situs dan dimaksudkan untuk digunakan di lebih dari aplikasi web Anda, yang berarti bahwa seluruh konsep CSRF tidak cukup berlaku.

Sebagai gantinya, Anda harus menggunakan strategi berbasis token untuk mengautentikasi permintaan API dengan kunci dan rahasia API karena Anda memverifikasi bahwa permintaan tersebut berasal dari klien API yang disetujui - bukan dari aplikasi Anda sendiri.

Anda dapat menonaktifkan CSRF seperti yang ditunjukkan oleh @dcestari:

class ApiController < ActionController::Base
  protect_from_forgery with: :null_session
end

Diperbarui. Di Rails 5 Anda dapat membuat aplikasi hanya API dengan menggunakan --apiopsi:

rails new appname --api

Mereka tidak menyertakan middleware CSRF dan banyak komponen lain yang superflouus.

maks
sumber
Terima kasih, saya memilih untuk menonaktifkan sebagian dari CSRF: stackoverflow.com/questions/5669322/…
cqcn1991
4
hal ini menunjukkan bahwa semua API melayani lalu lintas aplikasi-ke-aplikasi (di mana satu mitra diberikan kunci & rahasia unik). Dalam skenario itu, seperti komunikasi server-ke-server, jawaban ini sesuai. NAMUN, yang membingungkan sebagian besar pengembang aplikasi web adalah bahwa untuk klien Javascript, yang dikontrol dan ditulis oleh Anda, Anda TIDAK INGIN menggunakan satu kunci-rahasia (yang akan mengekspos satu kunci-rahasia ke semua klien). Sebaliknya, CSRF Rail dan mekanisme sesi cookie bekerja dengan baik — bahkan untuk aplikasi Javascript yang menggunakan API Anda — jika Anda meneruskan token CSRF kembali ke Rails dengan setiap permintaan.
Jason FB
cara Anda melakukan ini di AJAX dijelaskan dalam posting SO ini stackoverflow.com/questions/7203304/…
Jason FB
@JasonFB Anda benar karena satu rahasia dengan klien javascript tidak berfungsi. Namun menggunakan sesi dan perlindungan CSRF Rails masih bermasalah jika Anda bermaksud untuk membangun API yang juga dapat digunakan oleh jenis klien non-browser lainnya.
maks
Jika Anda menyediakan atau membangun alternatif untuk CSRF, jadilah tamu saya. Ketahui saja alasan Anda mengganti sehingga Anda tahu apa yang tepat untuk menggantinya. Jelas, sekarang pertengkaran kita menjadi kontekstual.
Jason FB
79

Cara lain untuk mematikan CSRF yang tidak akan membuat sesi null adalah dengan menambahkan:

skip_before_action :verify_authenticity_token

di Rails Controller Anda. Ini akan memastikan Anda masih memiliki akses ke info sesi.

Sekali lagi, pastikan Anda hanya melakukan ini di pengontrol API atau di tempat lain di mana perlindungan CSRF tidak cukup berlaku.

Matt Waldron
sumber
1
Ini sama dengan protect_from_forgery except: [:my_method_name]?
Arnold Roa
19

Ada info yang relevan tentang konfigurasi CSRF sehubungan dengan pengontrol API di api.rubyonrails.org :

Penting untuk diingat bahwa permintaan XML atau JSON juga terpengaruh dan jika Anda membuat API, Anda harus mengubah metode perlindungan pemalsuan di ApplicationController(secara default:) :exception:

class ApplicationController < ActionController::Base
  protect_from_forgery unless: -> { request.format.json? }
end

Kami mungkin ingin menonaktifkan perlindungan CSRF untuk API karena mereka biasanya dirancang untuk menjadi tanpa status. Artinya, klien API permintaan akan menangani sesi untuk Anda, bukan Rails.

Tuan Tao
sumber
4
Ini sangat membingungkan. Saya jungkir balik antara sumber yang mengatakan itu protect_from_forgerymasih diperlukan untuk API, dan sumber yang mengatakan bahwa itu tidak. Bagaimana jika API untuk aplikasi satu halaman, yang menggunakan cookie sesi untuk otentikasi pengguna?
Wylliam Judd
Mekanisme CSRF adalah cara bawaan Rails untuk menangani vektor serangan menggunakan cookie sesi. Mekanisme ini melindungi pengontrol Anda, saat beroperasi bersama (sebenarnya di dalam) cookie sesi Rails. Tanpa protect_from_forgery, atau mematikannya atau menyetel pengecualian padanya, Anda memberi tahu Rails untuk TIDAK melindungi tindakan itu menggunakan informasi dari token CSRF (yang diambil dari cookie sesi).
Jason FB
5
Menurut saya yang membingungkan adalah untuk dokumentasi Rails "API" berarti aplikasi server-ke-server yang akan menerima permintaan API dari mitra jarak jauh tepercaya (tidak semua pengguna web yang mengunjungi situs Anda). Untuk ppl itu, berikan pasangan kunci-rahasia unik melalui mekanisme aman yang tidak dapat diretas. Untuk aplikasi web modern, di mana banyak orang akan memuat aplikasi web Anda melalui klien web, Anda masih menggunakan sesi atau mekanisme berbasis token untuk mengidentifikasi secara unik setiap orang yang mengunjungi situs Anda. Jadi, kecuali Anda menggunakan BEBERAPA mekanisme LAIN untuk melakukan ini (token Json Web, dll), tetap gunakan barang bawaan Rails.
Jason FB
1
cara Anda melakukan ini di AJAX dijelaskan dalam posting SO ini stackoverflow.com/questions/7203304/…
Jason FB
13

Sejak Rails 5 Anda juga dapat membuat kelas baru dengan :: API, bukan :: Base:

class ApiController < ActionController::API
end
webaholik
sumber
3

Jika Anda ingin mengecualikan tindakan sampel pengontrol sampel

class TestController < ApplicationController
  protect_from_forgery :except => [:sample]

  def sample
     render json: @hogehoge
  end
end

Anda dapat memproses permintaan dari luar tanpa masalah.

Ryosuke Hujisawa
sumber
0

Solusi paling sederhana untuk masalah ini adalah melakukan hal-hal standar di pengontrol Anda atau Anda dapat langsung memasukkannya ke ApplicationController

class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true end

Arish Khan
sumber
0

Jika Anda hanya ingin melewatkan proteksi CSRF untuk satu atau lebih aksi pengontrol (bukan seluruh pengontrol), coba ini

skip_before_action :verify_authenticity_token, only [:webhook, :index, :create]

Di mana [:webhook, :index, :create]akan melewati pemeriksaan untuk 3 tindakan tersebut, tetapi Anda dapat mengubah ke mana saja yang ingin Anda lewati

stevec
sumber