OWASP Top10–2021


OWASP Top 10 ของปี 2021 เป็นการรวมรวบและจัดอันดับปัญหาความปลอดภัยของ Web Application ที่พบ ซึ่งแต่ละอันดับทาง OWASP จะมีทั้ง root cause และ symptom ของปัญหา โดยหนึ่งอันดับจะประกอบด้วยหลายๆ CWE (Common Weakness Enumeration) ดังนั้นแต่ละอันดับจะมีรายละเอียดเยอะมาก
เอกสาร OWASP Top 10 ปกติจะเป็นการอธิบายภาพรวม ตัวอย่าง และวิธีป้องกันแบบสั้นๆ และจะมีลิงค์เพื่อให้ดูรายละเอียดเพิ่มเติมได้ โดยในบทความนี้ ผมพยายามจะอธิบายให้ละเอียดให้ โดยหวังว่าผู้อ่านสามารถนำเอาไปใช้งานได้ โดยรายละเอียดผมเอามาหลายๆ ที่ รวมทั้งประสบการณ์ของผมเอง ก็หวังว่าผู้อ่านจะชอบแนวนี้ แต่เนื่องด้วยรายละเอียดแต่ละอันดับนั้นเยอะมาก ผมจะอธิบายเฉพาะที่ผมคิดว่าสำคัญในแต่ละอันดับ
เรื่องหนึ่งที่ผมไม่ค่อยเห็นคนพูดถึงกันคือ วัตถุประสงค์หลักของ OWASP Top 10 ซึ่งทาง OWASP เองบอกว่าเป็นเอกสารสำหรับสร้าง awareness และถ้าต้องการนำมาใช้เป็นมาตรฐานความปลอดภัยของ Application ก็ให้คิดว่าเป็นเพียงจุดเริ่มต้นสำหรับพัฒนา และทดสอบ Application เรื่องความปลอดภัย
โดยทาง OWASP เองแนะนำให้นำ OWASP Application Security Verification Standard (ASVS) มาปรับใช้เพื่อเป็นมาตรฐานความปลอดภัยของ Application ที่ออกแบบมาให้มีการยืนยัน (verifiable) และทดสอบระบบความปลอดภัยใน Application ได้ และยังนำไปใช้ใน Secure Development Lifecycle (SDL) ได้ด้วย
A01 Broken Access Control
Access control คือระบบการควบคุมการเข้าถึงทรัพยากรของระบบ เช่น ควบคุมการเข้าถึง server ควบคุมการเข้าถึงข้อมูล หรือควบคุมการเข้าถึงการใช้งาน feature เป็นต้น โดยความหมายของปัญหานี้คือ อะไรก็ได้ที่ทำให้ attacker เข้าถึงทรัพยากรที่เราไม่ต้องการ และการเข้าถึงนี้หมายถึงทั้งการอ่าน การเขียน การแก้ไข และอื่นๆ
สาเหตุของปัญหานี้ อาจจะมาจากการ design ที่ไม่ดีพอ หรืออาจมาจาก developer ที่ทำผิดพลาดหรือลืม หรืออาจจะเป็นการที่ผู้ดูแลระบบตั้งค่าผิดพลาด (misconfiguration) โดยทาง OWASP จะเน้นไปที่ application ที่เราพัฒนาขึ้นมา
ตัวอย่างของ Broken Access Control
ไม่มีการตรวจสอบการใช้งานก่อนการเข้าใช้งานในหน้าต่างๆ
ปัญหานี้อาจเกิดจากลืมตรวจสอบ หรือเป็นหน้าเว็บที่ซ่อนเอาไว้ไม่มี link ไปหา (security through obscurity) เช่นหน้าของผู้ดูแลระบบ แล้วคาดหวังว่า attacker จะไม่รู้ว่ามี URL นี้ ซึ่ง attacker อาจจะวิธีคาดเดา หรือ brute force ว่ามีหน้าอื่นซ่อนอยู่หรือไม่ และเมื่อ attacker หาเจอ URL ของผู้ดูแลระบบ ก็จะสามารถพิมพ์ URL เองเพื่อเข้ามาในหน้าของผู้ดูและระบบ (force browsing)
ส่งสิทธิ์หรือชื่อผู้ใช้งานไว้ใน HTTP parameter หรือ HTTP header
หลายคนน่าจะทราบกันอยู่แล้วว่า ข้อมูลที่อยู่ใน HTTP request ทั้งหมดเป็น untrusted input เพราะว่า attacker สามารถเปลี่ยนหรือกำหนดค่าอะไรเพิ่มก็ได้ แม้ว่า request จะถูกสร้างจาก application ที่ผู้ใช้งานทั่วไปไม่สามารถแก้ไขหรือเห็นข้อมูลพวกนี้ได้ เมื่อ server นำค่าที่ส่งมากำหนดสิทธิ์ในการเข้าถึงข้อมูล จะทำให้ผู้โจมตีสามารถแก้ไขสิทธิ์หรือผู้ใช้งานเป็นค่าไหนก็ได้
สาเหตุที่ผมพอนึกออกคือ
- developer ต้องการส่งต่อสิทธิ์หรือชื่อผู้ใช้งานจากระบบหนึ่งที่มีการ authentication แล้วไปอีกระบบหนึ่ง แต่ใช้วิธีส่งผ่านสิทธิ์ด้วย HTTP parameter หรือ HTTP header โดยไม่มีการตรวจสอบ (verification) ซึ่งสาเหตุนี้ผมมองว่าเป็นปัญหาเรื่อง insecure design
- developer ต้องการเก็บ state ไว้ใน client ซึ่งเป็น pattern ที่เห็นใน stateless application แต่เก็บชื่อหรือสิทธิ์ผู้ใช้งานด้วย แล้วไม่มีการตรวจสอบก่อนนำไปใช้งาน
- ค่าพวกนี้ เป็นค่าที่ซ่อนไว้อยู่ ไม่มีใครรู้หรอก
Insecure Direct Object References (IDOR)
สมมติว่ามีหน้าสำหรับดูข้อมูลของตัวเองด้วย URL https://www.example.com/view/user?id=123456 และเมื่อ attacker เปลียนค่าของ id เป็น 123455 ปรากฎว่าแสดงข้อมูลของคนที่ user id เป็น 123455 ซึ่งช่องโหว่นี้จะรวมถึง URL ที่ใช้แก้ไขข้อมูลด้วย
ปัญหาเรื่องนี้ ส่วนมากเป็นเรื่อง Horizontal access controls คือการเข้าถึงข้อมูลของผู้ใช้งานอื่นที่มีสิทธิ์เท่ากัน โดยส่วนมากเวลา implement จะดูแค่ว่าผู้ใช้งานมีสิทธิ์สำหรับ function นี้หรือเปล่า แต่กลับลืมว่าข้อมูลส่วนตัวของผู้ใช้งานของแต่ละคนต้องมีเรื่องของ owner มาเกี่ยวข้อง
CORS misconfiguration
Web browser จะมีเรื่อง Same Origin Policy (SOP) เป็น default คือไม่อนุญาตให้หน้าเว็บที่เปิดอยู่เรียกข้าม Origin (ประกอบด้วย protocol, hostname และ port) ด้วย XMLHttpRequest หรือ Fetch API ได้ โดยช่องโหว่นี้จะพบมากขึ้นในปัจจุบัน เพราะว่ามีการใช้งาน Web API ที่ต้องการ request ข้าม Origin
การเปิดใช้งาน CORS จะเปิดที่ Web API เพื่อให้ browser ตรวจสอบจาก HTTP response ว่า request จาก Origin ของหน้าเว็บที่เปิดอยู่ Origin ของ Web API ได้หรือไม่ (ปกติจะไม่อนุญาตเพราะ SOP)
ข้อผิดพลาดหลักเรื่อง CORS ที่พบได้แก่
- เปิดใช้งาน CORS ทั้งที่จริงๆ ไม่ได้ใช้
- เปิดแบบ global ทำให้ทุกๆ endpoint มีการใช้ CORS ทั้งๆ ที่บางหน้าเท่านั้นที่ใช้
- Implement CORS policy ที่ใช้งานได้จากหลายๆ Origin ผิดพลาด
- เปิดแบบรับได้จากทุกที่ (ACAO: *) ใน internal networks
นอกจากเรื่องที่กล่าวมาในตัวอย่างแล้ว OWASP ได้รวมช่องโหว่ที่หลายคนอาจจะคาดไม่ถึงไว้ใน Broken Access Control อีกด้วย ได้แก่ Path traversal, Cross Site Request Forgery (CSRF), Directory listing และ Open redirect ซึ่งถ้าดูดีๆ ช่องโหว่พวกนี้ล้วนทำการให้เกิดการเข้าถึงทรัพยากรของระบบของเราโดยที่เราไม่ต้องการ
วิธีป้องกัน
Deny by Default
คือเริ่มจากไม่อนุญาตให้ใครเข้าถึงเลย แล้วค่อยๆ เพิ่มว่ามีใครสามารถเข้าใช้งานได้ ซึ่งก็จะมีข้อยกเว้นกับพวก public resources จริงๆ แล้วเรื่องนี้เป็น pattern หนึ่งที่ควรทำสำหรับ application security เลยคือ เริ่มจากปิดทุกอย่าง แล้วค่อยๆ เปิดตามที่เราใช้งาน
ทำระบบ access control หลักให้อยู่ที่เดียว
เรื่องนี้ผมไม่แนะนำให้ implement ระบบ access control เองตั้งแต่ต้น ผมแนะนำให้ใช้ library หรือที่อยู่ใน framework อยู่แล้ว ช่วยในการ implement access control ที่ต้องการ โดยปกติ library และ framework จะทำให้ระบบ access control หลักอยู่ที่เดียวอยู่แล้ว ซึ่งง่ายต่อการ review และแก้ไข access control ของระบบ
โดยตัวอย่าง code ที่ผมยกมาเป็นการใช้ Spring security ซึ่งมี Role Based Access Control (RBAC) พร้อมให้ใช้งานอยู่แล้ว ซึ่งเป็นการกำหนดแบบ global ว่าใครมีสิทธิ์เข้าถึง endpoint ไหน

