trusty — Website Trust Platform
Website Security

วิธีวางระบบ Content Security Policy (CSP) สำหรับทีม Product, Engineering และ Privacy ในบริษัท SaaS แบบเป็นขั้นตอน

ขั้นตอนที่ทีม Product, Engineering และ Privacy ใน SaaS ใช้จริงในการออกแบบ CSP ตั้งแต่สำรวจสคริปต์ ทดสอบใน CI/Staging จนถึงเปิดใช้งาน Enforce

📅 เผยแพร่ 10 สิงหาคม 2569อัปเดตล่าสุด 10 สิงหาคม 2569✍️ เขียนโดย trusty Editorial Team⏱ อ่าน 8 นาที
A man in a black hoodie engaged in cybersecurity work using multiple monitors indoors.
ภาพโดย Tima Miroshnichenko จาก Pexels

💬 สรุปสั้น ๆ

การวางระบบ Content Security Policy สำหรับ SaaS เริ่มจากสำรวจโดเมนสคริปต์ที่แอปเรียกใช้จริง เขียนนโยบายแบบ Report-Only ทดสอบใน CI/Staging ร่วมกับ Privacy Team ก่อนค่อยเปลี่ยนเป็น Enforce

ทีม Engineering หลายบริษัท SaaS เปิด Content-Security-Policy แบบ Enforce ตั้งแต่วันแรกแล้วพบว่าหน้า Dashboard โหลด Widget ของ Intercom ไม่ขึ้น ปุ่ม Checkout ที่เรียก Stripe.js ค้าง หรือ Script วิเคราะห์จาก Growth Team ถูกบล็อกทั้งชุด บทความนี้เรียงลำดับขั้นตอนที่ทีม Product, Engineering, Growth และ Privacy ใช้ร่วมกัน เพื่อออกแบบ CSP ให้รัดกุมโดยไม่ทำแอปพัง

เนื้อหาเน้นระดับ Implementation จริง คือ Directive แต่ละตัวควรเขียนอย่างไร ทดสอบใน CI/Staging แบบไหนก่อนขึ้น Production และวิธีประสานกับ ทีม Privacy เรื่อง Allow-list สคริปต์บุคคลที่สาม

ทำความเข้าใจ Directive ที่ทีม Product ต้องออกแบบเอง

Content-Security-Policy ประกอบด้วย Directive หลายตัวที่ควบคุมแหล่งที่มาของทรัพยากรแต่ละประเภท ทีมที่ดูแลแอปต้องกำหนดค่าทีละตัวตามลักษณะการใช้งานจริงของผลิตภัณฑ์ ไม่ใช่ก็อปปี้ Policy จากที่อื่นมาใช้ทั้งชุด

  • script-src — โดเมนที่อนุญาตให้โหลด JavaScript รวมถึง SDK ของ Analytics, Support Widget, Payment ควรระบุ Nonce หรือ Hash แทนการเปิด unsafe-inline ทิ้งไว้
  • style-src — แหล่งที่มาของ CSS รวม Inline Style ที่ Framework Frontend บางตัวแทรกเข้ามาเอง ต้องตรวจว่าจำเป็นต้องเปิด unsafe-inline จริงหรือใช้ Nonce แทนได้
  • connect-src — โดเมน API ที่ Fetch/XHR/WebSocket เรียกใช้ รวมถึง Endpoint ของ Analytics ที่ยิงข้อมูล Event แบบ Asynchronous
  • img-src / font-src — แหล่งรูปภาพและฟอนต์ รวม CDN ที่ทีม Design ใช้ และ Storage Bucket ที่ผู้ใช้อัปโหลดรูปโปรไฟล์
  • frame-ancestors — ควบคุมว่าหน้าเว็บถูกฝังใน iframe จากโดเมนใดได้บ้าง สำคัญมากถ้ามี Embed Widget ให้ลูกค้าฝังในเว็บของตัวเอง
  • report-uri / report-to — Endpoint ที่เบราว์เซอร์ส่งรายงานการละเมิดนโยบายกลับมา ใช้เป็นข้อมูลตัดสินใจก่อนเปลี่ยนเป็น Enforce

