OWASP Top10–2021

Datafarm
14 min readOct 26, 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 เป็น <form>…</form> ผลลัพธ์ในหน้านี้ก็มีจะมีฟอร์มมาเพิ่มอีกอันหนึ่ง หรือ attacker จะส่งค่า msg เป็น <script>…</script> ผลลัพธ์ก็คือรัน 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 นิดหน่อย

ใช้ safe API

สาเหตุหลักของเรื่อง injection คือการนำข้อมูลของผู้ใช้งานมาต่อกับ string ที่ hardcode เอาไว้เพื่อสร้างคำสั่งหรือข้อมูลโดยไม่มีการ encode/escape ให้ถูกต้อง ส่งผลให้ความหมายของคำสั่งหรือข้อมูลเปลี่ยนไป วิธีทั่วไปที่ผมแนะนำเพื่อป้องกันปัญหา injection คือใช้วิธีเรียก safe API (วิธีที่แนะนำใน SQL injection และ XSS ก็ใช้ safe API) ที่รับ input เข้าไปเป็นตัวแปร แล้วแปลงให้เป็น string ให้ เช่น การสร้าง json string อย่างใน PHP ก็ควรใช้ json_encode เสมอ แต่บางครั้ง developer เห็นว่าสั้นๆ เลยใช้วิธีต่อ string เอาเอง เพื่อที่จะไม่ต้องเขียนโค้ดยาวๆ

แต่บาง API นั้นก็เอาข้อมูลของเราไปใช้โดยที่ไม่ encode/escape ข้อมูลให้ API พวกนี้ควรถือว่าเป็น unsafe API และหลีกเลี่ยงการใช้งานให้มากที่สุด โดยปัจจุบัน API จาก framework หรือ library ต่างๆ ส่วนใหญ่เป็น safe API ส่วนที่เป็น unsafe API ก็ต้องค่อยๆ เรียนรู้กันไป แล้วอาจะทำเป็น list ของ unsafe API

ส่วนวิธีการที่เราสั่ง encode/escape ข้อมูล แล้วเอา string มาต่อกันเอง วิธีนี้ผมแนะนำให้เป็นวิธีสุดท้าย เพราะมีโอกาสทำผิดพลาดได้ง่าย ซึ่งปกติจะเขียนเป็น method เพื่อที่จะใช้ซ้ำได้โดยไม่ต้องต่อ string เองในที่อื่นๆ

สรุปข้อแนะนำของผมคือใช้ framework แล้วศึกษา pattern ที่เอกสารแนะนำในการเขียน ซึ่งจะช่วยเรื่อง input validation และช่องโหว่ประเภท injection ได้เยอะมาก

A04 Insecure Design

ปัญหาเรื่องนี้อาจมองได้ว่าเป็นเรื่อง control design มีไม่เพียงพอ ส่งผลให้เกิดความเสี่ยงแม้ว่าในส่วน implementation ทำตาม design ได้ถูกต้อง ตัวอย่างที่น่าจะคุ้นเคยกันคือ การออกแบบกระบวนการ reset password ด้วยการตอบคำถาม security questions ที่ได้ตั้งไว้ตั้งแต่ registration จะเห็นว่าวิธีถึงแม้ว่าโค้ดจะเขียนได้สมบูรณ์แค่ไหน ก็ยังคงมีปัญหาที่ว่าคำตอบของคำถามไม่ใช่สิ่งที่ผู้ใช้งานรู้คนเดียว ซึ่งทั้งในเอกสาร NIST 800–63b, OWASP ได้ห้ามใช้ questions สำหรับ reset password

อีกตัวอย่างของ insecure design สมมติว่าโรงภาพยนตร์อนุญาตให้มีการจองที่นั่งแบบกลุ่มไว้ไม่เกิน 15 คนต่อกลุ่มก่อนที่จะต้องจ่ายเงิน แต่ไม่มี control อะไรสำหรับ feature นี้ ทำให้ attacker แบ่งการจองเป็นหลายๆ กลุ่มเพื่อที่จองที่นั่งทั้งโรงภาพยนตร์ไว้เฉยๆ ส่งผลให้ที่นั่งเต็มโดยไม่มีผู้ชม และสูญเสียรายได้ไป