สำหรับ RBAC ในแต่ละ role จะมีการกำหนด permission ว่าทำอะไรได้บ้าง ในการตรวจสอบการเข้าถึง ควรใช้ permission ในการตรวจสอบสิทธิ์ ซึ่งใน Spring framework จะเรียกว่า Authority
ต่อไปคือ access control ในส่วน endpoint ต่างๆ โปรแกรมปกติต้องมีการกำหนดการเข้าถึง function ต่างๆ ทีละเอียดกว่านี้ โดยเฉพาะ REST API ที่อาจจะแยก permission ตาม CRUD (Create, Read, Update, Delete) ตัว Spring ก็จะมีเรื่อง method security ไว้ให้ โดยในตัวอย่างต่อไป (ไม่เกี่ยวกับตัวอย่างก่อนหน้า) มีการ custom permission check เพื่อให้ตรวจสอบ data owner ด้วย เพราะว่า RBAC นั้นไม่สนับสนุนเรื่องการตรวจสอบค่าต่างๆ ของ object

สำหรับคนที่สงสัยว่าทำไมไม่ให้ใช้ role ในการตรวจสอบสิทธิ์ ลองดูตัวอย่างต่อไปนี้นะครับ ที่ไม่การตรวจสอบ data owner

จะเห็นว่าเวลาเขียนโปรแกรม ก็ต้องนึกว่ามีใครบ้างที่ใช้งานได้ และเวลาจะแก้ไขสิทธิ์ของแต่ละ role ก็ต้องมาตามแก้ทีละ method นอกจากนี้แล้วเวลาต้องการตรวจสอบว่า implement ถูกต้องหรือเปล่า ก็ต้องมาไล่ดูทีละ method ว่ามี role ไหนใช้งานได้บ้าง จะเห็นว่าวิธีนี้มีโอกาสที่จะ implement ผิดพลาดสูง ทำให้วิธีไม่แนะนำให้ใช้
หมายเหตุ: ที่ผมยก RBAC มาเป็นตัวอย่าง เพราะคิดว่ามันยังเหมาะสมกับหลายๆ application ซึ่งถ้าระบบ access control มีความซับซ้อนมากๆ ก็ควรพิจารณาใช้ Attribute Based Access Control (ABAC) โดยเลือกตั้งแต่ตอน design ระบบ
CORS
เรื่องนี้ขอเน้นก่อนเลยว่าถ้าไม่จำเป็นต้องใช้ CORS ไม่ต้องเปิดใช้งาน ปลอดภัยที่สุด ถ้าต้องใช้งานก็เปิดเท่าที่ใช้งาน เช่นอนุญาตให้ใช้ GET method จาก https://trusted.example.com เท่านั้น
ใช้ Rate limit
การทำ rate limit นั้นเป็นการ mitigation ปัญหาเรื่อง access control บางกรณี ที่ attacker ต้องมีการส่ง request มาเยอะๆ เช่น brute force หาหน้าที่ซ่อนไว้อยู่ หรืออาจจะมีบางหน้าที่ลืมตรวจสอบ data owner ทำให้มีช่องโหว่เรื่อง IDOR การที่ attacker ต้องการข้อมูลทั้งหมด ก็จำเป็นต้องไล่ request ทีละ id ซึ่งถ้ามี rate limit ก็จะทำให้ attacker ขโมยข้อมูลได้ช้าลง แล้วเราอาจจะตรวจสอบเจอก่อนที่ attacker จะขโมยข้อมูลได้ทั้งหมด ในกรณีที่มีการทำ logging and monitoring
Log access control events
เราควรจะทำการ log access control เมื่อพบว่ามีการพยายามเข้าถึงทรัพยากรที่ไม่อนุญาต และอาจจะมีการ alert หาผู้ดูแลระบบในกรณีที่ค่อนข้างแน่ใจว่าเป็นการโจมตี เช่น log เรื่องเดียวกันซ้ำๆ จาก source เดียวกัน นอกจากนี้ยังอาจจะ log access ที่ success ด้วยในส่วนที่ sensitive จริงๆ พร้อมกับมีการ alert ในกรณีที่เห็นสมควร
A02 Cryptographic Failures
หัวข้อนี้จะเกี่ยวข้องกับการเข้ารหัสข้อมูล ซึ่งมีความจำเป็นในการปกป้องข้อมูล เช่น password, หมายเลข credit card, หมายเลขบัตรประชาชน เป็นต้น
ตัวอย่างของ Cryptographic Failures
ใช้ plaintext protocol ในการรับส่งข้อมูล
เช่น HTTP, SMTP, FTP เป็นต้น ถ้ามีคนดักข้อมูลขณะที่ส่งด้วย protocol พวกนี้ คนดักฟังจะสามารถเห็นรายละเอียดข้อมูลของเราได้
ใช้ protocol หรือ algorithm ที่ไม่ปลอดภัยแล้ว
เช่น SSL 3.0, TLS 1.0, TLS 1.1, DES, 3DES, RC4, MD5, SHA1
ฝั่ง client ข้ามการตรวจสอบ server certificate ใน TLS protocol
ทำให้ attacker สามารถทำ Man In The Middle (MITM) เพื่อดักฟังข้อมูลได้
ใช้ random function ทั่วไปใน operation ที่เกี่ยวข้องกับความปลอดภัย
Random function ที่ไม่ใช่ cryptographically secure pseudorandom number generator (CSPRNG) อาจจะสุ่มค่าที่ผู้ attacker สามารถคาดเดาได้ ทำให้เดาผลลัพธ์ของการทำงานได้เช่น การสุ่ม password ชั่วคราวสำหรับผู้ใช้งาน
สร้าง encryption key ไม่ถูกต้อง
เช่น ต้องการสร้าง key ขนาด 128 bits สำหรับการเข้ารหัสแบบ AES ซึ่งในโปรแกรมคือต้องการ key ขนาด 16 bytes แต่ developer ใช้วิธีสุ่มจากตัวอักษร A-Za-z0–9 ซึ่งมีจำนวนน้อยกว่า 64 (26) ตัวอักษร ทำให้ key ที่ random นั้นมีขนาดน้อยกว่า 96 (6*16) bits
อีกตัวอย่างคือใช้ string ที่ hardcode เอาไว้หรือ string ที่เป็น password แล้วนำ string นั้นมา hash ด้วย MD5 แล้วนำมาเป็น key หรือใช้ PBKDF2 เพื่อเปลี่ยน string เป็น key
ใช้ encryption ผิดวิธี
- hardcode หรือไม่ secure random ค่า IV (Initialization Vector), nonce
- ใช้ IV หรือ nonce ซ้ำ
- เลือกใช้ block cipher mode เป็น ECB
- เลือกใช้ block cipher mode เป็น CBC และข้อมูลที่เข้ารหัสมีโอกาสถูกแก้ไขโดย attacker แต่ไม่มีการทำ MAC (Message Authentication Code) เพื่อป้องกันข้อมูลถูกแก้ไข
เก็บ password ไม่ดีพอ
hash ด้วยการใช้ cryptographic hash function เช่น MD5, SHA1, SHA-512
วิธีป้องกัน
เนื่องด้วยเรื่องการเข้ารหัสจะเกี่ยวข้องกับข้อมูลที่สำคัญหรือจำเป็น เราจึงจำเป็นแยกแยะข้อมูลที่ใช้ในระบบของเราว่ามีข้อมูลส่วนไหนจำเป็นต้องเข้ารหัสหรือไม่ ซึ่งต้องดูตามกฎหมายที่เกี่ยวข้องกับความเป็นส่วนตัว (privacy law) เช่น PDPA, EU’s GDPR เป็นต้น และระเบียบต่างๆ ที่เกี่ยวข้องกับ application ของเรา เช่น PCI DSS เป็นต้น ประกอบด้วย
ใช้ protocol หรือ algorithm ที่ยังปลอดภัยอยู่
เช่น TLS 1.2, TLS 1.3, AES, SHA-2, SHA-3, BLAKE2, BLAKE3, Chacha20-Poly1305
ใช้ protocol ที่มีการเข้ารหัสเท่านั้นเช่น HTTPS, FTPS, SSH, SMTPS
นอกจากเข้ารหัสแล้ว ปัจจุบัน protocol ส่วนมากจะ support เรื่อง forward secrecy อย่างใน TLS 1.2 ก็ควรเลือก cipher suite ที่มี ECDHE เท่านั้น เช่น TLS_ECDHE_RSA_WITH_AES128_GCM_SHA256 เป็นต้น ส่วน TLS 1.3 ไม่ต้องแก้ค่าอะไร เพราะทุก cipher suite เป็น forward secrecy
สำหรับ Web Application ก็ยังแนะนำให้ใช้ HTTP Strict Transport Security (HSTS) เพื่อบังคับให้ browser ต่อมาที่เว็บของเราด้วย HTTPS เท่านั้น
ไม่ cache response ที่มี sensitive data
เราสามารถควบคุมเรื่อง cache ใน browser โดยการกำหนด Cache-Control HTTP header ใน response ซึ่งค่าสำหรับป้องกัน response ถูก cache คือ “Cache-Control: no-cache, no-store”
ใช้ CSPRNG เพื่อสุ่มค่าใน operation ที่เกี่ยวข้องกับ security
ในหลายๆ ภาษาจะมีทั้ง random ปกติที่ไม่ secure และเป็น random ที่เป็น CSPRNG เพียงแค่เลือกใช้ให้ถูก เช่น Java มี java.security.SecureRandom, .NET มี RNGCryptoServiceProvider, PHP มี random_bytes() เป็นต้น และที่สำคัญคือบาง API มีตัวเลือกให้กำหนด seed ถ้ายังไม่เข้าใจจริงว่า seed ต้อง generate ยังไงให้ปลอดภัย ก็อย่ากำหนด seed เอง
อีกเรื่องที่อยากแนะนำ คือเมื่อต้องมี fixed key เพื่อใช้งานใน application เช่น HMAC key เพื่อ sign JWT ผมแนะนำให้เขียนโปรแกรมเรียก CSPRNG เพื่อ random key ขนาดอย่างน้อย 256 bits หรืออาจจะใช้คำสั่ง openssl rand เพื่อสุ่ม key โดย key ที่ออกมาสามารถเลือกได้ว่าจะ encode เป็น hex string หรือ base64 เพื่อความสะดวกในการ copy

