Rails: ไม่สามารถตรวจสอบความถูกต้องของโทเค็น CSRF เมื่อทำการร้องขอ POST


91

ฉันต้องการสร้างให้POST requestกับ dev ในพื้นที่ของฉันเช่นนี้:

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

อย่างไรก็ตามจากคอนโซลเซิร์ฟเวอร์จะรายงาน

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

นี่คือตัวควบคุมและการตั้งค่าเส้นทางของฉันมันค่อนข้างง่าย

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

  post 'fetch_heroku' => 'admin#fetch_heroku'

ฉันไม่แน่ใจว่าฉันต้องทำอะไร? หากต้องการปิด CSRF จะใช้งานได้อย่างแน่นอน แต่ฉันคิดว่ามันน่าจะเป็นความผิดพลาดของฉันเมื่อสร้าง API ดังกล่าว

มีการตั้งค่าอื่น ๆ ที่ฉันต้องทำหรือไม่?


5
สำหรับ API เป็นที่ยอมรับโดยทั่วไปในการปิด การตรวจสอบโทเค็นCSRF ฉันใช้protect_from_forgery with: :null_session.
dcestari

คำตอบ:


110

การปลอมแปลงคำขอข้ามไซต์ (CSRF / XSRF) คือเมื่อหน้าเว็บที่เป็นอันตรายหลอกล่อให้ผู้ใช้ดำเนินการตามคำขอที่ไม่ได้มีจุดมุ่งหมายเช่นโดยใช้ bookmarklets, iframes หรือเพียงแค่สร้างเพจที่มีลักษณะคล้ายกันมากพอที่จะหลอกผู้ใช้

การป้องกัน Rails CSRFสร้างขึ้นสำหรับเว็บแอป "คลาสสิก" เพียงแค่ให้ความมั่นใจในระดับหนึ่งว่าคำขอมาจากเว็บแอปของคุณเอง โทเค็น CSRF ทำงานเหมือนความลับที่มีเพียงเซิร์ฟเวอร์ของคุณเท่านั้นที่รู้ - Rails สร้างโทเค็นแบบสุ่มและเก็บไว้ในเซสชัน แบบฟอร์มของคุณส่งโทเค็นผ่านการป้อนข้อมูลที่ซ่อนอยู่และ Rails จะตรวจสอบว่าคำขอใด ๆ ที่ไม่ใช่ GET มีโทเค็นที่ตรงกับสิ่งที่เก็บไว้ในเซสชัน

อย่างไรก็ตาม API มักเป็นไปตามคำจำกัดความข้ามไซต์และมีไว้เพื่อใช้ในเว็บแอปของคุณมากกว่าซึ่งหมายความว่าแนวคิดทั้งหมดของ CSRF ใช้ไม่ได้มากนัก

แต่คุณควรใช้กลยุทธ์ที่ใช้โทเค็นในการตรวจสอบความถูกต้องคำขอ API ด้วยคีย์ API และข้อมูลลับเนื่องจากคุณกำลังตรวจสอบว่าคำขอนั้นมาจากไคลเอนต์ API ที่ได้รับการอนุมัติไม่ใช่จากแอปของคุณเอง

คุณสามารถปิดใช้งาน CSRF ได้ตามที่ @dcestari ชี้ไว้:

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

อัปเดตแล้ว ใน Rails 5 คุณสามารถสร้างเฉพาะแอปพลิเคชัน API โดยใช้ไฟล์--apiตัวเลือก:

rails new appname --api

ไม่รวมมิดเดิลแวร์ CSRF และส่วนประกอบอื่น ๆ อีกมากมายที่เป็น superflouus


ขอบคุณฉันเลือกที่จะปิด CSRF บางส่วน: stackoverflow.com/questions/5669322/…
cqcn1991

4
สิ่งนี้ชี้ให้เห็นว่า API ทั้งหมดให้บริการการรับส่งข้อมูลระหว่างแอปพลิเคชันต่อแอปพลิเคชัน (ซึ่งพันธมิตรรายเดียวจะได้รับคีย์และความลับที่ไม่ซ้ำกัน) ในสถานการณ์นั้นเช่นเดียวกับการสื่อสารระหว่างเซิร์ฟเวอร์กับเซิร์ฟเวอร์คำตอบนี้เหมาะสม อย่างไรก็ตามสิ่งที่สร้างความสับสนให้กับนักพัฒนาเว็บแอปส่วนใหญ่ก็คือสำหรับไคลเอ็นต์ Javascript ซึ่งควบคุมและเขียนโดยคุณคุณไม่ต้องการใช้คีย์ - ลับเดียว (ซึ่งจะเปิดเผยคีย์ - ลับเดียวกับไคลเอ็นต์ทั้งหมด) กลไกเซสชัน CSRF และคุกกี้ของ Rail ทำงานได้ดีแม้กระทั่งสำหรับแอป Javascript ที่ใช้ API ของคุณ - หากคุณส่งโทเค็น CSRF กลับไปที่ Rails พร้อมกับแต่ละคำขอ
Jason FB

วิธีที่คุณทำใน AJAX มีอธิบายไว้ใน SO โพสต์stackoverflow.com/questions/7203304/…
Jason FB

@JasonFB คุณถูกต้องในความลับเดียวกับไคลเอนต์จาวาสคริปต์ไม่ทำงาน อย่างไรก็ตามการใช้เซสชันและการป้องกัน Rails CSRF ยังคงเป็นปัญหาหากคุณต้องการสร้าง API ที่สามารถใช้ได้กับไคลเอนต์ที่ไม่ใช่เบราว์เซอร์ประเภทอื่น ๆ
สูงสุด