สาเหตุหนึ่งที่ทำให้เกิด insecure design คือการที่ไม่ได้ทำ Threat Modeling และ/หรือ Risk Profiling ของ application ส่งผลให้ไม่สามารถกำหนดได้ว่าระบบของเราต้องการ security control อะไรบ้าง

บางกิจกรรมเพื่อ Secure Design

  • นำ Secure Development Lifecycle (SDL) มาใช้โดยให้ผู้เชี่ยวชาญช่วยประเมินและออกแบบ control ที่เกี่ยวกับ security และ privacy
  • ใช้ library/framework ที่พร้อมในการทำ secure design patterns
  • ทำ threat modelling กับส่วนที่สำคัญเช่น authentication, access control, business logic, flow ของ cryptography key เป็นต้น
  • เพิ่ม security control เข้าไปใน user story
  • เขียน unit tests และ integration tests เพื่อทดสอบว่า application สามารถป้องกันได้ตาม threat model ที่สร้างเอาไว้ โดย testcases ต้องมีทั้งแบบ input ที่ถูกต้อง และแบบ input ที่ไม่ถูกต้อง

A05 Security Misconfiguration

หัวข้อนี้เป็นปัญหาเกี่ยวกับการตั้งค่าของระบบที่ไม่ดีพอ โดยเกี่ยวข้องกับทุกส่วนของ application เช่น เปิด service ที่ไม่ใช้งานใน server, มี directory listing ใน web server, แสดง error สำหรับ developer ใน application เป็นต้น

บางปัญหาในหัวข้อนี้ เมื่อดูแต่ละปัญหาอาจจะเห็นว่ามีผลกระทบด้านความปลอดภัยน้อยมาก แต่หลายๆ ครั้ง attacker เอาปัญหาเล็กๆ น้อยๆ พวกนี้มารวมกัน หรือเอามาใช้ร่วมกับช่องโหว่อื่น ทำให้เกิดการโจมตีที่มีความรุนแรงสูงได้ ดังนั้นผมแนะนำว่าอย่ามองข้ามปัญหาพวกนี้ โดยเฉพาะเรื่องที่สามารถแก้ไขได้ง่ายๆ

ตัวอย่าง Misconfiguration

ระบบยังมีการใช้ Default accounts and passwords

บางโปรแกรม หรือบางอุปกรณ์มากับ default accounts เพื่อให้ผู้ดูแลระบบสามารถเข้าไปจัดการ และเปลี่ยน default passwords แต่ก็มีผู้ดูแลระบบไม่เปลี่ยน default password ทำให้ attacker สามารถ login เข้าระบบได้

Error Message มีรายละเอียดมากเกินไป

เวลาเจอ bug ในโปรแกรม ปกติแล้ว developer ต้องการ error message ที่มี stack traces เพื่อช่วยหาว่า bug เกิดจากอะไรแล้วแก้ไขได้ตรงจุด แต่ error message นั้นก็มีประโยชน์กับ attacker เพราะบาง error message มีข้อมูลที่เป็นข้อมูลภายใน application หรืออาจะมี sensitive data รวมอยู่ด้วย และยังสามารถใช้เพื่อระบุปัญหาที่เกิดขึ้นว่า เกิดเพราะอะไร แล้วนำเป็นข้อมูลเพื่อใช้โจมตีระบบด้วยช่องโหว่อื่น

มี components หรือ services ที่ไม่ได้ใช้งาน