ถ้าได้ key มาในรูปแบบ base64 หรือ hex string เวลาจะใช้ API ก็ต้อง decode เป็น byte array ก่อน เพราะ API ปกติจะรับเป็น byte array
เลือกใช้ algorithm ที่เป็น Authenticated Encryption (AE)
เช่น AES-GCM, Chacha20-Poly1305
AES-CBC ที่มีการใช้กันแพร่หลาย แต่ cipher mode นี้ป้องกันเฉพาะเรื่อง confidentiality ไม่ได้ป้องกันเรื่อง integrity และถ้าต้องการให้ข้อมูลปลอดภัยจากการถูกแก้ไข developer ก็ต้องเขียนโปรแกรมเพิ่มเอาเอง โดยปกติจะใช้ HMAC ของข้อมูลที่เข้ารหัสแล้ว (Encrypt-then-Mac) ซึ่งอาจจะทำพลาดได้ แต่ถ้าใช้ algorithm ที่เป็น Authenticated Encryption แล้ว developer ก็แค่ทำเหมือน encryption ขั้นตอนเดียว
เก็บ password ด้วย Argon2id, Scrypt หรือ Bcrypt
algorithm พวกนี้ ทำให้การ brute force หา password ทำได้ช้า ในกรณีที่ผู้โจมตีสามารถขโมย password hash ไปได้
ไม่เก็บ sensitive data ถ้าไม่จำเป็น
บาง sensitive data เราต้องการแค่มาประมวลผลสำหรับงานเดียวเท่านั้น ดังนั้นเราก็ไม่ควรเก็บข้อมูลนี้ รวมถึงไม่ควร log ด้วย บาง sensitive data มีวิธีอื่นๆ ช่วยอยู่แล้ว เช่น บัตรเครดิตสามารถใช้วิธี Tokenization เพื่อที่ไม่ต้องเก็บหมายเลขบัตรเครดิต และลด scope ของ PCI DSS เป็นต้น
ใช้ Secret management service
Secret management service คือระบบที่ใช้จัดการความลับ เช่น username และ password, encryption key, token เป็นต้น โดยปกติ service นี้จะเก็บความลับของทั้งระบบไว้ในที่เดียว ทำให้ง่ายต่อการ audit ความลับที่มีอยู่ในระบบ และมี access control เพื่อควบคุมการเข้าถึงความลับ แต่เนื่องด้วยความลับทั้งหมดอยู่ในที่เดียว ดังนั้นเวลาใช้งานควรทำตามคำแนะนำในเอกสารที่เขาแนะนำทั้งหมด
เข้ารหัส sensitive data
ถ้าระบบเราต้องเก็บ sensitive data จริงๆ วิธีการเข้ารหัสข้อมูลก็เป็นวิธีลดความเสี่ยงเมื่อ attacker สามารถขโมยข้อมูลออกไปได้ ซึ่งวิธีที่เห็นบ่อยๆ คือ hardcode key ไว้ใน application ซึ่งเรื่องนี้จะมีความเสี่ยงในกรณีที่ attacker สามารถอ่าน source code ที่มี encryption key แล้วนำไป decrypt ข้อมูลที่ขโมยไปทั้งหมด
เพื่อลดความเสี่ยงจากการ hardcode key เราอาจจะใช้ Secret Management Service เพื่อทำการ encrypt/decrypt ข้อมูลให้เรา (Encryption as a Service) ส่วนถ้าทำใน mobile ก็จะมี feature ให้ใช้อยู่แล้วคือ Apple Secure Enclave และ Android Hardware-backed Keystore
เรื่องสุดท้ายที่ให้ระวังคือ encryption feature ใน DBMS ที่สามารถ decrypt ข้อมูลให้เราอัตโนมัติตอนเราทำ query จะป้องกันได้แค่การเข้าถึงไฟล์ database โดยตรงเท่านั้น แต่ attacker ส่วนมากขโมยข้อมูลด้วยการ query เหมือนที่โปรแกรมทำงาน ดังนั้นข้อมูลที่ถูกขโมยก็ยังเป็น plaintext อยู่ดี ทำให้ feature นี้น่าจะไม่น่าเพียงพอสำหรับการลดผลกระทบจากการขโมยข้อมูล
A03 Injection
หัวข้อนี้รวมช่องโหว่ที่เกิดจากที่ input ของผู้ใช้งานสามารถเปลี่ยนความหมายของคำสั่งหรือข้อมูลได้ ช่องโหว่ที่น่ารู้จักกันดีในหัวข้อนี้คือ SQL injection กับ Cross Site Scripting (XSS) ซึ่งเป็นส่วนหนึ่งของ HTML injection ส่วนช่องโหว่อื่นๆ ที่รู้จักกันน้อยลงมาก็ได้แก่ OS Command injection, Server Side Template Injection (SSTI), CRLF injection, LDAP injection, XPath injection เป็นต้น
ตัวอย่างเรื่อง Injection
SQL injection
เป็นช่องโหว่ที่ input ของ attacker สามารถเปลี่ยนความหมายของ SQL command ได้ ซึ่งมีตัวอย่างที่มีช่องโหว่ตามรูป

