ทำความเข้าใจเกี่ยวกับ CSRF อย่างละเอียด
นำเสนอการสำรวจเชิงลึกของการโจมตี Cross-Site Request Forgery (CSRF) โดยอธิบายกลไกของมัน แสดงตัวอย่าง และบอกรายละเอียดวิธีการป้องกันต่างๆ เพื่อเพิ่มความปลอดภัยให้กับเว็บแอปพลิเคชัน
นำเสนอการสำรวจเชิงลึกของการโจมตี Cross-Site Request Forgery (CSRF) โดยอธิบายกลไกของมัน แสดงตัวอย่าง และบอกรายละเอียดวิธีการป้องกันต่างๆ เพื่อเพิ่มความปลอดภัยให้กับเว็บแอปพลิเคชัน
เมื่อทำงานเกี่ยวกับการพัฒนาเว็บ โดยเฉพาะกับคุกกี้ เรามักจะได้ยินคำว่า "การตั้งค่านี้ช่วยป้องกัน CSRF" อย่างไรก็ตาม หลายคนมีเพียงความเข้าใจคร่าวๆ เกี่ยวกับ "CSRF" ภาษาในทีนี้หมายถึงอะไร
วันนี้เราจะมาลงรายละเอียดเกี่ยวกับ CSRF (Cross-Site Request Forgery) ซึ่งเป็นข้อบกพร่องด้านความปลอดภัยทางเว็บที่พบได้ทั่วไป เพื่อช่วยให้เราจัดการกับปัญหาที่เกี่ยวข้องกับ CSRF ได้อย่างมีประสิทธิภาพมากขึ้น
CSRF (Cross-Site Request Forgery) เป็นการโจมตีเว็บประเภทหนึ่งที่แฮกเกอร์หลอกให้ผู้ใช้ที่ผ่านการรับรองแล้ว ทำการกระทำที่ไม่ต้องการ สรุปง่ายๆ คือ "แฮกเกอร์แอบแฝงเป็นผู้ใช้และดำเนินการที่ไม่ได้รับอนุญาต"
เพื่อให้เข้าใจ CSRF เราจำเป็นต้องเข้าใจแนวคิดสำคัญบางอย่าง:
นโยบาย same-origin เป็นคุณลักษณะด้านความปลอดภัยในเบราว์เซอร์ที่จำกัดว่าด็อกหรือสคริปต์จากต้นกำเนิดหนึ่ง สามารถโต้ตอบกับทรัพยากรจากต้นกำเนิดอื่นได้อย่างไร
ต้นกำเนิดประกอบด้วยโปรโตคอล (เช่น HTTP หรือ HTTPS) ชื่อโดเมน และหมายเลขพอร์ต อย่างเช่น https://example.com:443 เป็นต้นกำเนิดเดียว ในขณะที่ https://demo.com:80 เป็นต้นกำเนิดอื่น
นโยบาย same-origin จำกัดการเข้าถึงข้อมูลระหว่างหน้าจากต้นกำเนิดต่างๆ ซึ่งหมายความว่า:
อย่างไรก็ตาม เพื่อรักษาความเปิดกว้างและความสามารถในการทำงานร่วมกันของเว็บ (เช่น โหลดทรัพยากรจาก CDN หรือส่งคำขอไปยัง API ของบุคคลที่สามเพื่อบันทึก) นโยบาย same-origin ไม่ได้จำกัดการร้องขอเครือข่ายข้ามต้นกำเนิด:
<script>, <img>, <link>, <iframe> แท็ก)กลไกการส่งคุกกี้โดยอัตโนมัติเป็นคุณสมบัติสำคัญของเบราว์เซอร์ เมื่อเบราว์เซอร์ส่งคำขอไปยังโดเมน มันจะติดคุกกี้ทั้งหมดของโดเมนนั้นโดยอัตโนมัติ กระบวนการนี้เป็นไปโดยอัตโนมัติและไม่ต้องการโค้ด JavaScript หรือการกระทำของผู้ใช้
กลไกนี้ทำให้เว็บไซต์จำสถานะการเข้าสู่ระบบของผู้ใช้ได้ง่ายๆ เพราะคำขอแต่ละรายการจะติดข้อมูลแสดงตัวตนของผู้ใช้อัตโนมัติ
ตัวหนา
ตัวอย่างเช่น เมื่อคุณเข้าสู่เว็บไซต์ธนาคาร (bank.com) และได้รับคุกกี้แสดงตัวตน เมื่อคุณคลิกเพื่อดูรายการบัญชี เบราว์เซอร์จะหาคุกกี้ทั้งหมดที่ตรงกับ bank.com และแนบไปกับคำขอดูรายการบัญชี เซิร์ฟเวอร์ของธนาคารจึงสามารถระบุคุณจากแบ็กเอนด์และส่งข้อมูลบัญชีของคุณกลับมาได้
ผู้ใช้เข้าสู่เว็บไซต์เป้าหมาย (เช่น เว็บไซต์ธนาคาร) และได้รับคุกกี้รับรองความถูกต้อง ขั้นตอนนี้ใช้กลไกการส่งคุกกี้โดยอัตโนมัติ หลังจากที่เว็บไซต์ธนาคารตั้งค่าคุกกี้รับรองความถูกต้องของตัวตน เบราว์เซอร์จะติดคุกกี้นี้กับคำขอทุกอย่างที่ส่งไปยังเว็บไซต์นั้นโดยอัตโนมัติ
โดยไม่ออกจากระบบ ผู้ใช้เข้าไปยังเว็บไซต์ที่ประสงค์ร้าย ขณะนี้เนื่องจากนโยบาย same-origin เว็บไซต์ที่ประสงค์ร้ายไม่สามารถอ่านหรือแก้ไขคุกกี้ของเว็บไซต์ธนาคารได้โดยตรง ข้อนี้ปกป้องข้อมูลตัวตนของผู้ใช้ไม่ให้ถูกขโมยโดยตรง
เว็บไซต์ที่ประสงค์ร้ายรวมคำขอไปยังเว็บไซต์เป้าหมาย (เช่น การทำธุรกรรมการโอนย้าย)
แม้ว่านโยบาย same-origin จะจำกัดการเข้าถึงข้ามต้นกำเนิด แต่มันอนุญาตให้คำขอข้ามต้นกำเนิด เช่น คำขอที่เริ่มต้นผ่าน <img>, <form> แท็ก ผู้โจมตีจะใช้ช่องโหว่นี้
เบราว์เซอร์ของผู้ใช้อัตโนมัติส่งคำขอนี้พร้อมกับคุกกี้ของเว็บไซต์เป้าหมาย นี่เป็นหัวใจหลักของการโจมตี CSRF มันใช้ประโยชน์จากทั้งนโยบาย same-origin ที่อนุญาตคำขอข้ามต้นกำเนิดและกลไกการส่งคุกกี้โดยอัตโนมัติ (แม้แต่คำขอที่ถูกกระตุ้นจากเว็บไซต์ที่ประสงค์ร้ายจะมีคุกกี้ที่ตรงกับโดเมน)
เว็บไซต์เป้าหมายได้รับคำขอ ตรวจสอบความถูกต้องของคุกกี้ และดำเนินการต่อ เซิร์ฟเวอร์ไม่สามารถบอกได้ว่าคำขอนี้มาจากการกระทำของผู้ใช้ที่ถูกต้องหรือไม่ เพราะคุกกี้ที่แนบมานั้นถูกต้อง
เราขอแสดงให้เห็นถึงการเกิดขึ้นของการโจมตี CSRF ด้วยตัวอย่างเฉพาะ โดยใช้เว็บไซต์ธนาคารที่เป็นเรื่องสมมุติ bank.com เป็นตัวอย่าง
ครั้งแรกที่ผู้ใช้เข้าไปที่ https://bank.com และเข้าสู่ระบบบัญชีของเขา
หลังจากเข้าสู่ระบบสำเร็จ เซิร์ฟเวอร์ตั้งค่าคุกกี้รับรองความถูกต้อง เช่น
ผู้ใช้ดำเนินการทำธุรกรรมการโอนย้ายบนเว็บไซต์ธนาคาร เช่น โอนเงิน $1000 ไปยังชื่อ Alice การกระทำนี้อาจส่งคำขอดังนี้:
ตอนนี้สมมติว่าผู้โจมตีสร้างเว็บไซต์ที่ประสงค์ร้าย https://evil.com และมีโค้ด HTML ดังนี้:
เมื่อผู้ใช้คลิกที่ลิ้งค์ https://evil.com โดยยังไม่ออกจากระบบบัญชีธนาคารของเขา เพราะว่าเขาได้ล็อกอินอยู่ใน bank.com แล้ว เบราว์เซอร์มีคุกกี้ session_id ที่ถูกต้อง
หลังจากโหลดหน้าเว็บที่ประสงค์ร้ายแล้ว มันจะส่งฟอร์มที่ซ่อนอยู่โดยอัตโนมัติ ซึ่งจะส่งคำขอโอนไปยัง https://bank.com/transfer
เบราว์เซอร์ของผู้ใช้จะติดคุกกี้ของ bank.com อย่างอัตโนมัติกับคำขอนี้ เซิร์ฟเวอร์ของ bank.com จะได้รับคำขอ ตรวจสอบความถูกต้องของคุกกี้ และแล้วทำการโอนย้ายที่ไม่ได้รับอนุญาต
นี่คือวิธีการป้องกัน CSRF ที่นิยมใช้หลายวิธี เราจะอธิบายรายละเอียดหลักการของแต่ละวิธีและวิธีป้องกัน CSRF อย่างมีประสิทธิภาพ:
โทเค็น CSRF เป็นหนึ่งในวิธีการทั่วไปที่สุดและมีประสิทธิภาพที่สุดในการป้องกันการโจมตี CSRF นี่คือวิธีการทำงานของมัน:
เนื่องจากโทเค็น CSRF เป็นค่าที่ไม่ซ้ำกันผูกพันกับเซสชันของผู้ใช้และผู้โจมตีไม่สามารถรู้หรือเดาค่านี้ได้ (เพราะมันต่างกันทุกครั้ง) แม้ว่าผู้โจมตีจะหลอกให้ผู้ใช้ส่งคำขอ คำขอจะถูกปฏิเสธโดยเซิร์ฟเวอร์เพราะขาดโทเค็น CSRF ที่ถูกต้อง
ตัวอย่างการใช้งาน:
ในฝั่งเซิร์ฟเวอร์ (ใช้ Node.js และ Express):
ใน JavaScript ฝั่งไคลเอนต์:
Referer headerReferer header ประกอบด้วย URL ของหน้าที่เริ่มคำขอ โดยการตรวจสอบ Referer header เซิร์ฟเวอร์สามารถตัดสินได้ว่าคำขอมาจากแหล่งที่มาที่ถูกต้องหรือไม่
เนื่องจากการโจมตี CSRF โดยทั่วไปมาจากโดเมนที่ต่างกัน Referer header จะแสดงชื่อโดเมนของผู้โจมตี โดยการยืนยันว่า Referer เป็นค่าเดียวกับที่คาดหวัง คำขอจากแหล่งที่ไม่รู้จักสามารถถูกบล็อกได้
อย่างไรก็ตาม ควรทราบว่ารักการนี้ไม่ได้ไว้ใจได้ทั้งหมด เนื่องจากเบราว์เซอร์บางส่วนอาจไม่ส่ง Referer header และผู้ใช้สามารถปิดการส่ง Referer header ได้จากการตั้งค่าเบราว์เซอร์หรือโปรแกรมเสริม
ตัวอย่างการใช้งาน:
SameSite ในคุกกี้SameSite เป็นแอตทริบิวต์ของคุกกี้ที่ใช้ควบคุมว่าคุกกี้จะถูกส่งพร้อมคำขอข้ามเว็บไซต์หรือไม่ ซึ่งมีค่าได้สามค่า:
Strict: คุกกี้จะถูกส่งเฉพาะในคำขอที่มาจากเว็บไซต์เดียวกันLax: คุกกี้จะถูกส่งในคำขอที่มาจากเว็บไซต์เดียวกันและการนำทางที่ระดับสูงสุดNone: คุกกี้จะถูกส่งในคำขอข้ามเว็บไซต์ทุกคำขอ (ต้องใช้ร่วมกับแอตทริบิวต์ Secure)เมื่อ SameSite ถูกตั้งค่าเป็น Strict มันจะป้องกันเว็บไซต์บุคคลที่สามไม่ให้ส่งคุกกี้ได้อย่างสมบูรณ์ จึงป้องกันการโจมตี CSRF ได้อย่างมีประสิทธิภาพ
ถ้า SameSite ถูกตั้งค่าเป็น Lax มันจะปกป้องการดำเนินการที่สำคัญในขณะที่อนุญาตให้มีการใช้งานข้ามเว็บไซต์ทั่วไป (เช่น การเข้าสู่เว็บไซต์จากลิงค์ภายนอก)
ตัวอย่างการใช้งาน:
สำหรับคำขอ AJAX สามารถเพิ่ม headers คำขอแบบกำหนดเองได้ เนื่องจากข้อจำกัดของนโยบาย same-origin ผู้โจมตีไม่สามารถตั้ง headers แบบกำหนดเองในคำขอข้ามต้นกำเนิดได้ เซิร์ฟเวอร์สามารถตรวจสอบการมีอยู่ของ headers แบบกำหนดเองนี้เพื่อยืนยันความชอบธรรมของคำขอ
ตัวอย่างการใช้งาน:
ในฝั่งไคลเอนต์:
ในฝั่งเซิร์ฟเวอร์:
การตรวจสอบคุกกี้สองครั้งเป็นเทคนิคป้องกัน CSRF ที่มีประสิทธิภาพ หลักการของมันคือเซิร์ฟเวอร์สร้างโทเค็นสุ่ม ตั้งมันเป็นคุกกี้และฝังมันในหน้าเว็บ (ปกติเป็นฟิลด์ฟอร์มที่ถูกซ่อน) เมื่อเบราว์เซอร์ส่งคำขอ มันจะรวมคุกกี้อย่างอัตโนมัติ ขณะที่ JavaScript ของหน้าเอกสารจะส่งโทเค็นเป็นพารามิเตอร์คำขอ เซิร์ฟเวอร์จะตรวจสอบว่าโทเค็นในคุกกี้ตรงกับโทเค็นในพารามิเตอร์คำขอหรือไม่
แม้ว่าผู้โจมตีจะรวมคุกกี้ของเว็บไซต์เป้าหมายในการร้องขอข้ามเว็บไซต์ได้ แต่พวกมันไม่สามารถอ่านหรือแก้ไขค่าคุกกี้ได้ และยังไม่สามารถเข้าถึงหรือแก้ไขค่าโทเค็นในหน้าเอกสารได้ โดยการต้องการคำขอให้รวมโทเค็นจากทั้งคุกกี้และพารามิเตอร์ จะเป็นการรับรองว่าคำขอมาจากแหล่งที่มีสิทธิ์อ่านคุกกี้ จึงป้องกัน CSRF ได้อย่างมีประสิทธิภาพ
สำหรับการดำเนินการที่สำคัญมาก (เช่น การเปลี่ยนรหัสผ่านหรือการโอนจำนวนมาก) ผู้ใช้อาจต้องยืนยันตัวตนใหม่ การยืนยันตัวตนใหม่ให้ผู้ใช้มีจุดตรวจสอบความปลอดภัยเพิ่มเติม แม้ว่าผู้ใช้จะสามารถเริ่มการโจมตี CSRF ได้สำเร็จ แต่เขาก็ไม่สามารถผ่านขั้นตอนการยืนยันตัวตนใหม่ได้
คำแนะนำในการใช้งาน:
ผ่านการสนทนาอย่างละเอียดนี้ เราหวังว่าคุณจะมีความเข้าใจอย่างครบถ้วนเกี่ยวกับการโจมตี CSRF เราไม่เพียงแต่ได้เรียนรู้วิธีการทำงานของ CSRF เท่านั้น แต่ยังสำรวจวิธีการป้องกันที่มีประสิทธิภาพต่างๆ ด้วย วิธีการเหล่านี้ทั้งหมดสามารถเพิ่มความปลอดภัยให้กับเว็บแอปพลิเคชันได้อย่างมีประสิทธิภาพ
หวังว่าความรู้นี้จะช่วยให้คุณจัดการกับปัญหาที่เกี่ยวข้องกับ CSRF ในการพัฒนาอาร์เบอร์ประจำวันได้ดีขึ้นและสร้างเว็บแอปพลิเคชันที่ปลอดภัยยิ่งขึ้น