เรื่องนี้จะมีทั้งใน application และ servers ที่เกี่ยวข้อง โดย servers จะเป็นการติดตั้งหรือเปิด services ที่ไม่ได้ใช้งาน และใน application จะเกี่ยวกับ libraries ต่างๆ รวมทั้งอาจจะมี features ที่ไม่ได้ใช้งาน ปัญหานี้อาจจะเกิดจากเคยใช้ แล้วไม่ได้เอาออกเมื่อเลิกใช้ หรืออาจจะใช้แนวคิดที่เป็น bad practice คือเปิดไปให้หมดไปก่อน หรือเพิ่ม libraries ต่างๆ เข้าไปก่อนทีเดียว เวลาจะใช้จะได้ไม่ต้องมีปัญหาเรื่องว่าขาด service ตัวนี้ ต้องการ library ตัวนั้น

Components หรือ services ที่ไม่ได้ใช้งานนั้นอาจจะมีช่องโหว่ทำให้ attacker สามารถใช้เพื่อเป็นทางโจมตีเพื่อยึดระบบได้ ดังนั้นยิ่งมีเยอะก็มีช่องทางการโจมตีมากขึ้น และถ้าต้องการความปลอดภัยก็ต้องมีการตรวจสอบว่า version ที่ติดตั้งอยู่มีช่องโหว่ที่ถูกค้นพบใหม่ แล้ว patch อย่างสม่ำเสมอ ดังนั้นยิ่งมีส่วนที่ไม่ใช้งานเยอะเท่าไร ก็ยิ่งต้องใช้เวลาในการดูแลมากขึ้น

ไม่ได้กำหนดค่าสำหรับ security

เช่นไม่ได้ set HttpOnly กับ Secure ใน session cookie

XML External Entity (XXE)

ช่องโหว่นี้เกิดจาก application หรือ libraries ที่เราใช้งานมีการรับ input ที่เป็น XML แต่ไม่มีการปิดการใช้งาน External Entity ของ XML ทำให้ attacker สามารถใช้ feature นี้เพื่ออ่านข้อมูลที่อยู่ในระบบของเรา

วิธีป้องกัน

Pre-hardened ทั้งใน development และ production

คือการทำ harden ระบบก่อนที่จะเริ่มมีการพัฒนา application การทำแบบนี้อาจทำให้ application ทำงานไม่ได้เมื่อ developer เขียนโค้ดที่ต้องใช้ configuration ที่อันตราย ซึ่งผมมองว่าเป็นเรื่องที่ดีเพราะว่าเราสามารถเห็นปัญหาได้ทันที โดยอาจต้องเขียนใหม่ถ้ามีวิธีอื่นที่ปลอดภัย หรือหา control มาเพิ่มเมื่อมีความจำเป็นต้องใช้ configuration ที่อันตราย

สำหรับ hardening ที่ทำในส่วน development แน่นอนว่าต้องปลอดภัยน้อยกว่า production เพื่อที่จะไม่กระทบการเขียนโค้ดของ developer ยกเว้นมีการใช้ configuration ที่อันตราย ตัวอย่าง configuration ที่ควรเปิดสำหรับ development คือการแสดง error message

Automate ขั้นตอนการ hardening

การ hardening ส่วนมากเป็นส่วนที่เกี่ยวกับ configuration ต่างๆ ซึ่งส่วนนี้สามารถใช้เครื่องมือประเภท Configuration Management ทำให้อัตโนมัติหลังจากเขียน configuration ครั้งแรก วิธีนี้ช่วยลดความผิดพลาดที่เกิดจากการแก้ไข configuration เองทีละค่า ซึ่งอาจจะทำให้เกิดช่องโหว่ขึ้นได้

เปิดใช้งานเฉพาะเท่าที่จำเป็น

ให้มั่นใจว่า application ของเรามีเฉพาะ components หรือ services ที่เราใช้งานเท่านั้น เพื่อลด attack surface และภาระที่ต้องตาม patch ช่องโหว่ที่เกิดขึ้นใน components ต่างๆ (A06 Vulnerable and Outdated Components) ที่เกิดจาก components หรือ services ที่ไม่มีความจำเป็น

จัดการ error messages