ตัวอย่างแรกจะเห็นว่าถ้า attacker ส่งค่า id เป็น 1;SQL_cmd ผลลัพธ์คือโปรแกรมจะสั่ง SQL_cmd ที่มาจาก attacker เพิ่มอีกคำสั่ง ส่วนตัวอย่างที่สองต้องการให้เห็นว่า SQL injection สามารถเกิดขึ้นได้หลายจุด
Cross Site Scripting (XSS)
ช่องโหว่นี้เกิดขึ้นใน web browser ของผู้ใช้ โดยมองได้ว่า HTML text ที่ application ฝั่ง server สร้างขึ้นมานั้นเป็นคำสั่งบอกให้ web browser ต้องแสดงผลยังไงให้กับผู้ใช้ และใน HTML จะมี javascript ซึ่งเป็นส่วนที่สามารถควบคุมค่าต่างๆ ในหน้านั้นได้ทั้งหมด ทำให้ปกติ attacker สนใจที่จะแทรก javascript เข้าไปเป็นหลัก
ดูตัวอย่างน่าจะเข้าใจง่ายกว่า รูปข้างล่างนี้ จะเป็นตัวอย่าง code ที่มีช่องโหว่ XSS ในจุดต่างๆ โดยสมมติว่าตัวแปรที่อยู่ใน ${…} ทั้งหมดเป็นค่าที่รับมาจาก user

