HTTP Header แบบ Security ฉบับพกพา #3

Datafarm
3 min readNov 18, 2020

--

สวัสดีครับ พบกับแอดมินเองคนเดิมนะครับ

เท่าที่เห็นมาสัปดาห์นี้หลาย ๆ คนก็คงยังวุ่นเหมือนเดิม ต้องรีบปิดงานก่อนจะหยุดยาวหรือก่อนจะทำ change freeze สิ้นปีนี้ คงจะไม่ดีแน่ ๆ ถ้ามี ad-hoc วิ่งเข้ามาใช่มะ ไม่มีทางหนีพ้นหรอกครับ คุณต้องอยู่กับมันอีกนาน แอดมินเองก็โดน แน่นอนว่าบทความรอบนี้ก็เป็น ad-hoc เช่นกัน T_T

จากบทความสัปดาห์ที่แล้วว่าด้วยเรื่อง Rendering Performance 101 ที่แอดมินเองอ้างอิงจาก developers.google.com เป็นหลัก แอดมินเองก็พยายามไล่อ่าน guideline ให้ครบ และพบว่ามีส่วนนึงพูดถึง Security ด้วย หนึ่งในนั้นก็คือ Content Security Policy ซึ่งนี่จะเป็นเรื่องที่เราจะมาทำความรู้จักกันในบทความนี้

Content Secuirt Policy (CSP) in a nutshell

เป็น HTTP response header ตัวนึง ที่ถูกกำหนดด้วยเหตุผลในเรื่องของ security ของไฟล์ ในที่นี้ส่วนมากหมายถึงไฟล์สามารถถูก execute บน browser ได้ มักจะรู้กันในนามว่า web page นั่นเอง

หน้าที่หลักของ CSP คือ อนุญาติ / ห้ามไม่ให้ โหลดไฟล์หรือ execute ไฟล์ resource ต่างๆ ได้แก่ .js .css .json ฯลฯ มายังที่ browser เพื่อที่จะทำการในกระบวนการต่าง ๆต่อไป

การกำหนด policy นี้ไม่จำเป็นต้องกำหนดผ่าน response header เสมอไป ยังสามารถทำผ่าน tag <meta> ได้ด้วยนะ และที่สำคัญ policy นี้รองรับทั้ง modern browser และ browser ตัวเก่าๆหลายตัวด้วย ภายใต้ CSP level ซึ่งในบทความนี้ไม่ขอกล่าวถึงส่วนนี้

ทำไมต้องมี CSP

การที่ไม่กำหนด CSP ใดๆเลย จะทำให้เว็ปไซต์ของเรามีโอกาสได้รับไฟล์ไม่พึงประสงค์หรือ malicious script มาปรากฏและ execute บน browser ได้ และผู้ที่ได้รับผลกระทบมากที่สุดคือผู้ใช้นั้นเอง เพื่อที่จะจัดการปัญหาเหล่านั้น เราจำเป็นต้องกำหนด CSP ให้ถูกต้องตาม use case ที่กำหนด

CSP directives เบื้องต้น

อย่างที่กล่าวไป CSP เป็น header ตัวนึง เราต้องกำหนด value เพื่อเป็นการบังคับการใช้งานให้มันด้วย ในที่นี้เราจะเรียกมันว่า directive ตัวอย่างเช่น

default-src ‘self’

อธิบายโครงสร้าง

default-src คือ ชื่อ directive

‘self’ คือ source list constant สามารถใส่เป็น domain ได้ แต่สำหรับ constant เช่น ‘self’ , wildcard (*) ต้องใช้กับ directive ที่ลงท้ายด้วย -src เท่านั้น ซึ่งใน 1 directive สามารถมีได้หลายตัว คั่นด้วย space bar ตัวอย่างเช่น

script-src https://host1.com https://host2.com

และใน policy value จะสามารถมีได้หลาย directive โดยคั่นด้วย semi-colon ( ; ) ตัวอย่างเช่น

Content-Security-Policy: default-src https://cdn.example.net; child-src ‘none’

ตัวอย่าง directive ที่มักถูกใช้บ่อยๆ

  1. default-src ใช้กำหนด policy สำหรับการดึง resource ทุกอย่างใน source list ที่กำหนด เช่น js, css, font, รูปภาพ, frame หรือ media ต่างๆ อย่างเช่น
default-src ‘self’ cdn.example.com;

อธิบายไซต์ของเราสามารถโหลด resource ต่างๆ ภายใต้ same origin (ในที่นี้คือ self ) และจาก cdn.example.com ได้

2) script-src ใช้กำหนด policy สำหรับการดึงไฟล์ js เป็นหลัก ตัวอย่างเช่น

script-src ‘self’ js.example.com;

3) style-src ใช้กำหนด policy สำหรับการดึงไฟล์ css เป็นหลัก ตัวอย่างเช่น