เรื่องปิดไม่ให้ error messages แสดงผลที่มีรายละเอียดในเครื่อง production นั้นปกติจะเป็นส่วนของ hardening แต่นอกจาก configuration แล้ว application ของเราควรมี global exception handler ดักไว้ในกรณีที่เราลืมจัดการ exception ในบางกรณี เพื่อไม่ให้ข้อมูลหลุดออกไปได้ แล้วทำการ log เพื่อใช้ในการแก้ไข error

ปิด External Entity ใน XML parser

External Entity เป็น feature ที่ปกติไม่ได้ใช้งาน โดยเฉพาะอย่างยิ่ง XML ที่มาจากผู้ใช้งาน ดังนั้นข้อแนะนำคือให้ปิด Document Type Definition (DTD) feature ไปเลย หรือถ้าต้องการใช้ DTD ก็ต้องปิด External Entity feature

A06 Vulnerable and Outdated Components

ปัญหานี้เกิดจากไม่มีการอัพเดตตัว application และ component ที่เกี่ยวข้องซึ่งได้แก่ libraries ที่นำมาใช้ทั้งหมด, Operating System (OS), Web Server, DBMS และอื่นๆ ที่อยู่ในระบบของเรา

วิธีป้องกัน

  • เอาพวก components ที่ไม่ได้ใช้งานออกให้หมด เพื่อลด components ที่ต้องตรวจสอบและอัพเดต
  • มีการตรวจสอบ version ของทุก components ทั้งฝั่ง server และ client ที่ใช้งานอย่างสม่ำเสมอ โดยพวก dependencies จะพอมีเครื่องมือช่วยในการตรวจสอบเช่น OWASP Dependency Check, retire.js เป็นต้น ซึ่งแนะนำให้เรียกใช้เครื่องมือจากใน CI/CD เพื่อการตรวจสอบอัตโนมัติ
  • Subscribe เพื่อรับ email alert สำหรับช่องโหว่ใน components ที่ใช้งานถ้าเป็นไปได้
  • ทุกๆ component ที่ใช้งานให้เอามาจาก official sources ผ่านทาง secure links เช่น HTTPS และถ้าเป็นไปได้ให้เลือกใช้ software ที่มี digital signature เพื่อลดความเสี่ยงจาก component ที่ถูกแก้ไข (A08 Software and Data Integrity Failures)

ส่วนที่ขาดไม่ได้คือ patch ในกรณีที่พบว่า components ที่ใช้งานนั้นมีช่องโหว่ในระดับวิกฤติ ควรที่จะทำการ patch ในทันที ส่วนช่องโหว่อื่นๆ ก็ควรที่ patch อย่างสม่ำเสมอ เพราะว่าปกติ patch ในแต่ละ component จะมีขนาดเล็ก แม้ว่าอาจจะต้องการแก้โค้ด ปกติก็แก้นิดเดียว แต่ถ้าเราไม่ได้ patch ไปหลายๆ version เวลาที่เจอช่องโหว่ที่สำคัญขึ้นมา ก็ต้องไปไล่ดูว่าต้องแก้โค้ดอะไรบ้างจาก patch ทั้งหมด ทำให้การ patch นั้นยากขึ้นมาก ดังนั้นเราควรที่จะมีการวางแผนและคนที่รับผิดชอบ เพื่อที่จะทำการ patch ได้อย่างสม่ำเสมอ

A07 Identification and Authentication Failures

หัวข้อนี้จะประกอบด้วยหัวข้อย่อยใหญ่ๆ คือ Identity ของผู้ใช้งาน Authentication และ Session management โดยเรื่องของข้อผิดพลาดส่วนมากคือ ไม่ได้มีการป้องกันเรื่องต่างๆ ซึ่งผมมองมา มาดูเลยดีกว่าว่าควรทำอะไรบ้างเพื่อป้องกัน

วิธีป้องกัน

Multi-Factor Authentication (MFA)

