Nginx

Content Security Policy (CSP) และ Security Headers ขั้นสูงใน Nginx

ความปลอดภัยของเว็บแอปพลิเคชันไม่ได้เกิดขึ้นเพียงแค่ผ่านการควบคุมการเข้าถึง (access control) หรือการเข้ารหัสข้อมูล (encryption) เท่านั้น ด้านหนึ่งที่มีความสำคัญอย่างมากคือ Security Headers ซึ่งเป็นเลเยอร์การป้องกันที่บอกให้เบราว์เซอร์ของผู้ใช้รู้ว่าจะแสดงผลและจัดการเนื้อหาได้อย่างไร หากไม่มีการตั้งค่า Security Headers ที่เหมาะสม เว็บไซต์ของคุณอาจเสี่ยงต่อการโจมตีประเภท XSS (Cross-Site Scripting), Clickjacking, และ Malware Injection

บทความนี้จะอธิบายรายละเอียดเกี่ยวกับ Content Security Policy (CSP) และ Security Headers ขั้นสูงอื่น ๆ พร้อมวิธีการนำไปใช้กับ Nginx เพื่อให้คุณสามารถปกป้องเว็บไซต์ของคุณได้อย่างมีประสิทธิภาพ

ความสำคัญของ Security Headers ต่อการป้องกันเว็บไซต์

Security Headers เป็นข้อมูลที่เซิร์ฟเวอร์ส่งมากับการตอบสนองทุกครั้ง (HTTP response) เพื่อให้เบราว์เซอร์ทราบถึงนโยบายความปลอดภัยของเว็บไซต์ ฟีเจอร์นี้ช่วยให้:

  • ลดความเสี่ยงต่อการโจมตี XSS (Cross-Site Scripting) ได้อย่างมาก
  • ป้องกัน Clickjacking และการเอียวใจให้ผู้ใช้คลิกไปยังเว็บไซต์ที่เป็นอันตราย
  • ควบคุมว่าเนื้อหาใดสามารถโหลดและมาจากแหล่งใดได้
  • บังคับให้ใช้ HTTPS สำหรับการสื่อสารทั้งหมด
  • รายงานความเสี่ยงผ่าน CSP Reporting เพื่อให้คุณสามารถติดตามการละเมิดนโยบายได้

Content Security Policy (CSP) — ความเข้าใจเบื้องต้น

Content Security Policy หรือ CSP เป็นมาตรการป้องกันที่ช่วยลดความเสี่ยงต่อการโจมตีประเภท XSS, Injection Attacks และ Data Exfiltration ด้วยการกำหนด Whitelist ของแหล่งต้นทาง (origin) ที่อนุญาตให้โหลดเนื้อหาต่าง ๆ เช่น JavaScript, CSS, รูปภาพ, ฟอนต์ เป็นต้น

CSP ทำงานด้วยการส่ง HTTP Header ชื่อ Content-Security-Policy จากเซิร์ฟเวอร์ไปยังเบราว์เซอร์ และเบราว์เซอร์จะปฏิบัติตามนโยบายที่กำหนดไว้ หากมีสคริปต์หรือทรัพยากรใดที่ละเมิดนโยบาย เบราว์เซอร์จะปฏิเสธการโหลด

ตัวอย่าง CSP Policy ง่าย ๆ

Content-Security-Policy: default-src 'self';

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

CSP Directives — คำสั่งที่สำคัญในนโยบาย

default-src

default-src เป็นตัวกำหนดค่าเริ่มต้นสำหรับ Directive อื่น ๆ ทั้งหมด หากไม่ได้ระบุ Directive อื่น ๆ ค่า default-src จะถูกใช้แทน

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';

script-src — การควบคุม JavaScript