ขั้นตอนที่ 1: สำรวจสคริปต์และโดเมนที่แอปเรียกใช้จริงก่อนเขียนนโยบาย

ก่อนเขียน Directive แม้แต่บรรทัดเดียว ทีม Engineering ควรสำรวจก่อนว่าแอปพลิเคชันเรียกใช้ทรัพยากรจากโดเมนใดบ้างในสภาพการใช้งานจริง วิธีที่ใช้ได้ผลคือเปิด Developer Tools แท็บ Network แล้วไล่ดูทุกหน้าหลักของผลิตภัณฑ์ รวมทั้งหน้า Onboarding, Billing และ Settings ที่มักมี Script บุคคลที่สามฝังอยู่มากกว่าหน้าอื่น

ขั้นตอนนี้ต้องทำร่วมกับ Growth Team เพราะ Growth มักเพิ่ม Pixel หรือ Tag Manager ใหม่โดยไม่แจ้ง Engineering ล่วงหน้า รายการโดเมนที่ได้จากขั้นตอนนี้จะเป็น Baseline ตั้งต้นของ Allow-list

ขั้นตอนที่ 2: เริ่มด้วย Header แบบ Report-Only ก่อนเสมอ

อย่าข้ามขั้นนี้แม้จะมั่นใจว่ารายการโดเมนครบแล้ว Header ชื่อ Content-Security-Policy-Report-Only ทำงานเหมือน CSP จริงทุกประการยกเว้นว่าเบราว์เซอร์จะไม่บล็อกทรัพยากรที่ละเมิดนโยบาย แค่ส่งรายงานกลับมาที่ report-uri หรือ report-to เท่านั้น

ปล่อย Report-Only ทำงานคู่ขนานกับ Production จริงอย่างน้อยหนึ่งถึงสองรอบ Sprint เพื่อเก็บรายงานการละเมิดจากผู้ใช้จริงที่มี Browser Extension, Locale หรือ Flow การใช้งานต่างจากที่ทีมทดสอบเอง

ขั้นตอนที่ 3: ทดสอบ CSP ใน CI และ Staging ก่อนขึ้น Production

ผูก Header CSP เข้ากับ Pipeline ให้ทุก Pull Request ที่แก้ไข Frontend รันชุดทดสอบ End-to-End บน Staging ที่เปิด CSP แบบ Enforce จริง ไม่ใช่แค่ Report-Only เพื่อจับ Regression ตั้งแต่ก่อน Merge ดูรายละเอียดการเก็บหลักฐานเพิ่มเติมได้ใน คู่มือ Audit CSP

  • ใช้ Headless Browser เช่น Playwright รันผ่านหน้าหลักของผลิตภัณฑ์ แล้วเก็บ Console Error ที่เกี่ยวกับ Content Security Policy
  • เพิ่ม Assertion ในชุดทดสอบว่าหน้า Checkout, Login และ Dashboard โหลด Widget ภายนอกได้ครบ ไม่มี Resource ถูกบล็อก
  • ตั้งค่า Staging ให้ Header CSP เหมือน Production เป๊ะ เพื่อไม่ให้เจอ Environment Drift ตอน Deploy จริง

ขั้นตอนที่ 4: ประสานกับ Privacy Team เรื่อง Allow-list สคริปต์ Growth และ Analytics

สคริปต์ที่ต้องเพิ่มใน script-src ทุกตัวควรผ่านการตรวจจาก Privacy Team ก่อน ไม่ใช่แค่ Engineering เห็นว่าทำงานได้แล้วเพิ่มเข้า Allow-list ทันที เพราะสคริปต์ Growth บางตัวส่งข้อมูลผู้ใช้ไปยัง Vendor ที่ยังไม่ผ่านการประเมินความเสี่ยงด้าน Privacy

