• การโจมตี csrf
  • ความปลอดภัยเว็บ
  • cross-site request forgery
  • ความปลอดภัยของคุกกี้
  • นโยบาย same-origin
  • การป้องกัน csrf
  • SameSite

ทำความเข้าใจเกี่ยวกับ CSRF อย่างละเอียด

นำเสนอการสำรวจเชิงลึกของการโจมตี Cross-Site Request Forgery (CSRF) โดยอธิบายกลไกของมัน แสดงตัวอย่าง และบอกรายละเอียดวิธีการป้องกันต่างๆ เพื่อเพิ่มความปลอดภัยให้กับเว็บแอปพลิเคชัน

Yijun
Yijun
Developer

เมื่อทำงานเกี่ยวกับการพัฒนาเว็บ โดยเฉพาะกับคุกกี้ เรามักจะได้ยินคำว่า "การตั้งค่านี้ช่วยป้องกัน CSRF" อย่างไรก็ตาม หลายคนมีเพียงความเข้าใจคร่าวๆ เกี่ยวกับ "CSRF" ภาษาในทีนี้หมายถึงอะไร

วันนี้เราจะมาลงรายละเอียดเกี่ยวกับ CSRF (Cross-Site Request Forgery) ซึ่งเป็นข้อบกพร่องด้านความปลอดภัยทางเว็บที่พบได้ทั่วไป เพื่อช่วยให้เราจัดการกับปัญหาที่เกี่ยวข้องกับ CSRF ได้อย่างมีประสิทธิภาพมากขึ้น

CSRF คืออะไร?

CSRF (Cross-Site Request Forgery) เป็นการโจมตีเว็บประเภทหนึ่งที่แฮกเกอร์หลอกให้ผู้ใช้ที่ผ่านการรับรองแล้ว ทำการกระทำที่ไม่ต้องการ สรุปง่ายๆ คือ "แฮกเกอร์แอบแฝงเป็นผู้ใช้และดำเนินการที่ไม่ได้รับอนุญาต"

CSRF ทำงานอย่างไร

เพื่อให้เข้าใจ CSRF เราจำเป็นต้องเข้าใจแนวคิดสำคัญบางอย่าง:

นโยบาย Same-Origin ของเบราว์เซอร์

นโยบาย same-origin เป็นคุณลักษณะด้านความปลอดภัยในเบราว์เซอร์ที่จำกัดว่าด็อกหรือสคริปต์จากต้นกำเนิดหนึ่ง สามารถโต้ตอบกับทรัพยากรจากต้นกำเนิดอื่นได้อย่างไร

ต้นกำเนิดประกอบด้วยโปรโตคอล (เช่น HTTP หรือ HTTPS) ชื่อโดเมน และหมายเลขพอร์ต อย่างเช่น https://example.com:443 เป็นต้นกำเนิดเดียว ในขณะที่ https://demo.com:80 เป็นต้นกำเนิดอื่น

นโยบาย same-origin จำกัดการเข้าถึงข้อมูลระหว่างหน้าจากต้นกำเนิดต่างๆ ซึ่งหมายความว่า:

  • JavaScript จากต้นกำเนิดหนึ่งไม่สามารถอ่าน DOM ของต้นกำเนิดอื่นได้
  • JavaScript จากต้นกำเนิดหนึ่งไม่สามารถอ่าน Cookie, IndexedDB หรือ localStorage ของต้นกำเนิดอื่นได้
  • JavaScript จากต้นกำเนิดหนึ่งไม่สามารถส่งคำขอ AJAX ไปยังต้นกำเนิดอื่นได้ (นอกจากใช้ CORS)

อย่างไรก็ตาม เพื่อรักษาความเปิดกว้างและความสามารถในการทำงานร่วมกันของเว็บ (เช่น โหลดทรัพยากรจาก CDN หรือส่งคำขอไปยัง API ของบุคคลที่สามเพื่อบันทึก) นโยบาย same-origin ไม่ได้จำกัดการร้องขอเครือข่ายข้ามต้นกำเนิด:

  • หน้าเว็บสามารถส่งคำขอ GET หรือ POST ไปยังต้นกำเนิดใดๆ (เช่น โหลดรูปภาพหรือส่งฟอร์ม)
  • ทรัพยากรจากต้นกำเนิดใดๆ สามารถมีได้ (เช่น <script>, <img>, <link>, <iframe> แท็ก)

กลไกการส่งคุกกี้โดยอัตโนมัติ

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