script-src ใช้สำหรับควบคุมว่าสคริปต์ JavaScript สามารถมาจากแหล่งใดได้ ค่าต่าง ๆ ที่ใช้บ่อย ๆ ได้แก่:

  • 'self' — อนุญาตเฉพาะสคริปต์จากโดเมนของตัวเอง
  • 'unsafe-inline' — อนุญาตให้ใช้ Inline Script (ไม่แนะนำในสภาพแวดล้อมที่ปลอดภัยสูง)
  • 'unsafe-eval' — อนุญาตให้ใช้ eval() (ตั้งแต่ JavaScript)
  • https://trusted-cdn.com — อนุญาตเฉพาะจากโดเมนที่ระบุ
  • 'nonce-' — อนุญาตเฉพาะสคริปต์ที่มี nonce attribute ตรงกัน
  • 'sha256-' — อนุญาตเฉพาะสคริปต์ที่มี hash ตรงกับค่าที่ระบุ
Content-Security-Policy: script-src 'self' 'nonce-random123' https://cdn.example.com;

style-src — การควบคุม CSS

style-src ใช้สำหรับควบคุมว่า CSS และสไตล์ (inline styles) สามารถมาจากแหล่งใดได้ การควบคุมนี้มีความสำคัญเพราะว่า CSS สามารถใช้เพื่อดำเนินการโจมตี CSS Injection ได้

Content-Security-Policy: style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;

img-src — การควบคุมรูปภาพ

img-src ระบุว่าเบราว์เซอร์สามารถโหลดรูปภาพจากแหล่งใดได้ ข้อมูลรูปภาพสามารถใช้ในการส่วนรั่วไหลข้อมูล (data exfiltration) ดังนั้นการควบคุมจึงมีความสำคัญ

Content-Security-Policy: img-src 'self' data: https:;

ในตัวอย่างนี้:

  • 'self' — อนุญาตรูปภาพจากตัวเอง
  • data: — อนุญาตรูปภาพที่เป็น Data URLs (Base64 encoded)
  • https: — อนุญาตรูปภาพจาก HTTPS URLs ใด ๆ

connect-src — การควบคุม AJAX, WebSocket, และ Fetch

connect-src ใช้สำหรับควบคุมว่าเว็บไซต์สามารถเชื่อมต่อไปยังแหล่งใดได้ผ่านทาง XMLHttpRequest, Fetch API, WebSocket, และ EventSource

Content-Security-Policy: connect-src 'self' https://api.example.com wss://socket.example.com;

report-uri — การรายงานการละเมิดนโยบาย

report-uri (ขณะนี้ deprecated และแนะนำให้ใช้ report-to แทน) ระบุจุดปลายทาง (endpoint) ที่เบราว์เซอร์ควรส่งรายงานการละเมิด CSP ไปยัง

Content-Security-Policy: default-src 'self'; report-uri https://example.com/csp-report;

เซิร์ฟเวอร์จะได้รับรายงาน JSON ที่มีข้อมูลเกี่ยวกับการละเมิด เช่น ประเภทการละเมิด ตำแหน่งที่ละเมิด และข้อมูลอื่น ๆ

Security Headers อื่น ๆ ที่สำคัญ

X-Frame-Options — ป้องกัน Clickjacking

X-Frame-Options ควบคุมว่าเว็บไซต์สามารถถูกฝังในเฟรม (iframe) ได้หรือไม่ ซึ่งช่วยป้องกันการโจมตี Clickjacking ที่ผู้โจมตีพยายามฝังเว็บไซต์ของคุณในเฟรมที่ไม่มองเห็นได้เพื่อให้ผู้ใช้คลิกโดยไม่รู้ตัว

X-Frame-Options: DENY

ค่าที่ใช้บ่อย ๆ:

  • DENY — ห้ามไม่ให้ฝังในเฟรมอย่างสิ้นเชิง
  • SAMEORIGIN — อนุญาตการฝังเฉพาะจากเฟรมที่มีการสร้างที่มาจากโดเมนเดียวกัน
  • ALLOW-FROM uri — อนุญาตการฝังเฉพาะจากโดเมนที่ระบุ (deprecated)

X-Content-Type-Options — ป้องกัน MIME Type Sniffing