MFA คือการใช้ authentication factor ตั้งแต่ 2 ขึ้นไป โดยปกติใช้แค่ 2 (2FA) และที่ใช้กันแพร่หลายใน application คือ password (Something you known) และ token (Something you have) โดยใน application ควรมีทางเลือกให้ผู้ใช้งานทั่วไปสามารถเปิดใช้งาน MFA ได้ และควรบังคับ MFA สำหรับผู้ใช้งานที่มีสิทธิ์สูง

สำหรับการ authentication แบบ Something you have นั้น ตามเอกสาร NIST SP 800–63 ได้ระบุว่า SMS ที่ส่งผ่าน PSTN นั้นอยู่ในหมวดจำกัดการใช้งาน ส่วน SMS ที่ผ่านทาง VoIP และ Email ไม่ให้ใช้งาน ดังนั้นถ้าต้องการทำตามเอกสารของ NIST วิธีง่ายสุดที่ไม่ต้องใช้ hardware คือ Software TOTP token แต่ถ้าเราไม่สามารถทำตามเอกสารของ NIST ได้ ก็ยังแนะนำให้ทำ MFA อยู่ดีนะครับ มี MFA ยังไงก็ดีกว่าไม่มี

Password

  • บังคับให้ความยาวของ password ว่าขั้นต่ำ และสูงสุดกี่ตัวอักษร
  • อนุญาตให้ใช้ทุกตัวอักษรใน Unicode
  • ควรมี strength meter ให้ผู้ใช้งานเห็นขณะเลือก password
  • มีการตรวจสอบ breached password ทุกครั้งที่มีการ authentication และตอน registration
  • ใช้ password hash algorithm เช่น Argon2id, Scrypt, Bcrypt เป็นต้น

Authentication

  • ส่งข้อมูล Authentication บน secure channel เท่านั้นเช่น TLS
  • มี rate limit เพื่อป้องกันการ brute force และไม่แนะนำการทำ account lockout เพราะทำให้เกิด Denial of Service (DoS) ได้ โดยควรทำทั้งส่วน registration, login และ credential recovery
  • ต้องมีการ Re-authentication เสมอ เมื่อมีการแก้ไขข้อมูลที่เกี่ยวข้องกับการ authentication และ credential recovery
  • ขั้นตอนการทำ credential recovery ต้องมีการ authentication ที่ดีกว่าหรือเท่ากับการ authentication ปกติ

Session Management

  • เปลี่ยน session id ทุกครั้งที่มีเปลี่ยน privilege เช่น login
  • อย่าตั้ง session timeout ไว้นานเกินไป
  • ถ้ามี session token ใน cookie ควรมีการใช้ SameSite, HttpOnly, Secure attribute และ __Host- prefix
  • การใช้ JWT token ต้องมีการ sign เสมอ และ key ที่ใช้ต้องมาจากการสุ่มที่มีความยาวพอเช่น 256 bit (ดู A02 Cryptographic Failures)

A08 Software and Data Integrity Failures

ตามที่ผมเข้าใจเรื่องใหญ่ของหัวข้อนี้คือ ต้องการให้มีการป้องกันทั้ง Software Supply Chain เพราะ attacker ไม่จำเป็นต้องมาโจมตี application ของเราที่ขึ้น production แล้วเท่านั้น แต่ไปโจมตีส่วนต่างๆ ที่เกี่ยวข้องกับการพัฒนาเพื่อไปแก้ไข source code หรือเพิ่ม malicious code ในกระบวนการ build software เช่น โจมตีผู้พัฒนา library/framework ที่เราใช้งาน โจมตีตัว repository เพื่อให้เราดึง malicious package เข้าไปใน application ของเรา โจมตีเครื่อง developer แล้วเพิ่ม code เข้าไป VCS ของเรา โจมตีส่วน CI/CD แล้วแทรก code ในกระบวนการ build เป็นต้น ซึ่งมีผลเรื่อง application integrity ของเราเอง

นอกจากเรื่อง Supply Chain แล้วหัวนี้นี้ยังรวมถึงเรื่อง integrity ของข้อมูลภายใน application ของเราเอง และ integrity ของ software หรือโค้ดที่ download มาก่อนที่จะสั่งให้ทำงาน