กลไกนี้ทำให้เว็บไซต์จำสถานะการเข้าสู่ระบบของผู้ใช้ได้ง่ายๆ เพราะคำขอแต่ละรายการจะติดข้อมูลแสดงตัวตนของผู้ใช้อัตโนมัติ ตัวหนา ตัวอย่างเช่น เมื่อคุณเข้าสู่เว็บไซต์ธนาคาร (bank.com) และได้รับคุกกี้แสดงตัวตน เมื่อคุณคลิกเพื่อดูรายการบัญชี เบราว์เซอร์จะหาคุกกี้ทั้งหมดที่ตรงกับ bank.com และแนบไปกับคำขอดูรายการบัญชี เซิร์ฟเวอร์ของธนาคารจึงสามารถระบุคุณจากแบ็กเอนด์และส่งข้อมูลบัญชีของคุณกลับมาได้

ขั้นตอนการโจมตี CSRF

  1. ผู้ใช้เข้าสู่เว็บไซต์เป้าหมาย (เช่น เว็บไซต์ธนาคาร) และได้รับคุกกี้รับรองความถูกต้อง ขั้นตอนนี้ใช้กลไกการส่งคุกกี้โดยอัตโนมัติ หลังจากที่เว็บไซต์ธนาคารตั้งค่าคุกกี้รับรองความถูกต้องของตัวตน เบราว์เซอร์จะติดคุกกี้นี้กับคำขอทุกอย่างที่ส่งไปยังเว็บไซต์นั้นโดยอัตโนมัติ

  2. โดยไม่ออกจากระบบ ผู้ใช้เข้าไปยังเว็บไซต์ที่ประสงค์ร้าย ขณะนี้เนื่องจากนโยบาย same-origin เว็บไซต์ที่ประสงค์ร้ายไม่สามารถอ่านหรือแก้ไขคุกกี้ของเว็บไซต์ธนาคารได้โดยตรง ข้อนี้ปกป้องข้อมูลตัวตนของผู้ใช้ไม่ให้ถูกขโมยโดยตรง

  3. เว็บไซต์ที่ประสงค์ร้ายรวมคำขอไปยังเว็บไซต์เป้าหมาย (เช่น การทำธุรกรรมการโอนย้าย) แม้ว่านโยบาย same-origin จะจำกัดการเข้าถึงข้ามต้นกำเนิด แต่มันอนุญาตให้คำขอข้ามต้นกำเนิด เช่น คำขอที่เริ่มต้นผ่าน <img>, <form> แท็ก ผู้โจมตีจะใช้ช่องโหว่นี้

  4. เบราว์เซอร์ของผู้ใช้อัตโนมัติส่งคำขอนี้พร้อมกับคุกกี้ของเว็บไซต์เป้าหมาย นี่เป็นหัวใจหลักของการโจมตี CSRF มันใช้ประโยชน์จากทั้งนโยบาย same-origin ที่อนุญาตคำขอข้ามต้นกำเนิดและกลไกการส่งคุกกี้โดยอัตโนมัติ (แม้แต่คำขอที่ถูกกระตุ้นจากเว็บไซต์ที่ประสงค์ร้ายจะมีคุกกี้ที่ตรงกับโดเมน)

  5. เว็บไซต์เป้าหมายได้รับคำขอ ตรวจสอบความถูกต้องของคุกกี้ และดำเนินการต่อ เซิร์ฟเวอร์ไม่สามารถบอกได้ว่าคำขอนี้มาจากการกระทำของผู้ใช้ที่ถูกต้องหรือไม่ เพราะคุกกี้ที่แนบมานั้นถูกต้อง

ตัวอย่างการโจมตี CSRF

เราขอแสดงให้เห็นถึงการเกิดขึ้นของการโจมตี 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 นี่คือวิธีการทำงานของมัน:

  1. เซิร์ฟเวอร์สร้างโทเค็นที่ไม่สามารถคาดเดาได้ที่ไม่ซ้ำกันสำหรับแต่ละเซสชัน
  2. โทเค็นนี้จะถูกฝังในฟอร์มทั้งหมดสำหรับการดำเนินการที่สำคัญ
  3. เมื่อผู้ใช้ส่งฟอร์ม เซิร์ฟเวอร์จะตรวจสอบความถูกต้องของโทเค็น

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

ตัวอย่างการใช้งาน:

ในฝั่งเซิร์ฟเวอร์ (ใช้ Node.js และ Express):

ใน JavaScript ฝั่งไคลเอนต์:

การตรวจสอบ Referer header

Referer header ประกอบด้วย URL ของหน้าที่เริ่มคำขอ โดยการตรวจสอบ Referer header เซิร์ฟเวอร์สามารถตัดสินได้ว่าคำขอมาจากแหล่งที่มาที่ถูกต้องหรือไม่