ทำเป็นขั้นตอนคู่ขนาน คือทุกครั้งที่ Growth Team ขอเพิ่มโดเมนใหม่เข้า script-src หรือ connect-src ให้แจ้ง Privacy Team พร้อมเหตุผลการใช้งาน แล้วให้ Privacy Team ยืนยันก่อนแก้ Policy จริง เก็บบันทึกการอนุมัตินี้ไว้เป็น Evidence สำหรับตรวจสอบย้อนหลัง ใช้แนวทางเดียวกับ เช็กลิสต์ CSP สำหรับ SaaS เป็นรายการตรวจก่อน Deploy

ขั้นตอนที่ 5: เปลี่ยนจาก Report-Only เป็น Enforce แบบมีแผน Rollback

เมื่อรายงานจาก Report-Only นิ่งแล้ว คือไม่มี Violation ใหม่จากโดเมนที่ควรอนุญาตอยู่แล้วต่อเนื่องหลายวัน ค่อยเปลี่ยน Header เป็น Content-Security-Policy แบบ Enforce จริง แนะนำให้ Deploy แบบ Canary คือเปิด Enforce ให้ผู้ใช้บางเปอร์เซ็นต์ก่อน แล้วเฝ้าดู Error Rate และ Support Ticket ก่อนเปิดเต็มระบบ

เตรียม Rollback Path ไว้ล่วงหน้า คือสลับกลับไปใช้ Header Report-Only ได้ทันทีถ้าพบว่า Flow สำคัญของผลิตภัณฑ์ เช่น การชำระเงิน ถูกบล็อกโดยไม่คาดคิด

พร้อมตรวจสอบความน่าเชื่อถือของเว็บไซต์คุณหรือยัง?

ทดลองใช้งาน trusty ฟรี ไม่ต้องใช้บัตรเครดิต เริ่มสแกนได้ทันที

ทดลองใช้งานระบบฟรี

ตัวอย่างค่า Directive และเหตุผลที่เลือกใช้

ตารางด้านล่างเป็นตัวอย่างแนวทางเขียนค่าแต่ละ Directive สำหรับแอป SaaS ทั่วไปที่มี Payment, Support Widget และ Analytics ฝังอยู่ ใช้เป็นจุดตั้งต้นแล้วปรับตามโดเมนจริงที่สำรวจได้ในขั้นตอนที่ 1 ไม่ใช่ค่ามาตรฐานที่ใช้ได้กับทุกแอปโดยไม่ต้องปรับ

Directiveแนวทางค่าเริ่มต้นเหตุผล
script-src'self' พร้อม Nonce ต่อ Request และโดเมน SDK ที่อนุมัติแล้วลดพื้นที่โจมตีจาก Script Injection โดยไม่ต้องเปิด unsafe-inline
style-src'self' พร้อม Nonce สำหรับ Style ที่ Framework แทรกเข้ามาเองป้องกัน CSS Injection ที่ใช้ขโมยข้อมูลผ่าน Attribute Selector
connect-srcโดเมน API หลักและปลายทาง Analytics ที่ระบุชัดเจนทีละโดเมนจำกัดปลายทางที่ข้อมูลผู้ใช้ถูกส่งออกผ่าน Fetch หรือ WebSocket
frame-ancestorsโดเมนลูกค้าที่ได้รับอนุญาตให้ฝัง Embed Widget เท่านั้นป้องกัน Clickjacking จากเว็บที่ไม่ได้รับอนุญาต
report-toEndpoint ภายในที่รับ JSON Report ตาม Reporting APIเก็บข้อมูล Violation แบบมีโครงสร้างไว้วิเคราะห์ย้อนหลัง

สำหรับ script-src ที่ต้องรองรับ Framework ฝั่ง Client ที่มีการ Render แบบ Inline บางส่วน แนะนำให้สร้าง Nonce ใหม่ทุก Request ฝั่ง Server แล้วส่งค่าเดียวกันเข้าไปใน Header และ Attribute nonce ของ Tag Script ที่เกี่ยวข้อง วิธีนี้ปลอดภัยกว่าการเปิด unsafe-inline เพราะ Nonce เดาไม่ได้และเปลี่ยนทุกครั้งที่โหลดหน้าใหม่