X-Content-Type-Options บอกให้เบราว์เซอร์รู้ว่าไม่ควร “เดา” ประเภท MIME ของไฟล์ เบราว์เซอร์บางตัวอาจพยายามเดาประเภท MIME ซึ่งอาจทำให้เกิดความเสี่ยงด้านความปลอดภัยได้

X-Content-Type-Options: nosniff

ค่าเดียวที่สำคัญคือ nosniff ซึ่งหมายความว่าเบราว์เซอร์ต้องเชื่อประเภท MIME ที่ระบุโดยเซิร์ฟเวอร์

Referrer-Policy — การควบคุมการส่ง Referrer Information

Referrer-Policy ระบุว่าเบราว์เซอร์ควรส่งข้อมูล HTTP Referrer Header ไปยังเว็บไซต์อื่นเมื่อผู้ใช้นำทางไปยังลิงก์ ซึ่งช่วยป้องกันการรั่วไหลข้อมูล

Referrer-Policy: strict-origin-when-cross-origin

ค่าที่ใช้บ่อย ๆ:

  • no-referrer — ไม่ส่ง Referrer Header เลย
  • strict-origin — ส่ง URL ต้นทางเท่านั้น (เฉพาะโปรโตคอล, โดเมน, พอร์ต)
  • strict-origin-when-cross-origin — ส่ง URL เต็มสำหรับคำขอทั่ว ๆ ไป ส่งเฉพาะต้นทางสำหรับคำขอข้ามโดเมน
  • same-origin — ส่งข้อมูล referrer เฉพาะสำหรับคำขอไปยังโดเมนเดียวกัน

Permissions-Policy — การควบคุมฟีเจอร์เบราว์เซอร์

Permissions-Policy (เดิมชื่อ Feature-Policy) ช่วยให้คุณควบคุมว่าฟีเจอร์เบราว์เซอร์ใดที่สามารถใช้ได้ เช่น กล้อง, ไมโครโฟน, ตำแหน่งที่ตั้ง, ไจโรสโคป เป็นต้น

Permissions-Policy: camera=(), microphone=(), geolocation=(self)

ในตัวอย่างนี้:

  • กล้องถูกปิดใช้งาน
  • ไมโครโฟนถูกปิดใช้งาน
  • ตำแหน่งที่ตั้งสามารถใช้ได้เฉพาะสำหรับเว็บไซต์เท่านั้น

Strict-Transport-Security (HSTS) — บังคับใช้ HTTPS

Strict-Transport-Security บอกให้เบราว์เซอร์รู้ว่าควรสื่อสารกับเว็บไซต์เฉพาะผ่าน HTTPS เท่านั้น ลดความเสี่ยงต่อการโจมตี Man-in-the-Middle (MITM)

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

พารามิเตอร์:

  • max-age=31536000 — บังคับให้ใช้ HTTPS เป็นเวลา 1 ปี (31536000 วินาที)
  • includeSubDomains — ใช้นโยบาย HSTS กับ subdomains ด้วย
  • preload — อนุญาตให้เว็บไซต์ของคุณถูกเพิ่มลงในรายชื่อ HSTS Preload ของเบราว์เซอร์

การใช้ Security Headers ใน Nginx

เมื่อต้องการเพิ่ม Security Headers ใน Nginx ให้ใช้ directive add_header ใน block server หรือ location

ตัวอย่างการตั้งค่า CSP พื้นฐานใน Nginx