ตัวอย่างที่น่าสนใจสำหรับความเสี่ยงนี้คือ Typosquatting เป็นการโจมตีโดยตั้งชื่อ package เป็นชื่อที่ developer มีโอกาสพิมพ์ผิด แล้ว upload package ขึ้นไปใน repository เช่นใน PyPI repository ถ้า developer ต้องการติดตั้ง package เพื่อใช้งาน sqlite แต่ใน pypi ไม่มี package ชื่อนี้ attacker จึงสร้าง package ที่ชือว่า sqlite แล้วคอยให้ developer ใส่ชื่อ package ผิด

อีกตัวอย่างคือ Dependency confusion เป็นวิธีโจมตีที่ถูกเปิดเผยในปี 2021 โดยการตั้งชื่อ package ใน public repository ให้ตรงกับชื่อ package ที่อยู่ใน private repository ขององค์กรที่เป็นเป้าหมาย จากการทดลองกับองค์กรใหญ่ๆ ปรากฏว่าหลาย configuration ที่เคยดึง package จาก private repository มาใช้ package ที่อยู่ใน public repository

วิธีป้องกัน

ตรวจสอบ software signature ก่อนใช้งาน

เวลาเรากด download software/firmware จากผู้ผลิต หลายครั้งเราจะเห็น signature หรือ checksum hash ไว้คู่กัน ค่าพวกนี้มีไว้สำหรับตรวจสอบว่าไฟล์ที่เรา download มานั้นไม่มีคนอื่นมาแก้ไขก่อนถึงมือเรา ดังนั้นก่อนการติดตั้งหรือใช้งานต้องมีการตรวจสอบก่อนว่าค่า signature หรือ checksum นั้นถูกต้องหรือไม่

อีกเรื่องคือการใช้งาน javascript และ css จาก CDN ต่างๆ ก็ควรที่จะใส่ค่า Subresource Integrity (SRI) ไว้ด้วยเพื่อป้องกันกรณีที่ CDN ถูก compromised ซึ่ง browser จะใช้ค่านี้มาตรวจสอบว่า javascript หรือ css ที่ load มาจาก CDN นั้นค่า hash ตรงกับ SRI ที่กำหนดไว้หรือไม่ เช่น

ใช้งานจาก trusted repositories เท่านั้น

พวก library และ dependency ต่างๆ ทีใช้งานต้องถูกดึงมาจาก trusted repository เท่านั้น และต้องถูกดึงมาจาก repository ที่ตั้งใจไว้เท่านั้นในกรณีมีหลาย repository

ใช้เครื่องมือเพื่อตรวจสอบว่าทุก component ไม่มีช่องโหว่ที่ public แล้ว

วิธีป้องกันนี้จะตรงกับ A06 Vulnerable and Outdated Components โดยทาง OWASP แนะนำให้ใช้เครื่องมือช่วยในการตรวจสอบเช่น OWASP Dependency Checker, OWASP CycloneDX เป็นต้น

มี process ในการตรวจทาน code และ configuration

เนื่องด้วย attacker อาจจะยึดเครื่องของ developer ได้แล้วไปเพิ่ม malicious code เข้าไป ดังนั้นเราควรจะมีกระบวนการตรวจทาน code และ configuration ก่อนที่จะ commit เข้า VCS กลาง

ป้องกัน CI/CD pipeline

ในส่วนของ CI/CD เองก็ต้องก็ต้องมีการป้องกันให้ครบถ้วน ทั้งเรื่องการแยก network การตั้งค่าความปลอดภัย เรื่อง access control เพื่อไม่ให้มี attacker มาแก้ไขและเพิ่ม malicious code ใน CI/CD pipeline

ป้องกัน Insecure Deserialization

ช่องโหว่นี้วิธีแก้ไขที่ผมแนะนำคือใช้ format ในการรับส่งข้อมูลที่เป็น data อย่างเดียวเช่น JSON เป็นต้น ส่วนในกรณีที่เป็น application ที่ทำไปแล้วและเปลี่ยน format ยาก หรือต้องการเก็บ state ไว้ที่ฝั่ง client ก็แนะนำให้มีการทำ data signing เช่นใช้ HMAC หรือ public/private key pair