ตัวอย่าง Timeline การ Rollout แบบ Canary

ทีม Engineering ที่ทำตามขั้นตอนนี้ส่วนใหญ่วาง Timeline การเปลี่ยนจาก Report-Only เป็น Enforce เป็นช่วงสั้นๆ ต่อเนื่องกัน แทนที่จะเปิดทีเดียวทั้งระบบ

  • สัปดาห์ที่ 1-2: เปิด Report-Only เต็มระบบ เก็บรายงาน Violation ทุกวันเพื่อหา Allow-list ที่ขาดหาย
  • สัปดาห์ที่ 3: เปิด Enforce ให้ผู้ใช้ประมาณ 5-10% ผ่าน Feature Flag พร้อมเฝ้าดู Error Rate และ Support Ticket แบบใกล้ชิด
  • สัปดาห์ที่ 4: ขยายเป็น 50% หากไม่พบปัญหาผิดปกติ พร้อมยืนยันว่า Violation Report ใหม่ที่เข้ามาเป็นกรณีคาดการณ์ได้ทั้งหมด
  • สัปดาห์ที่ 5: เปิด Enforce เต็มระบบ 100% พร้อมเก็บ Rollback Path ไว้ใช้งานได้ทันทีอีกอย่างน้อยหนึ่ง Sprint

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

เช็กลิสต์ปฏิบัติ

  • สำรวจโดเมน Script/Style/API ที่แอปเรียกใช้จริงจากทุกหน้าหลัก ก่อนเขียน Directive
  • เปิด Header แบบ Report-Only อย่างน้อย 1-2 Sprint ก่อนเปลี่ยนเป็น Enforce
  • เพิ่มการทดสอบ CSP เข้า CI Pipeline ด้วย Headless Browser บน Staging ที่ตั้งค่าเหมือน Production
  • ให้ Privacy Team ตรวจและอนุมัติทุกโดเมนก่อนเพิ่มเข้า script-src หรือ connect-src
  • Deploy Enforce แบบ Canary ก่อนเปิดเต็มระบบ พร้อมเฝ้าดู Error Rate
  • เตรียม Rollback Path กลับไป Report-Only ได้ทันทีเมื่อพบ Flow สำคัญถูกบล็อก

ข้อผิดพลาดที่พบบ่อย

  • เปิด Enforce ทันทีโดยไม่ผ่านช่วง Report-Only ทำให้ Flow การชำระเงินหรือ Login พังโดยไม่รู้ตัวก่อน
  • ปล่อย unsafe-inline หรือ unsafe-eval ไว้ถาวรใน script-src เพราะแก้ปัญหาเฉพาะหน้าได้เร็ว แต่ทำให้ Policy อ่อนแรงลงมาก
  • เพิ่มโดเมนใหม่เข้า Allow-list ตามคำขอของ Growth Team โดยไม่ให้ Privacy Team ตรวจก่อน
  • ทดสอบ CSP เฉพาะใน Local Development ที่ไม่มี Header จริง แล้วข้ามการทดสอบบน Staging
  • ลืมอัปเดต Policy เมื่อเพิ่ม SDK หรือ Third-party Widget ใหม่ ทำให้ Enforce แล้วเว็บใช้งานไม่ได้

สรุป

การวางระบบ CSP ที่ใช้ได้จริงในบริษัท SaaS ต้องเริ่มจากสำรวจของจริงที่แอปเรียกใช้ ผ่านช่วง Report-Only เพื่อเก็บข้อมูล ทดสอบใน CI/Staging อย่างเป็นระบบ และให้ Privacy Team ร่วมตรวจ Allow-list ทุกครั้งก่อนเปิด Enforce จริง แนวทางนี้ลดความเสี่ยงที่ Flow สำคัญของผลิตภัณฑ์จะพังตอนเปิดใช้งานจริง

แหล่งข้อมูลอ้างอิง

คำถามที่พบบ่อย