server {
    listen 443 ssl http2;
    server_name example.com;

    # Content Security Policy
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; report-uri https://example.com/csp-report;" always;

    # X-Frame-Options
    add_header X-Frame-Options "DENY" always;

    # X-Content-Type-Options
    add_header X-Content-Type-Options "nosniff" always;

    # Referrer-Policy
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Permissions-Policy
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=(self)" always;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

ตัวอย่างการตั้งค่า CSP Report-Only Mode

ในช่วงแรก ควรใช้ CSP ในโหมด Report-Only เพื่อทดสอบและรวบรวมข้อมูลเกี่ยวกับการละเมิดของนโยบายโดยไม่บังคับใช้นโยบายจริง ๆ

server {
    listen 443 ssl http2;
    server_name example.com;

    # CSP Report-Only Mode — เพื่อทดสอบและรวบรวมข้อมูล
    add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; report-uri https://example.com/csp-report;" always;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

คุณต้องสร้าง endpoint (เช่น PHP, Node.js) เพื่อรับและบันทึกรายงาน CSP

ขั้นที่ 3: วิเคราะห์รายงาน

ตรวจสอบรายงานและปรับแต่ง Policy ตามความจำเป็น

ขั้นที่ 4: นำไปใช้ในโหมดบังคับใช้

เมื่อแน่ใจแล้ว เปลี่ยนจาก Content-Security-Policy-Report-Only เป็น Content-Security-Policy

ตัวอย่างการตั้งค่า Complete Security Headers สำหรับ Production

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=(self)" always;

    # Content Security Policy
    add_header Content-Security-Policy "
        default-src 'self';
        script-src 'self' https://trusted-cdn.com 'nonce-$request_id';
        style-src 'self' 'nonce-$request_id' https://fonts.googleapis.com;
        img-src 'self' data: https:;
        font-src 'self' https://fonts.gstatic.com;
        connect-src 'self' https://api.example.com;
        media-src 'self';
        object-src 'none';
        frame-ancestors 'none';
        base-uri 'self';
        report-uri https://example.com/csp-report;
    " always;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

สรุป

Content Security Policy (CSP) และ Security Headers อื่น ๆ เป็นเลเยอร์ป้องกันที่สำคัญสำหรับเว็บไซต์ของคุณ ด้วยการตั้งค่า Security Headers ที่ถูกต้อง คุณสามารถลดความเสี่ยงต่อการโจมตี XSS, Clickjacking, MIME Type Sniffing และการรั่วไหลข้อมูลได้อย่างมาก

ขอแนะนำให้:

  • เริ่มต้นด้วย CSP Report-Only mode เพื่อทดสอบและรวบรวมข้อมูล
  • ใช้เครื่องมือเช่น securityheaders.com เพื่อตรวจสอบและปรับปรุง Headers
  • หลีกเลี่ยงการใช้ ‘unsafe-inline’ และ ‘unsafe-eval’ เท่าที่เป็นไปได้
  • ทบทวน CSP Policy อย่างสม่ำเสมอเมื่อมีการเปลี่ยนแปลงในแอปพลิเคชัน
  • ใช้ Nonces หรือ Hashes สำหรับ Inline Scripts และ Styles
  • ติดตามการรายงาน CSP เพื่อระบุและแก้ไขปัญหาความปลอดภัยได้ทันท่วงที

แนะนำบริการ DE

การตั้งค่า Security Headers และ CSP ที่เหมาะสมเป็นสิ่งสำคัญสำหรับความปลอดภัยของเว็บไซต์ หากคุณต้องการเซิร์ฟเวอร์ที่มีความยืดหยุ่น อำนาจ และสามารถควบคุมการตั้งค่า Nginx ได้เต็มที่ Cloud VPS ของ DE เป็นตัวเลือกที่เหมาะสม คุณสามารถปรับแต่งการตั้งค่า Nginx ตามความต้องการของคุณโดยไม่มีข้อจำกัด

นอกจากนี้ หากต้องการโฮสต์เว็บไซต์ด้วยความสะดวกสบาย โดยไม่ต้องกังวลเรื่องการจัดการเซิร์ฟเวอร์ Cloud Hosting ของ DE ก็เป็นอีกทางเลือกที่ดี ด้วยการสนับสนุนจาก Plesk ซึ่งช่วยให้คุณสามารถจัดการ Security Headers ได้อย่างง่ายดายผ่านอินเทอร์เฟส Graphical User Interface

เลือกบริการ DE และเพิ่มชั้นปกป้องความปลอดภัยให้กับเว็บไซต์ของคุณวันนี้