ฉันกำลังเขียนแอปพลิเคชัน (Django มันเกิดขึ้น) และฉันแค่ต้องการทราบว่าจริงๆแล้ว "โทเค็น CSRF" คืออะไรและมันปกป้องข้อมูลอย่างไร ข้อมูลโพสต์ไม่ปลอดภัยหากคุณไม่ใช้โทเค็น CSRF หรือไม่
ฉันกำลังเขียนแอปพลิเคชัน (Django มันเกิดขึ้น) และฉันแค่ต้องการทราบว่าจริงๆแล้ว "โทเค็น CSRF" คืออะไรและมันปกป้องข้อมูลอย่างไร ข้อมูลโพสต์ไม่ปลอดภัยหากคุณไม่ใช้โทเค็น CSRF หรือไม่
คำตอบ:
www.mybank.com
mybank.com
ผลพระทัยในคำขอของ (แนวคิด) http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>
แบบฟอร์ม (ไม่จำเป็นต้องใช้หมายเลขบัญชีของคุณเพราะมันจะบอกเป็นนัย ๆ จากการเข้าสู่ระบบของคุณ)www.cute-cat-pictures.org
โดยไม่ทราบว่าเป็นไซต์ที่เป็นอันตรายmybank.com
(ต้องโชคบาง!) พวกเขาอาจรวมถึงในหน้าของพวกเขาร้องขอเช่นhttp://www.mybank.com/transfer?to=123456;amount=10000
(ซึ่ง123456
เป็นจำนวนบัญชีเกาะเคย์แมนของพวกเขา และ10000
เป็นจำนวนเงินที่คุณเคยคิดว่าคุณเป็นความยินดีที่จะมี)www.cute-cat-pictures.org
หน้านั้นดังนั้นเบราว์เซอร์ของคุณจะทำการร้องขอwww.mybank.com
คุกกี้ของคุณและจะถูกต้องตามกฎหมายอย่างสมบูรณ์ มีเงินของคุณ!นี่คือโลกโดยไม่ต้องราชสกุล CSRF
ตอนนี้เพื่อสิ่งที่ดีกว่าด้วยโทเค็น CSRF :
http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971
.mybank.com
จะรวมอยู่ในหน้าเว็บของพวกเขาเองเมื่อพวกเขาให้บริการแก่คุณ มันแตกต่างกันทุกครั้งที่พวกเขาแสดงหน้าใด ๆ กับใครwww.mybank.com
จะได้รับการปฏิเสธโดยผลลัพธ์: คุณเก็บ10000
หน่วยเงินของคุณ ผมขอแนะนำให้คุณบริจาคบางส่วนของวิกิพีเดีย
(ระยะทางของคุณอาจแตกต่างกัน)
แก้ไขจากความคิดเห็นมูลค่าการอ่าน:
เป็นเรื่องสมควรที่จะทราบว่าสคริปต์จากwww.cute-cat-pictures.org
ปกติจะไม่มีการเข้าถึงโทเค็นต่อต้าน CSRF ของคุณwww.mybank.com
เนื่องจากการควบคุมการเข้าถึง HTTP หมายเหตุนี้มีความสำคัญสำหรับบางคนที่ส่งส่วนหัวอย่างไม่สมเหตุสมผลAccess-Control-Allow-Origin: *
สำหรับการตอบกลับทุกเว็บไซต์โดยไม่ทราบว่าเป็นเพราะเพียงเพราะพวกเขาไม่สามารถใช้ API จากเว็บไซต์อื่น
www.cute-cat-pictures.org
ปกติจะไม่มีการเข้าถึงโทเค็นต่อต้าน CSRF ของคุณwww.mybank.com
เนื่องจากการควบคุมการเข้าถึง HTTP หมายเหตุนี้มีความสำคัญสำหรับบางคนที่ส่งส่วนหัวอย่างไม่สมเหตุสมผลAccess-Control-Allow-Origin: *
สำหรับการตอบกลับทุกเว็บไซต์โดยไม่ทราบว่าเป็นเพราะเพียงเพราะพวกเขาไม่สามารถใช้ API จากเว็บไซต์อื่น
ใช่ข้อมูลโพสต์มีความปลอดภัย แต่ที่มาของข้อมูลนั้นไม่ใช่ วิธีนี้บางคนสามารถหลอกผู้ใช้ด้วย JS ให้เข้าสู่เว็บไซต์ของคุณในขณะที่เรียกดูหน้าเว็บของผู้โจมตี
เพื่อป้องกันสิ่งนั้น django จะส่งคีย์สุ่มทั้งในคุกกี้และข้อมูลในแบบฟอร์ม จากนั้นเมื่อผู้ใช้โพสต์มันจะตรวจสอบว่าสองปุ่มเหมือนกัน ในกรณีที่ผู้ใช้ถูกหลอกเว็บไซต์ของบุคคลที่สามไม่สามารถรับคุกกี้ของเว็บไซต์ของคุณได้ซึ่งจะทำให้เกิดข้อผิดพลาดในการตรวจสอบ
เว็บไซต์สร้างโทเค็นที่ไม่ซ้ำกันเมื่อมันทำให้หน้าแบบฟอร์ม โทเค็นนี้จำเป็นสำหรับการโพสต์ / รับข้อมูลกลับไปยังเซิร์ฟเวอร์
เนื่องจากเว็บไซต์ของคุณสร้างโทเค็นและให้บริการเฉพาะเมื่อมีการสร้างเพจที่มีแบบฟอร์มเว็บไซต์อื่น ๆ บางแห่งจึงไม่สามารถเลียนแบบฟอร์มของคุณได้เนื่องจากจะไม่มีโทเค็นจึงไม่สามารถโพสต์ในเว็บไซต์ของคุณได้
บล็อก Cloud Under มีคำอธิบายที่ดีเกี่ยวกับโทเค็น CSRF
ลองนึกภาพคุณมีเว็บไซต์อย่าง Twitter ที่ง่ายและโฮสต์บน a.com ผู้ใช้ที่ลงชื่อเข้าใช้สามารถป้อนข้อความ (ทวีต) ลงในแบบฟอร์มที่ถูกส่งไปยังเซิร์ฟเวอร์เป็นคำขอ POST และเผยแพร่เมื่อพวกเขากดปุ่มส่ง บนเซิร์ฟเวอร์ผู้ใช้จะถูกระบุโดยคุกกี้ที่มี ID เซสชันที่ไม่ซ้ำกันดังนั้นเซิร์ฟเวอร์ของคุณจะรู้ว่าใครโพสต์ทวีต
แบบฟอร์มอาจจะง่ายเหมือน:
<form action="http://a.com/tweet" method="POST"> <input type="text" name="tweet"> <input type="submit"> </form>
ทีนี้ลองนึกภาพคนเลวคัดลอกและวางแบบฟอร์มนี้ไปยังเว็บไซต์ที่เป็นอันตรายของเขาสมมติว่า b.com แบบฟอร์มยังคงใช้งานได้ ตราบใดที่ผู้ใช้ลงชื่อเข้าใช้ Twitter ของคุณ (นั่นคือพวกเขาได้รับคุกกี้เซสชันที่ถูกต้องสำหรับ a.com) คำขอ POST จะถูกส่งไป
http://a.com/tweet
และประมวลผลตามปกติเมื่อผู้ใช้คลิกปุ่มส่งจนถึงตอนนี้ไม่ใช่ปัญหาใหญ่ตราบใดที่ผู้ใช้ทราบว่าแบบฟอร์มทำอะไร แต่ถ้าคนเลวของเราปรับเปลี่ยนรูปแบบเช่นนี้:
<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>
ทีนี้ถ้าผู้ใช้คนใดคนหนึ่งของคุณจบลงบนเว็บไซต์ของคนเลวและเข้าสู่“ คลิกเพื่อชนะ!” ปุ่มแบบฟอร์มจะถูกส่งไปยังเว็บไซต์ของคุณผู้ใช้จะถูกระบุอย่างถูกต้องโดยรหัสเซสชันในคุกกี้และทวีตที่ซ่อนอยู่ได้รับการเผยแพร่
หากคนเลวของเราแย่ยิ่งกว่านั้นเขาจะทำให้ผู้ใช้ที่ไร้เดียงสาส่งแบบฟอร์มนี้ทันทีที่พวกเขาเปิดหน้าเว็บของเขาโดยใช้จาวาสคริปต์อาจซ่อนตัวอยู่ใน iframe ที่มองไม่เห็น โดยทั่วไปนี่เป็นการปลอมแปลงคำขอข้ามไซต์
สามารถส่งแบบฟอร์มจากทุกที่ไปยังทุกที่ได้อย่างง่ายดาย โดยทั่วไปแล้วเป็นคุณลักษณะทั่วไป แต่มีอีกหลายกรณีที่จำเป็นต้องอนุญาตให้ส่งแบบฟอร์มจากโดเมนที่เป็นของเท่านั้น
สิ่งที่เลวร้ายยิ่งกว่านั้นหากเว็บแอปพลิเคชันของคุณไม่แยกความแตกต่างระหว่างคำขอ POST และ GET (เช่นใน PHP โดยใช้ $ _REQUEST แทนที่จะเป็น $ _POST) อย่าทำอย่างนั้น! คำขอเปลี่ยนแปลงข้อมูลสามารถส่งได้ง่ายเหมือน
<img src="http://a.com/tweet?tweet=This+is+really+bad">
ฝังอยู่ในเว็บไซต์ที่เป็นอันตรายหรือแม้แต่อีเมลฉันจะแน่ใจได้อย่างไรว่าสามารถส่งแบบฟอร์มจากเว็บไซต์ของฉันเองเท่านั้น นี่คือที่ที่โทเค็น CSRF เข้ามาโทเค็น CSRF เป็นสตริงแบบสุ่มที่ยากต่อการคาดเดา บนหน้าเว็บที่มีรูปแบบที่คุณต้องการป้องกันเซิร์ฟเวอร์จะสร้างสตริงแบบสุ่มโทเค็น CSRF เพิ่มไปยังแบบฟอร์มเป็นเขตข้อมูลที่ซ่อนอยู่และจำได้ด้วยวิธีใดวิธีหนึ่งโดยเก็บไว้ในเซสชันหรือโดยการตั้งค่าคุกกี้ มีค่า ตอนนี้แบบฟอร์มจะมีลักษณะเช่นนี้:
<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>
เมื่อผู้ใช้ส่งแบบฟอร์มเซิร์ฟเวอร์จะต้องเปรียบเทียบค่าของเขตข้อมูลที่โพสต์ csrf-token (ชื่อไม่สำคัญ) กับโทเค็น CSRF จำได้โดยเซิร์ฟเวอร์ หากทั้งสองสตริงเท่ากันเซิร์ฟเวอร์อาจดำเนินการกับแบบฟอร์มต่อไป มิฉะนั้นเซิร์ฟเวอร์ควรหยุดการประมวลผลแบบฟอร์มทันทีและตอบกลับด้วยข้อผิดพลาด
ทำไมจึงใช้งานได้ มีสาเหตุหลายประการที่ทำให้คนเลวจากตัวอย่างด้านบนไม่สามารถรับโทเค็น CSRF:
การคัดลอกซอร์สโค้ดแบบสแตติกจากหน้าของเราไปยังเว็บไซต์อื่นจะไม่มีประโยชน์เพราะค่าของฟิลด์ที่ซ่อนอยู่จะเปลี่ยนไปกับผู้ใช้แต่ละคน หากไม่มีเว็บไซต์ของคนเลวที่รู้โทเค็น CSRF ของผู้ใช้ปัจจุบันเซิร์ฟเวอร์ของคุณจะปฏิเสธคำขอ POST เสมอ
เนื่องจากหน้าเว็บที่เป็นอันตรายของคนที่ไม่ดีถูกโหลดโดยเบราว์เซอร์ของผู้ใช้ของคุณจากโดเมนอื่น (b.com แทนที่จะเป็น a.com) คนที่ไม่ดีจึงไม่มีโอกาสที่จะใช้รหัส JavaScript ซึ่งโหลดเนื้อหาและทำให้โทเค็น CSRF ปัจจุบันของผู้ใช้ เว็บไซต์ของคุณ. นั่นเป็นเพราะเว็บเบราว์เซอร์ไม่อนุญาตการร้องขอ AJAX ข้ามโดเมนโดยค่าเริ่มต้น
คนเลวก็ไม่สามารถเข้าถึงคุกกี้ที่เซิร์ฟเวอร์ของคุณกำหนดได้เนื่องจากโดเมนจะไม่ตรงกัน
ฉันควรป้องกันการปลอมแปลงคำขอข้ามไซต์เมื่อใด หากคุณมั่นใจได้ว่าคุณไม่ได้รวมวิธีการรับ GET, POST และอื่น ๆ ตามที่อธิบายไว้ข้างต้นการเริ่มต้นที่ดีคือการปกป้องคำขอ POST ทั้งหมดตามค่าเริ่มต้น
คุณไม่จำเป็นต้องปกป้องคำขอ PUT และ DELETE เนื่องจากตามที่อธิบายไว้ข้างต้นเบราว์เซอร์แบบฟอร์ม HTML มาตรฐานไม่สามารถส่งโดยใช้วิธีการเหล่านั้น
จาวาสคริปต์ในทางตรงกันข้ามสามารถทำให้คำขอประเภทอื่น ๆ เช่นใช้ฟังก์ชัน $ .ajax () ของ jQuery แต่จำไว้ว่าสำหรับคำขอ AJAX ในการทำงานโดเมนต้องตรงกัน (ตราบใดที่คุณไม่ได้กำหนดค่าเว็บเซิร์ฟเวอร์ของคุณอย่างชัดเจน) .
ซึ่งหมายความว่าบ่อยครั้งที่คุณไม่จำเป็นต้องเพิ่มโทเค็น CSRF ในคำขอ AJAX แม้ว่าจะเป็นคำขอ POST แต่คุณจะต้องตรวจสอบให้แน่ใจว่าคุณผ่านการตรวจสอบ CSRF ในเว็บแอปพลิเคชันของคุณเท่านั้นหากคำขอ POST นั้น คำขอ AJAX คุณสามารถทำได้โดยค้นหาการมีส่วนหัวเช่น X-Requested-With ซึ่งการร้องขอ AJAX มักจะรวมถึง นอกจากนี้คุณยังสามารถตั้งค่าส่วนหัวที่กำหนดเองอื่นและตรวจสอบว่ามีอยู่ในฝั่งเซิร์ฟเวอร์ ปลอดภัยเพราะเบราว์เซอร์จะไม่เพิ่มส่วนหัวที่กำหนดเองในการส่งแบบฟอร์ม HTML ปกติ (ดูด้านบน) ดังนั้นจึงไม่มีโอกาสที่ Mr Bad Guy จะจำลองพฤติกรรมนี้ด้วยแบบฟอร์ม
หากคุณมีข้อสงสัยเกี่ยวกับคำขอ AJAX เนื่องจากเหตุผลบางประการที่คุณไม่สามารถตรวจสอบส่วนหัวเช่น X-Requested-With เพียงส่งโทเค็น CSRF ที่สร้างไปยัง JavaScript ของคุณและเพิ่มโทเค็นไปยังคำขอ AJAX มีหลายวิธีในการทำเช่นนี้ อาจเพิ่มลงในเพย์โหลดเหมือนกับแบบฟอร์ม HTML ปกติหรือเพิ่มส่วนหัวที่กำหนดเองในคำขอ AJAX ตราบใดที่เซิร์ฟเวอร์ของคุณรู้ว่าต้องค้นหาที่ใดในคำขอที่เข้ามาและสามารถเปรียบเทียบกับค่าเดิมที่จำได้จากเซสชันหรือคุกกี้คุณจะถูกจัดเรียง
รากของมันทั้งหมดคือเพื่อให้แน่ใจว่าคำขอมาจากผู้ใช้จริงของเว็บไซต์ โทเค็น csrf ถูกสร้างขึ้นสำหรับฟอร์มและต้องเชื่อมโยงกับเซสชันของผู้ใช้ มันถูกใช้เพื่อส่งคำขอไปยังเซิร์ฟเวอร์ซึ่งโทเค็นตรวจสอบพวกเขา นี่เป็นวิธีหนึ่งในการป้องกัน csrf และอีกวิธีหนึ่งคือการตรวจสอบส่วนหัวของผู้อ้างอิง