ควรเริ่ม CSP ด้วย Report-Only นานแค่ไหนก่อนเปลี่ยนเป็น Enforce โดยทั่วไปทีม Engineering ควรปล่อยให้ Report-Only ทำงานอย่างน้อย 1-2 Sprint เพื่อให้ครอบคลุม Flow การใช้งานที่หลากหลายพอ และให้รายงาน Violation นิ่งลงก่อนค่อยเปลี่ยน

ใครควรเป็นผู้อนุมัติโดเมนใหม่ใน Allow-list ของ script-src ควรให้ Privacy Team ร่วมตรวจทุกครั้งที่ Growth หรือ Engineering ขอเพิ่มโดเมนใหม่ เพราะสคริปต์บางตัวอาจส่งข้อมูลผู้ใช้ไปยัง Vendor ที่ยังไม่ผ่านการประเมินความเสี่ยง

ทดสอบ CSP ใน CI Pipeline ต้องใช้เครื่องมืออะไร ใช้ Headless Browser เช่น Playwright รัน End-to-End Test บน Staging ที่ตั้งค่า Header เหมือน Production จริง แล้วเก็บ Console Error ที่เกี่ยวกับ Content Security Policy เป็น Assertion ในชุดทดสอบ

ถ้าเปิด Enforce แล้วพบว่า Flow การชำระเงินถูกบล็อกต้องทำอย่างไร สลับ Header กลับไปใช้ Report-Only ทันทีตามแผน Rollback ที่เตรียมไว้ล่วงหน้า แล้วตรวจ Violation Report เพื่อหาโดเมนที่ขาดหายไปจาก Allow-list ก่อนเปิด Enforce ใหม่

คำถามที่พบบ่อย

ควรเริ่ม CSP ด้วย Report-Only นานแค่ไหนก่อนเปลี่ยนเป็น Enforce

โดยทั่วไปทีม Engineering ควรปล่อยให้ Report-Only ทำงานอย่างน้อย 1-2 Sprint เพื่อให้ครอบคลุม Flow การใช้งานที่หลากหลายพอ และให้รายงาน Violation นิ่งลงก่อนค่อยเปลี่ยน

ใครควรเป็นผู้อนุมัติโดเมนใหม่ใน Allow-list ของ script-src

ควรให้ Privacy Team ร่วมตรวจทุกครั้งที่ Growth หรือ Engineering ขอเพิ่มโดเมนใหม่ เพราะสคริปต์บางตัวอาจส่งข้อมูลผู้ใช้ไปยัง Vendor ที่ยังไม่ผ่านการประเมินความเสี่ยง

ทดสอบ CSP ใน CI Pipeline ต้องใช้เครื่องมืออะไร

ใช้ Headless Browser เช่น Playwright รัน End-to-End Test บน Staging ที่ตั้งค่า Header เหมือน Production จริง แล้วเก็บ Console Error ที่เกี่ยวกับ Content Security Policy เป็น Assertion ในชุดทดสอบ

ถ้าเปิด Enforce แล้วพบว่า Flow การชำระเงินถูกบล็อกต้องทำอย่างไร

สลับ Header กลับไปใช้ Report-Only ทันทีตามแผน Rollback ที่เตรียมไว้ล่วงหน้า แล้วตรวจ Violation Report เพื่อหาโดเมนที่ขาดหายไปจาก Allow-list ก่อนเปิด Enforce ใหม่

เนื้อหานี้จัดทำขึ้นเพื่อให้ข้อมูลทั่วไปเท่านั้น ไม่ถือเป็นคำแนะนำทางกฎหมาย กรุณาปรึกษาผู้เชี่ยวชาญด้านกฎหมายหรือ Data Protection Officer ของหน่วยงานท่านก่อนนำไปปฏิบัติจริง

พร้อมตรวจสอบความน่าเชื่อถือของเว็บไซต์คุณหรือยัง?

ทดลองใช้งาน trusty ฟรี ไม่ต้องใช้บัตรเครดิต เริ่มสแกนได้ทันที