จากตัวอย่าง สมมติถ้า attacker ส่งค่า msg เป็น
ผลลัพธ์ในหน้านี้ก็มีจะมีฟอร์มมาเพิ่มอีกอันหนึ่ง หรือ attacker จะส่งค่า msg เป็น ผลลัพธ์ก็คือรัน javascript อะไรก็ได้วิธีป้องกัน
Validate Input
Input Validation นั้นเป็นสิ่งที่ควรทำอยู่แล้ว และต้องทำก่อนการใช้งาน input (ถ้าเป็นไปได้ ควร validate input ทั้งหมดเป็นอย่างแรกของการทำงาน) เพื่อให้มั่นใจว่าโปรแกรมของเรากำลังจัดการกับ input ที่เราต้องการเท่านั้น รวมทั้งไม่เก็บข้อมูลที่ผิดลงในฐานข้อมูลของเรา
สิ่งสำคัญของ input validation คือต้องทำที่ทำฝั่ง server การทำในฝั่ง client นั้น ทำเพื่อแค่ลดการติดต่อกับ server และอาจทำเพื่อแยกแยะผู้ใช้งานทั่วไปกับ attacker และที่ต้องระลึกไว้อีกอย่างคือ input ที่ถูก validate แล้ว ยังอาจจะอันตรายเมื่อนำมาใช้กับส่วนอื่นของระบบ
Input Validation สามารถแบ่งได้เป็น allow list กับ deny list โดยปกติโปรแกรมควรใช้วิธี allow list คืออนุญาตเฉพาะค่าที่กำหนดไว้ ในมุมมองของผม developer ควรรู้อยู่แล้วว่าโปรแกรมสามารถประมวลผล input ค่าอะไรบ้าง ก็เพียงเอาค่าทั้งหมดที่เป็นไปได้มาเป็น allow list
ลองดูตัวอย่างการ validate URL เพื่อที่จะได้เข้าใจมากขึ้น URL นั้นมี syntax ที่โดนกำหนดไว้อยู่แล้ว โดยปกติการ validate ข้อมูลทั่วไปเช่น URL, Email เป็นต้น ผมแนะนำให้ใช้ validator ที่มีอยู่แล้วใน framework ที่ใช้อยู่ หรือใช้ library ที่เป็นที่รู้จักมาใช้ แทนการเขียนด้วยตนเอง เพื่อหลีกเลี่ยงข้อผิดพลาดต่างๆ
เมื่อเราทำการ validate syntax ของ URL เรียบร้อยแล้ว สิ่งที่ควรทำต่อมาคือการ validate semantic ของ URL ว่าเป็นไปตามที่โปรแกรมเรากำหนดหรือไม่เช่น ต้องเป็น HTTPS เท่านั้น ต้องต่อไปที่ default port เท่านั้น เป็นต้น
ป้องกัน SQL injection
วิธีป้องกันที่หลายๆ ที่แนะนำคือ ใช้ Parameterized Query โดยจะมีชื่ออื่นที่รู้จักกันได้แต่ Prepared Statement, Binding Parameters ซึ่งจะใช้ได้เฉพาะกับส่วนที่เป็นข้อมูลใน table เท่านั้น ไม่สามารถใช้ได้กับชื่อ table, ชื่อ column ดังนั้นเวลาที่ต้องรับ input ที่ parameterized ไม่ได้ เช่นชื่อ column ก็ต้องทำ input validation ด้วย allow list ก่อนเอา string มาต่อเป็น Prepared statement
ปัจจุบันการใช้พวก ORM (Object Relational Mapping), DAO (Data Access Object) เป็นที่แพร่หลาย และช่วยให้ปลอดภัยจาก SQL injection เกือบ 100% แต่อาจจะมีปัญหาได้เมื่อ developer พยายามผสมการเขียน SQL statement เข้าไปด้วยเช่น เอา input string มาต่อกันเองใน WHERE clause
ดังนั้นถ้าใช้พวก ORM, DAO ผมแนะนำให้หลีกเลี่ยงการเขียน SQL statement เอง หาวิธีการเรียก method ที่ library มีให้ ซึ่งปกติภายใน library จะทำให้ปลอดภัยอยู่แล้ว ยกเว้นในกรณีมีช่องโหว่ใน library ซึ่งจะกลายเป็นปัญหาเรื่อง A06 Vulnerable and Outdated Components
ป้องกัน XSS
วิธีที่ตรงไปตรงมาที่สุดคือ encode ทุกๆ output ที่เป็นตัวแปรตาม context เช่นใน HTML, ใน javascript, ใน CSS เป็นต้น แต่วิธีที่ผมแนะนำคือใช้ HTML template engine ที่สามารถ encode/escape ตัวแปรของเราตาม context ให้อัตโนมัติเช่น Java Thymeleaf, PHP Laravel Blade, Django template เป็นต้น
จากตัวอย่างข้างบน เมื่อใช้ Thymeleaf ก็จะเปลี่ยนเป็น code รูปข้างล่าง

