ฉันพบปัญหาบางอย่างเกี่ยวกับโทเค็นของแท้ใน Rails เนื่องจากตอนนี้ฉันมีหลายครั้งแล้ว
แต่ฉันไม่ต้องการแก้ปัญหานี้และทำต่อไป ฉันอยากจะเข้าใจโทเค็นของแท้ คำถามของฉันคือคุณมีแหล่งข้อมูลที่สมบูรณ์ในเรื่องนี้หรือคุณจะใช้เวลาอธิบายรายละเอียดที่นี่?
ฉันพบปัญหาบางอย่างเกี่ยวกับโทเค็นของแท้ใน Rails เนื่องจากตอนนี้ฉันมีหลายครั้งแล้ว
แต่ฉันไม่ต้องการแก้ปัญหานี้และทำต่อไป ฉันอยากจะเข้าใจโทเค็นของแท้ คำถามของฉันคือคุณมีแหล่งข้อมูลที่สมบูรณ์ในเรื่องนี้หรือคุณจะใช้เวลาอธิบายรายละเอียดที่นี่?
คำตอบ:
เกิดอะไรขึ้น
เมื่อผู้ใช้ดูฟอร์มเพื่อสร้างอัปเดตหรือทำลายทรัพยากรแอพ Rails จะสร้างแบบสุ่มจัดauthenticity_token
เก็บโทเค็นนี้ในเซสชันและวางไว้ในเขตข้อมูลที่ซ่อนอยู่ในแบบฟอร์ม เมื่อผู้ใช้ส่งแบบฟอร์ม Rails จะค้นหาauthenticity_token
เปรียบเทียบกับแบบฟอร์มที่เก็บไว้ในเซสชันและหากพวกเขาตรงกับคำขอจะได้รับอนุญาตให้ดำเนินการต่อ
ทำไมมันเกิดขึ้น
เนื่องจากโทเค็นของแท้ถูกเก็บไว้ในเซสชันไคลเอนต์จึงไม่สามารถรู้คุณค่าของมัน สิ่งนี้ป้องกันไม่ให้ผู้คนส่งแบบฟอร์มไปยังแอพ Rails โดยไม่ต้องดูฟอร์มภายในแอพนั้น ลองนึกภาพว่าคุณกำลังใช้บริการ A คุณลงชื่อเข้าใช้บริการและทุกอย่างก็โอเค ทีนี้ลองนึกภาพว่าคุณไปใช้บริการ B แล้วคุณเห็นภาพที่คุณชอบและกดที่ภาพเพื่อดูขนาดที่ใหญ่ขึ้น ตอนนี้ถ้าบางรหัสชั่วอยู่ที่นั่นที่บริการ B, มันอาจจะส่งคำขอไปยังบริการ (ซึ่งคุณเข้าสู่ระบบ) http://serviceA.com/close_account
และขอให้ลบบัญชีของคุณโดยการส่งคำขอไปยัง นี่คือสิ่งที่เป็นที่รู้จักกันCSRF (Cross Site ปลอมขอ)
หากบริการ A กำลังใช้โทเค็นของแท้เวกเตอร์การโจมตีนี้จะไม่สามารถใช้งานได้อีกต่อไปเนื่องจากคำขอจากบริการ B จะไม่มีโทเค็นของแท้ที่ถูกต้องและจะไม่ได้รับอนุญาตให้ดำเนินการต่อ
เอกสาร APIอธิบายรายละเอียดเกี่ยวกับเมตาแท็ก:
การป้องกัน CSRF เปิดใช้งานด้วย
protect_from_forgery
วิธีการซึ่งจะตรวจสอบโทเค็นและรีเซ็ตเซสชันหากไม่ตรงกับสิ่งที่คาดหวัง การเรียกใช้วิธีนี้ถูกสร้างขึ้นสำหรับแอพพลิเคชั่น Rails ใหม่ตามค่าเริ่มต้น พารามิเตอร์โทเค็นถูกตั้งชื่อauthenticity_token
ตามค่าเริ่มต้น ต้องเพิ่มชื่อและค่าของโทเค็นนี้ในทุกเลย์เอาต์ที่แสดงแบบฟอร์มโดยรวมcsrf_meta_tags
ไว้ในส่วนหัว HTML
หมายเหตุ
โปรดจำไว้ว่า Rails จะตรวจสอบไม่ใช่วิธี idempotent (POST, PUT / PATCH และ DELETE) คำขอ GET ไม่ได้รับการตรวจสอบโทเค็นของแท้ ทำไม? เนื่องจากข้อมูลจำเพาะ HTTP ระบุว่าคำขอ GET นั้นเป็น idempotent และไม่ควรสร้างแก้ไขหรือทำลายทรัพยากรที่เซิร์ฟเวอร์และคำขอนั้นควรเป็น idempotent (หากคุณเรียกใช้คำสั่งเดียวกันหลายครั้งคุณควรได้ผลลัพธ์เดียวกันทุกครั้ง)
การใช้งานจริงนั้นซับซ้อนกว่าเล็กน้อยตามที่กำหนดไว้ในตอนต้นทำให้มั่นใจในความปลอดภัยที่ดีขึ้น Rails ไม่ได้ออกโทเค็นที่จัดเก็บไว้เหมือนกันกับทุกรูปแบบ ไม่สร้างและเก็บโทเค็นที่แตกต่างกันทุกครั้ง มันสร้างและเก็บแฮชการเข้ารหัสในเซสชันและออกโทเค็นการเข้ารหัสใหม่ซึ่งสามารถจับคู่กับที่เก็บไว้ทุกครั้งที่มีการแสดงหน้าเว็บ ดูrequest_forgery_protection.rb
บทเรียน
ใช้authenticity_token
เพื่อปกป้องวิธีการไม่ใช้ idempotent ของคุณ (POST, PUT / PATCH และ DELETE) ตรวจสอบให้แน่ใจด้วยว่าไม่อนุญาตคำขอ GET ใด ๆ ที่อาจแก้ไขทรัพยากรบนเซิร์ฟเวอร์
แก้ไข:ตรวจสอบความคิดเห็นโดย @erturneเกี่ยวกับคำขอ GET เป็น idempotent เขาอธิบายในวิธีที่ดีกว่าที่ฉันทำที่นี่
โทเค็นของแท้ได้รับการออกแบบเพื่อให้คุณรู้ว่าฟอร์มของคุณกำลังถูกส่งจากเว็บไซต์ของคุณ มันถูกสร้างขึ้นจากเครื่องที่ทำงานด้วยตัวระบุเฉพาะที่มีเพียงเครื่องของคุณเท่านั้นที่สามารถรู้ได้จึงช่วยป้องกันการโจมตีปลอมแปลงคำขอข้ามไซต์
หากคุณมีปัญหากับรางที่ปฏิเสธการเข้าถึงสคริปต์ AJAX ของคุณคุณสามารถใช้
<%= form_authenticity_token %>
เพื่อสร้างโทเค็นที่ถูกต้องเมื่อคุณสร้างแบบฟอร์มของคุณ
โทเค็นของแท้เป็นวิธีการตอบโต้การปลอมแปลงคำขอข้ามไซต์ (CSRF) CSRF คืออะไรคุณถาม
เป็นวิธีที่ผู้โจมตีสามารถจี้เซสชันโดยไม่ต้องรู้โทเค็นเซสชัน
สถานการณ์สมมติ :
โซลูชั่น CSRF :
ตัวอย่างการโจมตีน้อยที่สุดที่จะป้องกันได้: CSRF
บนเว็บไซต์ของevil.com
ฉันฉันโน้มน้าวให้คุณส่งแบบฟอร์มต่อไปนี้:
<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>
หากคุณลงชื่อเข้าใช้ธนาคารของคุณผ่านคุกกี้เซสชันแล้วคุกกี้จะถูกส่งไปและการโอนเงินจะเกิดขึ้นโดยที่คุณไม่รู้ตัว
นั่นคือโทเค็น CSRF ที่เข้ามาเล่น:
ดังนั้นฟอร์มบนเบราว์เซอร์ของแท้จะมีลักษณะดังนี้:
<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>
ดังนั้นการโจมตีของฉันจะล้มเหลวเนื่องจากไม่ได้ส่งauthenticity_token
พารามิเตอร์และไม่มีทางที่ฉันจะเดาได้เพราะมันเป็นตัวเลขสุ่มขนาดใหญ่
เทคนิคการป้องกันนี้เรียกว่ารูปแบบสัญญาณซิงโครไนซ์
นโยบายกำเนิดเดียวกัน
แต่ถ้าผู้โจมตีทำการร้องขอสองครั้งด้วย JavaScript หนึ่งอันเพื่ออ่านโทเค็นและอีกอันที่สองเพื่อทำการถ่ายโอน
รูปแบบโทเค็นของ synchronizer เพียงอย่างเดียวไม่เพียงพอที่จะป้องกันสิ่งนั้น!
นี่คือที่มาของนโยบายกำเนิดเดียวกันเพื่อช่วยชีวิตตามที่ฉันได้อธิบายไว้ที่: /security/8264/why-is-the-the-same-origin-poligin-so-important/72569# 72569
Rails ส่งโทเค็นอย่างไร
ครอบคลุมใน: Rails: csrf_meta_tag ทำงานอย่างไร
โดยทั่วไป:
ผู้ช่วยเหลือ HTML เช่นform_tag
เพิ่มเขตข้อมูลที่ซ่อนอยู่ในแบบฟอร์มสำหรับคุณหากไม่ใช่แบบฟอร์ม GET
AJAX ถูกจัดการโดยอัตโนมัติโดยjquery-ujsซึ่งอ่านโทเค็นจากmeta
องค์ประกอบที่เพิ่มในส่วนหัวของคุณโดยcsrf_meta_tags
(แสดงในเทมเพลตเริ่มต้น) และเพิ่มลงในคำขอใด ๆ ที่ทำ
uJS ยังพยายามอัพเดตโทเค็นในแบบฟอร์มในแฟรกเมนต์แคชที่ล้าสมัย
แนวทางการป้องกันอื่น ๆ
X-Requested-With
:
Origin
ส่วนหัว: /security/91165/why-is-the-synchronizer-token-pattern-preferred-over-the-origin-header-check-toโทเค็นของแท้ใช้เพื่อป้องกันการโจมตีข้ามการร้องขอการปลอมแปลง (CSRF) เพื่อให้เข้าใจโทเค็นของแท้คุณต้องเข้าใจการโจมตี CSRF ก่อน
bank.com
สมมติว่าคุณเป็นผู้เขียน คุณมีแบบฟอร์มบนเว็บไซต์ของคุณที่ใช้ในการโอนเงินไปยังบัญชีอื่นที่มีคำขอ GET:
แฮกเกอร์สามารถส่งคำขอ HTTP ไปยังเซิร์ฟเวอร์ว่าGET /transfer?amount=$1000000&account-to=999999
ใช่ไหม
ไม่ถูกต้อง. การโจมตีของแฮกเกอร์จะไม่ทำงาน โดยทั่วไปแล้วเซิร์ฟเวอร์จะคิดอย่างไร
ฮะ? ผู้ชายคนนี้คือใครพยายามเริ่มการถ่ายโอน ไม่ใช่เจ้าของบัญชีนั่นคือแน่นอน
เซิร์ฟเวอร์รู้ได้อย่างไร เพราะไม่มีsession_id
คุกกี้รับรองความถูกต้องของผู้ร้องขอ
เมื่อคุณลงชื่อเข้าใช้ด้วยชื่อผู้ใช้และรหัสผ่านเซิร์ฟเวอร์จะตั้งsession_id
คุกกี้บนเบราว์เซอร์ของคุณ ด้วยวิธีนี้คุณไม่จำเป็นต้องตรวจสอบสิทธิ์แต่ละคำขอด้วยชื่อผู้ใช้และรหัสผ่านของคุณ เมื่อเบราว์เซอร์ของคุณส่งsession_id
คุกกี้เซิร์ฟเวอร์จะรู้:
โอ้นั่นคือจอห์นโด เขาลงชื่อเข้าใช้สำเร็จ 2.5 นาทีที่แล้ว เขาดีที่จะไป
แฮกเกอร์อาจคิดว่า:
อืมมม คำขอ HTTP ปกติไม่ทำงาน แต่ถ้าฉันสามารถหยิบ
session_id
คุกกี้นั้นฉันจะเป็นสีทอง
เบราว์เซอร์ผู้ใช้มีการตั้งค่าคุกกี้จำนวนมากสำหรับbank.com
โดเมน ทุกครั้งที่ผู้ใช้ทำการร้องขอไปยังbank.com
โดเมนคุกกี้ทั้งหมดจะถูกส่งไปพร้อมกัน รวมไปถึงsession_id
คุกกี้
ดังนั้นหากแฮ็กเกอร์สามารถให้คุณทำตามคำขอ GET ที่โอนเงินเข้าบัญชีเขาจะประสบความสำเร็จ เขาจะหลอกให้คุณทำเช่นนั้นได้อย่างไร? ด้วยการปลอมแปลงคำขอข้ามไซต์
มันค่อนข้างง่ายจริงๆ แฮกเกอร์สามารถพาคุณเยี่ยมชมเว็บไซต์ของเขาได้ บนเว็บไซต์ของเขาเขาสามารถมีแท็กรูปภาพดังต่อไปนี้:
<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">
เมื่อเบราว์เซอร์ผู้ใช้พบแท็กภาพนั้นจะเป็นการขอ GET ไปยัง URL นั้น และเนื่องจากคำขอมาจากเบราว์เซอร์ของเขาจึงจะส่งพร้อมคุกกี้ทั้งหมดที่เชื่อมโยงกับbank.com
มัน หากผู้ใช้เพิ่งลงชื่อเข้าใช้bank.com
... session_id
คุกกี้จะถูกตั้งค่าและเซิร์ฟเวอร์จะคิดว่าผู้ใช้ตั้งใจจะโอนเงิน $ 1,000,000 ไปยังบัญชี 999999!
เอาล่ะอย่าไปเยี่ยมเว็บไซต์อันตรายและคุณก็สบายดี
นั่นไม่เพียงพอ จะเป็นอย่างไรถ้ามีคนโพสต์ภาพนั้นบน Facebook และมันปรากฏบนกระดานข้อความของคุณ? จะเกิดอะไรขึ้นถ้ามันถูกฉีดเข้าไปในเว็บไซต์ที่คุณเข้าชมด้วยการโจมตี XSS
มันไม่ได้เลวร้ายนัก คำขอ GET เท่านั้นที่มีช่องโหว่
ไม่จริง. แบบฟอร์มที่ส่งคำขอ POST สามารถสร้างได้แบบไดนามิก นี่คือตัวอย่างจากRails Guide on Security :
<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>
เมื่อคุณApplicationController
มีสิ่งนี้:
protect_from_forgery with: :exception
นี้:
<%= form_tag do %>
Form contents
<% end %>
รวบรวมไว้ในนี้:
<form accept-charset="UTF-8" action="/" method="post">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
Form contents
</form>
โดยเฉพาะอย่างยิ่งสิ่งต่อไปนี้ถูกสร้างขึ้น:
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
เพื่อป้องกันการโจมตีจาก CSRF ถ้า Rails ไม่เห็นโทเค็นของแท้ที่ส่งมาพร้อมกับคำขอนั้นจะไม่ถือว่าคำขอนั้นปลอดภัย
ผู้โจมตีควรรู้ว่าโทเค็นนี้คืออะไร? ค่าที่แตกต่างจะถูกสร้างขึ้นแบบสุ่มในแต่ละครั้งที่สร้างแบบฟอร์ม:
การโจมตี Cross Site Scripting (XSS) - นั่นเป็นวิธี แต่นั่นเป็นช่องโหว่ที่แตกต่างกันในแต่ละวัน
Authenticity Token
คือวิธีการเพื่อให้รางป้องกัน 'ข้ามไซต์ปลอมขอ (CSRF หรือ XSRF) โจมตี'
เพื่อให้ง่ายตรวจสอบให้แน่ใจว่าคำขอ PUT / POST / DELETE (วิธีการที่สามารถแก้ไขเนื้อหา) ไปยังเว็บแอปของคุณนั้นทำจากเบราว์เซอร์ของไคลเอ็นต์และไม่ใช่จากบุคคลที่สาม (ผู้โจมตี) ที่เข้าถึงคุกกี้ที่สร้างขึ้น ในฝั่งไคลเอ็นต์
เนื่องจากAuthenticity Token
มีความสำคัญมากและใน Rails 3.0+ คุณสามารถใช้งานได้
<%= token_tag nil %>
เพื่อสร้าง
<input name="authenticity_token" type="hidden" value="token_value">
ทุกแห่ง
XSS
ในหน้าเข้าสู่ระบบไม่ใช่เพื่อจุดประสงค์ชั่วร้าย แต่เพื่อสร้างเซสชันใหม่ด้วยชื่อผู้ใช้ที่กรอกไว้ล่วงหน้า value="token_value"
ตอนนี้ฉันรู้ว่าฉันก็สามารถใช้
ระวังกลไกโทเค็นของแท้อาจส่งผลให้เกิดสภาวะการแข่งขันหากคุณมีคำขอหลายรายการพร้อมกันจากลูกค้ารายเดียวกัน ในสถานการณ์นี้เซิร์ฟเวอร์ของคุณสามารถสร้างโทเค็นความถูกต้องได้หลายตัวเมื่อควรมีเพียงหนึ่งเท่านั้นและลูกค้าที่ได้รับโทเค็นก่อนหน้าในรูปแบบจะล้มเหลวเนื่องจากคำขอครั้งถัดไปเนื่องจากโทเค็นคุกกี้เซสชันถูกเขียนทับ มีการเขียนขึ้นเกี่ยวกับปัญหานี้และวิธีแก้ปัญหาไม่ได้ทั้งหมดที่นี่: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/
วิธีการที่authenticity_token
จำเป็น
authenticity_token
เป็นสิ่งจำเป็นในกรณีของวิธีการ idempotent เช่นการโพสต์, การวางและลบเพราะวิธีการ Idempotent มีผลกระทบต่อข้อมูล
ทำไมมันเป็นสิ่งจำเป็น
จะต้องมีการป้องกันจากการกระทำที่ชั่วร้าย authenticity_token ถูกเก็บไว้ในเซสชั่นเมื่อใดก็ตามที่มีการสร้างแบบฟอร์มบนหน้าเว็บสำหรับการสร้างหรือการปรับปรุงทรัพยากรโทเค็นของแท้จะถูกเก็บไว้ในเขตที่ซ่อนอยู่และส่งมาพร้อมกับรูปแบบบนเซิร์ฟเวอร์ ก่อนที่จะดำเนินการกับผู้ใช้ที่ดำเนินการส่ง authenticity_token จะถูกตรวจสอบข้ามโดย
authenticity_token
เก็บไว้ในเซสชัน หากauthenticity_token
เหมือนกันกระบวนการจะดำเนินการต่อมิฉะนั้นจะไม่ดำเนินการใด ๆ
การรับรองความถูกต้องคืออะไร
นี่คือสตริงสุ่มที่ใช้โดยแอปพลิเคชันทางรถไฟเพื่อให้แน่ใจว่าผู้ใช้กำลังร้องขอหรือดำเนินการจากหน้าแอพไม่ใช่จากแอพหรือไซต์อื่น
ทำไมต้องมี authentication_token
เพื่อปกป้องแอปหรือไซต์ของคุณจากการปลอมแปลงคำขอข้ามไซต์
วิธีเพิ่ม authentication_token ให้กับฟอร์มได้อย่างไร
หากคุณกำลังสร้างรูปแบบการใช้ form_for แท็ก authentication_token ถูกเพิ่มโดยอัตโนมัติอื่น ๆ <%= csrf_meta_tag %>
ที่คุณสามารถใช้