หากคุณจัดหาหรือสร้างทางเลือกอื่นให้กับ CSRF เป็นแขกของฉัน เพียงแค่ทราบเหตุผลของสิ่งที่คุณจะแทนที่เพื่อที่คุณจะได้รู้ว่าอะไรเหมาะสมที่จะแทนที่ด้วย เห็นได้ชัดว่าตอนนี้การเล่นลิ้นของเรากลายเป็นบริบท
Jason FB

79

อีกวิธีในการปิด CSRF ที่จะไม่แสดงเซสชันว่างคือการเพิ่ม:

skip_before_action :verify_authenticity_token

ใน Rails Controller ของคุณ เพื่อให้แน่ใจว่าคุณยังสามารถเข้าถึงข้อมูลเซสชันได้

อีกครั้งตรวจสอบให้แน่ใจว่าคุณทำสิ่งนี้ในตัวควบคุม API หรือในที่อื่น ๆ ที่การป้องกัน CSRF ใช้ไม่ได้


1
นี้เหมือนกันกว่าprotect_from_forgery except: [:my_method_name]?
Arnold Roa

19

มีข้อมูลที่เกี่ยวข้องเกี่ยวกับการกำหนดค่า CSRF เกี่ยวกับตัวควบคุม API ในapi.rubyonrails.org :

สิ่งสำคัญคือต้องจำไว้ว่าคำขอ XML หรือ JSON ได้รับผลกระทบเช่นกันและหากคุณกำลังสร้างAPIคุณควรเปลี่ยนวิธีการป้องกันการปลอมแปลงในApplicationController(โดยค่าเริ่มต้น:) :exception:

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

เราอาจต้องการปิดใช้งานการป้องกัน CSRF สำหรับ API เนื่องจากโดยทั่วไปแล้วจะได้รับการออกแบบให้ไม่ระบุสถานะ นั่นคือไคลเอนต์APIคำขอจะจัดการเซสชันให้คุณแทน Rails


4
นี่จึงสับสน ฉันกำลังมองหาระหว่างแหล่งที่มาที่บอกว่าprotect_from_forgeryยังจำเป็นสำหรับ API และแหล่งที่มาที่บอกว่าไม่ใช่ จะเกิดอะไรขึ้นหาก API มีไว้สำหรับแอปหน้าเดียวที่ใช้คุกกี้เซสชันสำหรับการตรวจสอบผู้ใช้
Wylliam Judd

กลไก CSRF เป็นวิธีในตัวของ Rails เพื่อจัดการกับเวกเตอร์การโจมตีโดยใช้คุกกี้เซสชัน กลไกนี้ปกป้องคอนโทรลเลอร์ของคุณในขณะที่ทำงานควบคู่ไปกับคุกกี้เซสชัน Rails หากไม่มี protect_from_forgery หรือปิดหรือตั้งค่าข้อยกเว้นแสดงว่าคุณกำลังแจ้งให้ Rails ไม่ปกป้องการกระทำนั้นโดยใช้ข้อมูลจากโทเค็น CSRF (ซึ่งนำมาจากคุกกี้เซสชัน)
Jason FB

5
ฉันคิดว่าสิ่งที่ทำให้สับสนคือสำหรับเอกสาร Rails "API" หมายถึงแอปพลิเคชันเซิร์ฟเวอร์ต่อเซิร์ฟเวอร์ที่จะรับคำขอ API จากพันธมิตรระยะไกลที่เชื่อถือได้ (ไม่ใช่ผู้ใช้เว็บทั้งหมดที่เข้าชมไซต์ของคุณ) สำหรับ ppl เหล่านั้นให้คู่คีย์ - ลับเฉพาะผ่านกลไกที่ปลอดภัยไม่สามารถแฮ็กได้ สำหรับเว็บแอปพลิเคชันสมัยใหม่ที่ผู้คนจำนวนมากจะโหลดแอปพลิเคชันเว็บของคุณผ่านเว็บไคลเอ็นต์คุณยังคงใช้กลไกเซสชันหรือโทเค็นเพื่อระบุบุคคลที่เข้าชมไซต์ของคุณโดยไม่ซ้ำกัน ดังนั้นหากคุณไม่ใช้กลไกอื่น ๆ ในการทำสิ่งนี้ (โทเค็น Json Web ฯลฯ ) ให้ยึดติดกับสิ่งที่มีอยู่ในตัวของ Rails
Jason FB

1
วิธีที่คุณทำใน AJAX มีอธิบายไว้ใน SO โพสต์stackoverflow.com/questions/7203304/…
Jason FB


3

หากคุณต้องการยกเว้นการดำเนินการตัวอย่างของตัวควบคุมตัวอย่าง

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

  def sample
     render json: @hogehoge
  end
end

คุณสามารถดำเนินการตามคำขอจากภายนอกได้โดยไม่มีปัญหาใด ๆ


0

วิธีแก้ปัญหาที่ง่ายที่สุดคือทำสิ่งที่เป็นมาตรฐานในคอนโทรลเลอร์ของคุณหรือคุณสามารถใส่ลงในApplicationControllerโดยตรง

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


0

หากคุณต้องการข้ามการป้องกัน CSRF สำหรับการดำเนินการของคอนโทรลเลอร์อย่างน้อยหนึ่งรายการ (แทนที่จะเป็นคอนโทรลเลอร์ทั้งหมด) ให้ลองทำเช่นนี้

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

ไหน[:webhook, :index, :create]จะข้ามการตรวจสอบสำหรับผู้ที่ 3 การกระทำ แต่คุณสามารถเปลี่ยนใดก็ตามที่คุณต้องการที่จะข้าม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.