วิธีตั้งค่า HSTS โดยไม่ทำให้ตัวเองถูกล็อกออก
แผนการปล่อยใช้งาน Strict-Transport-Security แบบเป็นขั้นตอนและย้อนกลับได้ ที่ช่วยเพิ่มความเป็นส่วนตัวโดยไม่ทำให้ใบรับรองที่ผิดพลาดเพียงใบเดียวกลายเป็นเหตุขัดข้องทั้งระบบ
สารบัญ
- HSTS นั้นเรียบง่าย จนกระทั่งมันไม่เรียบง่าย
- HSTS header ทำอะไรจริง ๆ
- สถานการณ์ล็อกเอาต์ที่ควรหลีกเลี่ยง
- 1. subdomain ที่ถูกลืมยังไม่พร้อมสำหรับ HTTPS
- 2. ใบรับรองหมดอายุ
- 3. เครื่องมือ staging หรือเครื่องมือภายในอยู่ภายใต้ production domain
- 4. preload ถูกมองเป็น checkbox ตามปกติ
- แผนการปล่อยใช้งานที่ปลอดภัย
- Step 1: ตรวจสอบทุก hostname ที่คุณควบคุม
- Step 2: แก้ HTTPS ให้เรียบร้อยก่อนเพิ่ม HSTS
- Step 3: เริ่มด้วย max-age ที่สั้นมาก
- Step 4: เพิ่มขึ้นทีละขั้น
- Step 5: เพิ่ม includeSubDomains เฉพาะหลังจาก audit จริงจังแล้ว
- Step 6: มอง preload เป็นโครงการแยกต่างหาก
- ตัวอย่างการตั้งค่า
- Nginx
- Apache
- CDN หรือ edge platform
- วิธีถอน HSTS อย่างปลอดภัย
- Checklist การทดสอบก่อน ship
- เหตุผลด้านความเป็นส่วนตัวของ HSTS
HSTS นั้นเรียบง่าย จนกระทั่งมันไม่เรียบง่าย
HTTP Strict Transport Security ซึ่งมักย่อว่า HSTS บอกเบราว์เซอร์ว่า “สำหรับไซต์นี้ ให้ใช้ HTTPS เสมอ” เมื่อเบราว์เซอร์ได้รับ header นี้ผ่านการเชื่อมต่อ HTTPS ที่ถูกต้องแล้ว เบราว์เซอร์จะจดจำกฎนี้ตามระยะเวลาที่คุณกำหนด
สิ่งนี้มีประโยชน์ เพราะช่วยป้องกันการโจมตีแบบลดระดับโปรโตคอล ลดคำขอที่ไม่ปลอดภัยโดยไม่ตั้งใจ และหลีกเลี่ยงช่วงเวลาน่าอึดอัดที่ผู้ใช้พิมพ์ example.com แล้วแตะ plain HTTP ชั่วครู่ก่อนถูก redirect
แต่มันก็ติดแน่นเช่นกัน หากคุณเผยแพร่นโยบาย HSTS ที่ผิด เบราว์เซอร์อาจยังบังคับใช้นโยบายนั้นต่อไปนานหลังจากคุณเอา header ออกจากเซิร์ฟเวอร์แล้ว นี่คือวิธีที่ทีมต่าง ๆ ทำให้ตัวเองถูกล็อกออก ไม่ได้ล็อกจากแผงผู้ดูแลของตัวเองโดยตรง แต่ล็อกจากเบราว์เซอร์ของผู้ใช้, subdomain, ระบบ staging, endpoint เก่า และบริการที่ถูกลืมซึ่งยังไม่พร้อมสำหรับการบังคับใช้ HTTPS
เป้าหมายไม่ใช่การหลีกเลี่ยง HSTS เป้าหมายคือการ deploy มันเหมือนการ migration ไม่ใช่เหมือนสวิตช์เปิดปิด
HSTS header ทำอะไรจริง ๆ
HSTS header โดยทั่วไปหน้าตาเป็นแบบนี้:
Strict-Transport-Security: max-age=31536000; includeSubDomains
มันมีส่วนสำคัญสามส่วน:
max-age: ระยะเวลาเป็นวินาทีที่เบราว์เซอร์ควรบังคับใช้ HTTPS สำหรับ host นี้includeSubDomains: กฎนี้จะมีผลกับทุก subdomain ด้วยหรือไม่preload: สัญญาณว่าคุณต้องการให้ domain ถูกรวมอยู่ใน preload list ของเบราว์เซอร์
เบราว์เซอร์จะเชื่อถือ header นี้เฉพาะเมื่อได้รับผ่าน HTTPS ที่ถูกต้องเท่านั้น หากใบรับรองไม่ถูกต้อง หมดอายุ หรือไม่ตรงกัน เบราว์เซอร์ไม่ควรรับนโยบาย HSTS ใหม่จาก response นั้น
เมื่อนโยบายถูกบันทึกไว้แล้ว ความพยายามในอนาคตที่จะเข้า http://example.com จะถูกเบราว์เซอร์อัปเกรดเป็น https://example.com ก่อนส่งคำขอออกไป นี่คือประโยชน์ด้านความเป็นส่วนตัว: คำขอที่ไม่ปลอดภัยจะไม่ออกจากอุปกรณ์เลย
สถานการณ์ล็อกเอาต์ที่ควรหลีกเลี่ยง
ความล้มเหลวของ HSTS ส่วนใหญ่ไม่ได้เกิดจากเว็บไซต์หลัก แต่มักเกิดที่ขอบระบบ
1. subdomain ที่ถูกลืมยังไม่พร้อมสำหรับ HTTPS
includeSubDomains ฟังดูเป็นระเบียบ แต่มีผลแบบเด็ดขาด หากคุณตั้งไว้บน example.com มันจะมีผลกับ:
www.example.comapi.example.comold-crm.example.comprinter-setup.example.comstaging.example.com- สิ่งอื่นใดก็ตามที่อยู่ภายใต้ domain นั้น
หาก host ใดในกลุ่มนี้ไม่สามารถให้บริการ HTTPS ที่ถูกต้องได้ ผู้ใช้ที่มีนโยบาย HSTS cache ไว้จะไม่สามารถเข้าถึง host เหล่านั้นผ่าน HTTP ได้
2. ใบรับรองหมดอายุ
เมื่อไม่มี HSTS บางครั้งผู้ใช้ก็คลิกผ่านคำเตือนใบรับรอง แม้นั่นจะไม่ใช่แนวปฏิบัติด้านความปลอดภัยที่ดี แต่ก็เกิดขึ้นจริง
เมื่อมี HSTS เบราว์เซอร์สมัยใหม่จะไม่อนุญาตให้ข้ามข้อผิดพลาดของใบรับรองสำหรับ host นั้นได้ง่าย ๆ นี่คือประเด็นสำคัญ และยังหมายความว่าการต่ออายุใบรับรองต้องเป็นเรื่องธรรมดา มีการ monitor และผ่านการทดสอบ
3. เครื่องมือ staging หรือเครื่องมือภายในอยู่ภายใต้ production domain
การวางเครื่องมือภายในไว้ใต้ *.example.com อาจกลายเป็นเรื่องเจ็บปวดเมื่อ parent domain ใช้ includeSubDomains หากเครื่องมือเหล่านั้นใช้ใบรับรอง self-signed, private certificate authorities, การตั้งค่า TLS เก่า หรือไม่มี HTTPS เลย HSTS จะทำให้ทางลัดนั้นถูกเปิดเผย
นี่เป็นเหตุผลหนึ่งที่หลายทีมเก็บระบบภายในและระบบทดลองไว้ภายใต้ domain แยกต่างหากซึ่งมีนโยบายความปลอดภัยของตัวเอง
4. preload ถูกมองเป็น checkbox ตามปกติ
HSTS preload ไม่ใช่เพียง directive อีกตัวหนึ่ง แต่มันหมายความว่า domain ของคุณสามารถถูกบรรจุอยู่ในเบราว์เซอร์ในฐานะ HTTPS-only ก่อนที่ผู้ใช้คนใดจะเคยเข้าชมไซต์ของคุณ
สิ่งนี้ปิดช่องว่างของ “การเข้าชมครั้งแรก” แต่ย้อนกลับได้ยากกว่ามาก การนำออกจาก preload list อาจใช้เวลาหลายสัปดาห์หรือหลายเดือนกว่าจะถึงผู้ใช้ ขึ้นอยู่กับรอบการปล่อยเวอร์ชันของเบราว์เซอร์ preload เหมาะกับ domain ที่เสถียรและเติบโตเต็มที่ ไม่เหมาะกับไซต์ที่ยังอยู่ระหว่างค้นหาว่ามี subdomain อะไรบ้าง
แผนการปล่อยใช้งานที่ปลอดภัย
Step 1: ตรวจสอบทุก hostname ที่คุณควบคุม
ก่อนตั้ง includeSubDomains ให้ทำรายการ hostname ทั้งหมดภายใต้ domain นั้น DNS records เป็นจุดเริ่มต้น แต่ไม่ใช่ทั้งหมด ตรวจสอบการตั้งค่า CDN, hosting dashboards, hostname ที่เกี่ยวข้องกับอีเมล, เครื่องมือการตลาดเก่า, storage buckets และเอกสารภายใน
สำหรับแต่ละ hostname ให้ตอบคำถาม:
- ให้บริการ HTTP, HTTPS หรือทั้งสองอย่าง?
- ใบรับรอง HTTPS ถูกต้องและต่ออายุอัตโนมัติหรือไม่?
- redirect จาก HTTP ไป HTTPS ได้เรียบร้อยหรือไม่?
- ตั้งใจให้เป็น public หรือไม่?
- ยังจำเป็นอยู่หรือไม่?
หากทีมของคุณมีนิสัย debug production header อยู่แล้ว งานนี้จะเข้ากันได้ดีกับการตรวจ redirect และ header เราเคยอธิบาย workflow นั้นไว้ใน ชุดเครื่องมือขนาดเล็กสำหรับ debug redirects และ HTTP headers ใน production
Step 2: แก้ HTTPS ให้เรียบร้อยก่อนเพิ่ม HSTS
HSTS ไม่ได้ทำให้การตั้งค่า HTTPS ที่พังกลายเป็นสิ่งปลอดภัย มันเพียงทำให้ HTTPS เป็นข้อบังคับ
ก่อนเปิดใช้งาน ให้ตรวจสอบว่า:
- TLS certificates ครอบคลุม hostname ที่ถูกต้อง
- Certificates ต่ออายุอัตโนมัติ
- HTTP redirect ไป HTTPS ด้วย hop เดียวที่สะอาดเมื่อเป็นไปได้
- Canonical host redirects สอดคล้องกัน เช่น จาก non-
wwwไปwwwหรือในทางกลับกัน - Application assets ไม่พึ่งพา URL แบบ
http://ที่ไม่ปลอดภัย
Mixed content พบได้น้อยกว่าในอดีต แต่ยังคงปรากฏใน CMS themes เก่า, analytics snippets, embedded media และ image paths ที่ hard-code ไว้
Step 3: เริ่มด้วย max-age ที่สั้นมาก
อย่าเริ่มด้วยหนึ่งปี ให้เริ่มด้วยห้านาที:
Strict-Transport-Security: max-age=300
Deploy เฉพาะบน hostname ที่คุณกำลังทดสอบ โดยปกติคือเว็บไซต์ production แบบ canonical ยังไม่ต้องใส่ includeSubDomains
จากนั้นทดสอบในเบราว์เซอร์จริงและด้วยคำสั่ง command-line:
curl -I https://example.com
คุณควรเห็น Strict-Transport-Security header เพียงรายการเดียวพอดี HSTS headers ที่ซ้ำกันจาก app server และ CDN เป็นแหล่งความสับสนที่พบได้บ่อย โดยทั่วไปเบราว์เซอร์จะใช้นโยบายที่มีผลจริง แต่คนที่กำลัง debug incident ไม่จำเป็นต้องเจอกับความคลุมเครือ
Step 4: เพิ่มขึ้นทีละขั้น
หากไม่มีอะไรพัง ให้เพิ่มระยะเวลาเป็นขั้น ๆ:
Strict-Transport-Security: max-age=86400
จากนั้น:
Strict-Transport-Security: max-age=604800
แล้วอาจเป็น:
Strict-Transport-Security: max-age=2592000
ตารางเวลาที่ใช้งานได้จริงคือ:
- 5 นาที
- 1 วัน
- 1 สัปดาห์
- 1 เดือน
- 6 เดือน หรือ 1 ปี
ไม่มีรางวัลสำหรับความรีบร้อน จุดประสงค์ทั้งหมดของ staged rollout คือการให้ monitoring, support inbox และ edge cases มีเวลาบอกคุณว่าสิ่งใดที่ checklist ของคุณพลาดไป
Step 5: เพิ่ม includeSubDomains เฉพาะหลังจาก audit จริงจังแล้ว
เมื่อ public subdomain ทุกตัวพร้อมสำหรับ HTTPS แล้ว คุณจึงพิจารณาได้ว่า:
Strict-Transport-Security: max-age=31536000; includeSubDomains
นี่คือช่วงเวลาที่ควรระมัดระวัง หาก legacy service หนึ่งยังต้องใช้ HTTP อย่าเพิ่ม includeSubDomains ลงใน parent domain ให้ migrate service นั้น ย้ายไป domain อื่น หรือยอมรับว่านโยบาย HSTS ของคุณต้องแคบกว่านี้ไปก่อน
Security headers ควรสะท้อนความเป็นจริง ไม่ควรถูกใช้เป็นโปสเตอร์สร้างแรงบันดาลใจสำหรับ infrastructure ที่คุณหวังว่าจะมีในภายหลัง
Step 6: มอง preload เป็นโครงการแยกต่างหาก
พิจารณา preload เฉพาะเมื่อทุกข้อต่อไปนี้เป็นจริง:
- domain และ subdomain ทั้งหมดรองรับ HTTPS ที่ถูกต้อง
- HTTP redirect ไป HTTPS
- HSTS header ใช้
max-ageอย่างน้อย 31536000 วินาที - header มี
includeSubDomains - header มี
preload - คุณมั่นใจว่าจะไม่ต้องใช้ plain HTTP ที่ใดก็ตามภายใต้ domain นี้
header ที่พร้อมสำหรับ preload หน้าตาเป็นแบบนี้:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
การ submit เข้าสู่ preload list คือพันธะระยะยาว หากไซต์เป็น campaign microsite, product domain ชั่วคราว หรือ domain ที่ขอบเขตความเป็นเจ้าของยังไม่ชัดเจน ให้ข้ามไป
ตัวอย่างการตั้งค่า
Nginx
ใช้ always เพื่อให้ส่ง header ใน error responses ด้วย:
add_header Strict-Transport-Security "max-age=300" always;
หลังจาก rollout เสถียรแล้ว:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Apache
เมื่อเปิดใช้ mod_headers แล้ว:
Header always set Strict-Transport-Security "max-age=300"
ต่อมา:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
CDN หรือ edge platform
หาก CDN ของคุณตั้ง response headers ให้จัดการ HSTS ในที่เดียวจะดีกว่า อย่าตั้งนโยบายหนึ่งที่ origin และอีกนโยบายหนึ่งที่ edge เว้นแต่คุณมีเหตุผลที่ชัดเจนมาก
ตรวจสอบด้วยว่า CDN ใช้ headers กับ redirects, cached errors และ custom error pages หรือไม่ เว็บไซต์ production ไม่ได้มีแค่ response 200 OK เท่านั้น
วิธีถอน HSTS อย่างปลอดภัย
หากคุณต้องปิดใช้ HSTS ให้ส่ง:
Strict-Transport-Security: max-age=0
แต่มีเงื่อนไขสำคัญ: เบราว์เซอร์ต้องเข้าถึงไซต์ผ่าน HTTPS ที่ถูกต้องได้สำเร็จเพื่อรับ header นั้น หาก HTTPS เองพัง ผู้ใช้ที่มีนโยบาย HSTS cache ไว้จะไม่สามารถดึงคำสั่งที่จะล้างนโยบายได้
ดังนั้นลำดับการกู้คืนโดยทั่วไปคือ:
- กู้คืน HTTPS ที่ถูกต้อง
- เสิร์ฟ
Strict-Transport-Security: max-age=0 - คงไว้ให้นานพอให้ผู้ใช้ที่กลับมาได้รับ header นี้
- ลบหรือแทนที่ header หลังจาก incident ได้รับการแก้ไขแล้ว
หาก domain อยู่ใน preload แล้ว การเสิร์ฟ max-age=0 ไม่เพียงพอสำหรับ browser profiles ใหม่ คุณต้องขอให้นำออกจาก preload list และรอให้การเปลี่ยนแปลงนั้นถูกส่งผ่าน browser updates ด้วย
Checklist การทดสอบก่อน ship
ใช้ checklist นี้ก่อนเพิ่ม max-age หรือเพิ่ม includeSubDomains:
- Canonical HTTPS URL ส่งคืนใบรับรองที่ถูกต้อง
- HTTP redirect ไป HTTPS
- มี HSTS header เพียงรายการเดียว
- header ปรากฏบน redirects และ error responses เมื่อเหมาะสม
- public subdomain ทั้งหมดมี HTTPS ที่ถูกต้อง
- มีการ monitor การต่ออายุใบรับรอง
- ไม่มีระบบภายในที่สำคัญพึ่งพา HTTP ภายใต้ parent domain เดียวกัน
- มีการหารือเรื่อง preload อย่างชัดเจน ไม่ใช่เพิ่มตามความเคยชิน
Lighthouse อาจเตือนเรื่อง security headers ที่ขาดหายหรืออ่อนแอในบางบริบทได้เช่นกัน แต่ไม่ควรเป็นวิธีตรวจสอบเพียงอย่างเดียวของคุณ หากคุณใช้มันเป็นส่วนหนึ่งของการ review ที่กว้างกว่า ให้อ่านผลลัพธ์เป็นสัญญาณมากกว่าคำตัดสิน แนวคิดเดียวกันนี้ใช้ได้เมื่อคุณ อ่าน Lighthouse report โดยไม่ตื่นตระหนก
<!-- tool-cta:start -->
💡 ลองทำสิ่งนี้: ก่อนและหลังการเปลี่ยนแปลง HSTS แต่ละครั้ง ให้ตรวจสอบ Strict-Transport-Security response ด้วย Get Headers เพื่อยืนยันว่า max-age, includeSubDomains และ preload เป็นไปตามที่คุณคาดไว้
<!-- tool-cta:end -->
เหตุผลด้านความเป็นส่วนตัวของ HSTS
HSTS มักถูกมองว่าเป็น security header และมันก็เป็นเช่นนั้นจริง นอกจากนี้ยังมีประโยชน์ด้านความเป็นส่วนตัว: มันลดโอกาสที่คำขอแรกของผู้ใช้จะรั่วผ่าน plain HTTP บนเครือข่ายที่ไม่น่าเชื่อถือ
เรื่องนี้สำคัญบน Wi-Fi สนามบิน, เครือข่ายโรงแรม, เครือข่าย guest ขององค์กร และทุกที่ที่ทราฟฟิกของผู้ใช้อาจถูกสังเกตหรือแก้ไขได้ คำขอ plain HTTP อาจเปิดเผย hostname, path, cookies ที่ไม่มี Secure flag และรายละเอียดคำขออื่น ๆ HTTPS ไม่ใช่เวทมนตร์ แต่การบังคับใช้อย่างสม่ำเสมอจะตัดการรั่วไหลทั้งประเภทที่หลีกเลี่ยงได้ออกไป
การ deploy HSTS ที่ดีที่สุดคือแบบไม่น่าสนใจ มันถูกปล่อยใช้งานอย่างช้า ๆ มีใบรับรองที่เชื่อถือได้รองรับ และน่าเบื่อพอที่ไม่มีใครสังเกตเห็น นั่นคือสิ่งที่คุณต้องการจาก header ที่โหมดความล้มเหลวอาจรุนแรงได้