style-src ‘self’ css.example.com;

4) img-src ใช้กำหนด policy สำหรับการดึงไฟล์รูปเป็นหลัก ตัวอย่างเช่น

img-src ‘self’ img.example.com;

ส่วน directive ตัวอื่น สามารถดูได้ที่นี่

https://content-security-policy.com/#directive

ตัวอย่าง source list constant

1) Wildcard (*) หมายถึง source สามารถเป็นที่ไหนก็ได้

ตัวอย่างสำหรับ Policy ที่สามารถโหลดไฟล์รูปภาพจากที่ไหนก็ได้

img-src *

2) ‘none’ หมายถึงป้องกันการโหลด resources จากทุกที่

ตัวอย่างสำหรับ Policy ที่ไม่อนุญาติให้โหลด image บนเว็ปไซต์

img-src ‘none’

3) ‘self’ หมายถึง source ต้องเป็น same-origin ทั้ง scheme, host และ port

ตัวอย่างสำหรับ Policy ที่อนุญาติให้โหลดไฟล์ .js ที่ same-origin เท่านั้น

script-src ‘self’

4) https หมายถึง source ต้องเป็น https เท่านั้น

ตัวอย่างสำหรับ Policy ที่อนุญาติให้โหลดไฟล์ image จากที่ไหนก็ได้ที่เป็น https

img-src https

5) ‘nonce-’ หมายถึง source จะถูกโหลดและ execute ได้ก็ต่อเมื่อ directive tag มีค่า attribute nonce ตรงกับค่าที่ Content-Security-Policy header หรือ meta tag กำหนดไว้ (ใช้ได้เฉพาะ CSP level 2 เท่านั้น)

ตัวอย่างสำหรับ Policy ที่อนุญาตให้ inline-script tag ที่มีค่า nonce เป็น oZImzO2wm0k86ckz5ojz สามารถทำงานได้

Content-Security-Policy: script-src ‘nonce- oZImzO2wm0k86ckz5ojz’<script nonce=”oZImzO2wm0k86ckz5ojz”>//</script>

และเนื่องจาก source list constant มีหลายตัว บทความนี้ไม่สามารถกล่าวได้ทั้งหมด คนอ่านสามารถดูได้ที่นี่

https://content-security-policy.com/#source_list

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

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

เมื่อแอดมินเองเจอตารางนัดหมายงาน จึงเปิดเข้าไปดูรายละเอียด และโดน inline-script reflect ไปเต็มๆ

วิธีแก้ไข

จากกรณีนี้ วิธีแก้ไขคือเพิ่ม Policy ไม่ให้โหลด inline-script tag อย่าง nonce ลงไปที่ CSP header ให้เรียบร้อยซะ

กรณีเพิ่ม Header จากฝั่ง Server

Header set Content-Security-Policy “script-src ‘nonce-fR0m12@ndoMfunct1on’;”

กรณีเพิ่ม meta tag จากฝั่ง HTML (กำหนดให้ถูกตำแหน่งนะ ถ้าผิดตำแหน่ง Policy จะไม่ถูกบังคับใช้)

<meta http-equiv=”Content-Security-Policy” content=”script-src ‘nonce-oZImzO2wm0k86ckz5ojz’”>

และก็อย่าลืม คื น ตั ง ด้ ว ย

ผลลัพธ์หลังจากการเพิ่ม nonce

สรุป

การที่จะโหลด resource ใดๆบนไซต์เรา เราจำเป็นต้องกำหนด Policy ให้ชัดเจนว่าสิ่งที่ควรนำมาโชว์ให้ผู้ใช้เห็นต้องเป็นสิ่งที่มาจาก valid source จริงๆ ถึงแม้ว่าการจัดการ CSP ที่ดีจะทำให้เหตุการณ์ผิดปกติบางอย่างไม่เกิดขึ้น แต่เอาจริงๆมันไม่ได้ช่วยป้องกัน XSS ได้เท่าที่ควรเพราะสองเรื่องนี้คาบเกี่ยวกันในบางจุดเท่านั้น การป้องกันต้องทำจากหลายจุดทั้งการออกแบบ flow, การทำ validation, การทำเทสให้มาก รวมไปถึงการใช้ framework ให้ถูกต้อง เพราะ framework ถูกออกแบบมาให้หลีกเลี่ยงความเสี่ยงในการใช้ชุดคำสั่งอันตราย (เช่น eval) มาให้ระดับนึง ซึ่งก็ถือว่าดีกว่าไม่ได้ใช้ล่ะนะ…

สัปดาห์นี้แอดมินเองขอตัวก่อน หากมีอะไรผิดพลาดก็ขออภัยมา ณ ที่นี้ด้วยครับ

สวัสดีครับ

--

--