ส่วนในกรณีที่รับมาตรงๆ จากผู้ใช้แล้วเปลี่ยน serialization format ไม่ได้ แนะนำให้ทำ allow list ว่าสามารถ deserialize เป็น object อะไรได้บ้าง

A09 Security Logging and Monitoring Failures

เนื่องด้วยความปลอดภัยของระบบนั้น ไม่มีคำว่า 100% ทำให้ระบบ logging กับ monitoring นั้นมีความจำเป็นเพื่อช่วยในการตรวจจับการโจมตีที่เกิดขึ้นในระบบ และในกรณีถูกโจมตีแล้ว ก็สามารถตรวจสอบได้ว่า attacker ได้ใช้ช่องโหว่อะไรเข้ามาในระบบ และเข้ามาในส่วนในบ้างของระบบมีความเสียหายอะไรบ้าง และหลังจากเข้าใจวิธีการโจมตี ก็นำมาเพิ่มใน Threat Model ที่เราสร้างไว้ แล้วคิดวิธีป้องกันไม่ให้เกิดการโจมตีประเภทนี้ซ้ำอีก

ข้อแนะนำในการทำ Logging และ Monitoring

  • มีระบบกลางสำหรับเก็บ log เพื่อป้องกันการลบ log ในเครื่องที่ถูกโจมตี
  • Log format ควรที่จะเป็นรูปแบบเดียวกันทั้งหมดใน application และระบบอื่นที่อยู่ในองค์กรถ้าเป็นไปได้
  • ไม่ log sensitive data เช่น password, credit card เป็นต้น
  • Log ข้อมูลให้ครบถ้วนโดยอย่างน้อยต้องมีข้อมูลเกี่ยวกับ เวลา (When), เกิดขึ้นที่ส่วนไหนของระบบ (Where), ใครเป็นคนทำให้เกิด event นี้ (Who), และเกิดอะไรขึ้นในระบบ (What)
  • Log ทุกครั้งที่การตรวจสอบเรื่องเกี่ยวกับ security มีข้อผิดพลาด เช่น login fail, พยายามเข้าถึงข้อมูลที่ไม่ได้อนุญาต, input validation ไม่ผ่าน เป็นต้น
  • Log ทุก login และคำสั่งที่เกี่ยวข้องกับ sensitive data ทั้งหมด
  • มีระบบ monitoring แบบ real time และมีการแจ้งเตือนผู้รับผิดชอบทันทีที่พบการโจมตีเกิดขึ้น
  • มีผู้รับชอบที่ชัดเจนเมื่อมี alert เกิดขึ้น

ผมขอเน้นในหัวข้อนี้ว่าต้องมีเรื่อง monitoring ด้วย เพราะว่าการเก็บ log อย่างเดียวไม่ช่วยให้รู้ว่าระบบคุณโดนโจมตีอยู่หรือเปล่า และอาจถึงขั้นโดนขโมยข้อมูลไปแล้วก็ไม่รู้ตัวเป็นปีๆ

A10 Server Side Request Forgery (SSRF)

ช่องโหว่นี้เกิดจาก feature ใน application มีการใช้ URL จากผู้ใช้งาน เพื่อไปใช้ติดต่อระบบอื่น เช่น callback URL, link preview, analytics เป็นต้น ทำให้ server ของเราที่คาดหวังว่า URL พวกนี้จะต่อไปหาเครื่องที่อยู่ใน network ภายนอก กลายเป็นต่อมาหาเครื่องที่อยู่ใน network ภายในรวมถึง localhost

ตัวอย่างช่องโหว่ SSRF

Input คือ URL

สมมติว่าเป็น callback URL โดยผู้ใช้งานส่ง URL มาแล้วโปรแกรมใช้ URL โดยไม่มีการ validation ใดๆ ดังเช่นโค้ด Java ตัวอย่างต่อไปนี้