เนื่องจากการโจมตี CSRF โดยทั่วไปมาจากโดเมนที่ต่างกัน Referer header จะแสดงชื่อโดเมนของผู้โจมตี โดยการยืนยันว่า Referer เป็นค่าเดียวกับที่คาดหวัง คำขอจากแหล่งที่ไม่รู้จักสามารถถูกบล็อกได้

อย่างไรก็ตาม ควรทราบว่ารักการนี้ไม่ได้ไว้ใจได้ทั้งหมด เนื่องจากเบราว์เซอร์บางส่วนอาจไม่ส่ง Referer header และผู้ใช้สามารถปิดการส่ง Referer header ได้จากการตั้งค่าเบราว์เซอร์หรือโปรแกรมเสริม

ตัวอย่างการใช้งาน:

การใช้แอตทริบิวต์ SameSite ในคุกกี้

SameSite เป็นแอตทริบิวต์ของคุกกี้ที่ใช้ควบคุมว่าคุกกี้จะถูกส่งพร้อมคำขอข้ามเว็บไซต์หรือไม่ ซึ่งมีค่าได้สามค่า:

  • Strict: คุกกี้จะถูกส่งเฉพาะในคำขอที่มาจากเว็บไซต์เดียวกัน
  • Lax: คุกกี้จะถูกส่งในคำขอที่มาจากเว็บไซต์เดียวกันและการนำทางที่ระดับสูงสุด
  • None: คุกกี้จะถูกส่งในคำขอข้ามเว็บไซต์ทุกคำขอ (ต้องใช้ร่วมกับแอตทริบิวต์ Secure)

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

ตัวอย่างการใช้งาน:

การใช้ headers คำขอแบบกำหนดเอง

สำหรับคำขอ AJAX สามารถเพิ่ม headers คำขอแบบกำหนดเองได้ เนื่องจากข้อจำกัดของนโยบาย same-origin ผู้โจมตีไม่สามารถตั้ง headers แบบกำหนดเองในคำขอข้ามต้นกำเนิดได้ เซิร์ฟเวอร์สามารถตรวจสอบการมีอยู่ของ headers แบบกำหนดเองนี้เพื่อยืนยันความชอบธรรมของคำขอ

ตัวอย่างการใช้งาน:

ในฝั่งไคลเอนต์:

ในฝั่งเซิร์ฟเวอร์:

การตรวจสอบคุกกี้สองครั้ง

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

แม้ว่าผู้โจมตีจะรวมคุกกี้ของเว็บไซต์เป้าหมายในการร้องขอข้ามเว็บไซต์ได้ แต่พวกมันไม่สามารถอ่านหรือแก้ไขค่าคุกกี้ได้ และยังไม่สามารถเข้าถึงหรือแก้ไขค่าโทเค็นในหน้าเอกสารได้ โดยการต้องการคำขอให้รวมโทเค็นจากทั้งคุกกี้และพารามิเตอร์ จะเป็นการรับรองว่าคำขอมาจากแหล่งที่มีสิทธิ์อ่านคุกกี้ จึงป้องกัน CSRF ได้อย่างมีประสิทธิภาพ

การใช้การยืนยันตัวตนใหม่สำหรับการดำเนินการที่สำคัญ

สำหรับการดำเนินการที่สำคัญมาก (เช่น การเปลี่ยนรหัสผ่านหรือการโอนจำนวนมาก) ผู้ใช้อาจต้องยืนยันตัวตนใหม่ การยืนยันตัวตนใหม่ให้ผู้ใช้มีจุดตรวจสอบความปลอดภัยเพิ่มเติม แม้ว่าผู้ใช้จะสามารถเริ่มการโจมตี CSRF ได้สำเร็จ แต่เขาก็ไม่สามารถผ่านขั้นตอนการยืนยันตัวตนใหม่ได้

คำแนะนำในการใช้งาน:

  • ก่อนทำการดำเนินการที่สำคัญ เปลี่ยนเส้นทางไปยังหน้าการยืนยันตัวตนแยกต่างหาก
  • ในหน้านี้ต้องให้ผู้ใช้กรอกรหัสผ่านหรือข้อมูลยืนยันตัวตนอื่นๆ
  • หลังจากการยืนยันผ่าน ให้สร้างโทเค็นสำหรับการใช้งานเพียงครั้งเดียวและใช้โทเค็นนี้ในการดำเนินการที่สำคัญต่อไป

สรุป

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

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