ผลลัพธ์คือปลอดภัยจาก XSS เกือบทุกกรณี ยกเว้น href attribute ที่สามารถทำ XSS ด้วย javascript:js_code โดยต้องอาศัย validation มาช่วยตรวจสอบว่าค่า url ที่รับมาเป็น URL ที่ถูก format และใช้ protocol ที่อนุญาตเท่านั้น (allow list) เช่น http, https เป็นต้น
วิธีป้องกัน XSS ที่กล่าวมาก่อนหน้านี้นั้นสำหรับ Server XSS เท่านั้น ยังมี Client XSS ด้วยที่เกิดจากการใช้ javascript ในการสร้าง HTML ซึ่งสาเหตุหลักก็มาจากเอา string มาต่อกันเช่นกัน เช่นตัวอย่างของ jQuery ต่อไปนี้

วิธีป้องกัน Client XSS ก็คือใช้วิธีเรียก safe API ที่รับค่าทีละส่วน แล้ว API เอาไปประกอบเอง เช่น

นอกจากนี้ web browser ยังมี Content Security Policy (CSP) ที่สามารถช่วย mitigate ช่องโหว่ XSS โดยผมแนะนำให้ใช้ strict CSP แต่อาจจะต้องมีการแก้ไข HTML นิดหน่อย