ตัวอย่างนี้ attacker สามารถส่ง URL เป็น http://localhost:12345/ ผลลัพธ์คือ server ของเราพยายามต่อไปที่ localhost:12345 ซึ่งเป็นเป้าหมายที่ application ของเราไม่ควรต่อไม่ว่ากรณีใดๆ ก็ตาม และนอกจาก localhost เป้าหมายอื่นๆ สำหรับ attacker คือต่อหาเครื่องอื่นที่อยู่ภายใน

Input เป็นส่วนหนึ่งของ URL

สมมติว่า input เป็นแค่ส่วนของ URL path ดังตัวอย่างโค้ด Java ดังต่อไปนี้

ตัวอย่างนี้ attacker สามารถส่ง name เป็นค่า ../path/to/secret# ทำให้ request ของเราส่งไปที่ path ที่เราไม่ต้องการได้

วิธีป้องกัน

วิธีป้องกันช่องโหว่นี้จะยุ่งยากหน่อย เพราะว่ามันเกี่ยวข้องกับ network โดยเฉพาะเมื่อรับเป็น URL จะมี hostname ถึงแม้ว่าจะทำการตรวจสอบว่า hostname คือ IP address ที่อนุญาตหรือไม่ก่อนแล้ว ตอนที่มี request จริง hostname อาจจะเปลี่ยนเป็นอีก IP address ก็ได้ ทำให้แนะนำให้มีการป้องกันทั้งในระดับ network และ application

การป้องกันในระดับ network

  • แยก network สำหรับ server ที่มี feature ใช้ URL จากผู้ใช้งาน
  • ใช้ firewall policy ที่ block all แล้วค่อยเปิดให้ต่อออกไปหา intranet เฉพาะ server ที่จำเป็นเท่านั้น

การป้องกันใน application

  • ทำการ validate URL โดยใช้ validator library ที่เชื่อถือได้
  • ถ้า URL มีส่วน fragment, username, password แนะนำให้ถือว่าเป็น Invalid URL
  • ตรวจสอบ protocol, host, port, path ว่าเป็นที่อนุญาตเท่านั้น เช่น protocol เป็น https เท่านั้น, port ต้องเป็น default port เท่านั้น
  • ปิด HTTP redirection ใน HTTP client API
  • HTTP client API ที่ใช้ต่อควรมีการ custom connector หรือ DNS resolver เพื่อป้องกันปัญหาเรื่อง Time Of Check, Time Of Use (TOCTOU) จากการแปลง hostname เป็น IP address เพื่อตรวจสอบก่อนมีการต่อจริง และการแปลง hostname เป็น IP address เพื่อที่จะต่อไปหา target
  • ตรวจสอบ response ก่อนส่งไปหากลับไปที่ clients

เนื่องด้วยโค้ดตัวอย่างสำหรับการป้องกัน SSRF ค่อนข้างยาว ผมขอยกตัวอย่างที่เป็นคำสั่ง curl แทน โดยเป็นการเรียกใช้หลังจาก validation ทุกส่วนของ URL เสร็จแล้ว และได้ตรวจสอบว่า hostname และ IP address แล้ว

จะเห็นว่าคำสั่งนี้เป็นการกำหนดคำสั่ง curl ไว้ว่า hostname ต้องถูกแปลงเป็น IP address ที่ถูกตรวจสอบแล้วเท่านั้น ทำให้มั่นใจได้ว่า request ไม่มีทางต่อไปหาเครื่องที่อยู่ใน network ภายใน แต่จุดหนึ่งที่ต้องระวังสำหรับการเรียกใช้คำสั่ง curl จากในโปรแกรมคือเรื่อง OS command injection

ส่วนกรณีที่ input ของผู้ใช้งานอยู่ใน URL path หรือ query string ก็ให้ทำการ encode ให้ถูกต้อง ซึ่งจะเป็นเรื่องเกี่ยวกับ injection

--

--

No responses yet