หลังจาก Cloudflare ล่มเมื่อต้นเดือนที่ผ่านมา โดยได้ชี้แจงเหตุไปแล้วว่าเกิดจากกฎไฟร์วอลล์ข้อเดียว ทางบริษัทก็ยังเขียนบล็อกรายงานถึงสาเหตุการล่มเพิ่มเติม โดยกฎไฟร์วอลล์ที่ว่านั้นมีกฎเต็มๆ ว่า
(?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*)))
ส่วนที่สำคัญที่สุดคือส่วนท้าย ที่มี non-capturing group (จัดกลุ่มโดยไม่ต้องการให้ regular expression (RE) ตัดสตริงในกลุ่มนี้แยกออกมาให้) โดยเขียนแบบไม่จัดกลุ่มเหลือได้ว่า .*.*=.*
การเขียน RE เช่นนี้ทำให้ซีพียูเต็มได้ยาวนาน จากปัญหา backtrack ในเอนจิน RE บางตัว
เนื่องจากกฎสามารถเขียนเป็นภาษาบ้านๆ ได้ว่า อะไรก็ได้-ตามด้วยอะไรก็ได้-ตามด้วยเครื่องหมายเท่ากับ-ตามด้วยอะไรก็ได้ กฎเช่นนี้ทำให้กระบวนการตรวจสอบว่าสตริงตรงตามกฎหรือไม่ในเอนจิน RE ที่ใช้เทคนิค backtrack ใช้เวลานานกว่าปกติอย่างมาก โดยสตริงง่ายๆ แค่ x=x
จะใช้การตรวจสอบถึง 23 ขั้นตอน และเมื่อสตริงยาวขึ้นเป็น 20 ตัวอักษร ก็ใช้การตรวจสอบถึง 555 ขั้นตอน หากสตริงไม่ตรงตามกฎเลย เช่น ไม่มีเครื่องหมายเท่ากับ จะใช้ถึง 4,067 ขั้นตอน
การเขียน RE ที่ดี ควรระมัดระวังให้ตรวจกฎได้แน่ชัด ไม่สร้างกฎที่กำกวมโดยไม่จำเป็น หรือเลือกใช้เอนจิน RE ที่ไม่ใช้เทคนิค backtrack แต่สร้าง finite automata ขึ้นมาตรวจสอบสตริง โดยทั่วไปแล้ว regular expression จะเทียบเท่า finite automata (จากวิชา Theory of Computation ที่บุกเบิกโดย Alan Turing)
ทาง Cloudflare ไม่ได้โทษคนเขียนกฎไฟร์วอลล์ข้อนี้เพียงอย่างเดียว แต่ยอมรับว่าปัญหาเกิดจากความผิดพลาดหลายๆ ส่วนประกอบกัน โดยก่อนหน้านี้มีโค้ดป้องกันกฎไฟร์วอลล์ใช้ซีพียูเกินกำหนดอยู่ แต่ถูกถอดออกไปด้วยความผิดพลาด, การเลือกใช้เอนจิน RE ที่ไม่เหมาะสม, การกู้คืนระบบที่ทำได้ช้าเพราะระบบภายในของบริษัทเองก็มีปัญหาไปด้วย ตอนนี้จึงเดินหน้าแก้ทุกปัญหาไปพร้อมๆ กัน
ที่มา - Cloudflare
Comments
นึกถึงวันเก่าๆ ตอนเรียน 555
คำผิด
?:*.=*.
ปล. ใครเขียน RE อ่ะครับ
.*=.*
หมายถึง <ตัวอักษรอะไรก็ได้ มีหรือไม่มีก็ได้>=<ตัวอักษรอะไรก็ได้ มีหรือไม่มีก็ได้> แปลว่าถ้าเจอ =, ==, ===, ====, 1=1, ==1, 1== และอะไรก็ได้ แค่มี = คั่นกลางก็ถือว่า match rule อันนี้แล้วอยากโบกจังเลยครับ
คุณไม่เข้าใจ regular expression เลยคิดว่ามันผิดครับ
lewcpe.com, @wasonliw
คือผมเข้าใจว่า
.*.*=.*" พาล่มทั้งระบบ
ที่หัวข่าวเขียนผิดเพราะผมดู rule เต็มๆ แล้วไม่เห็นมีน่ะครับ เพราะเห็นมีแค่ non-capturing group (?:.=.) ตอนท้าย เลยคิดว่าเขียนผิดน่ะครับ (หัวข่าวมี .* 3 occurences.*.*=.*
ส่วนใน rule เต็มๆ มีแค่ 2 occurences .=.)หรือว่าผมอ่านข่าวไม่เข้าใจหว่า
ปล. ผมก็มีเขียนอธิบาย rule อยู่ในคอมเมนท์นะ ทำไมถึงคิดว่าผมไม่เข้าใจ RE อ่ะ
แก้ไข เข้าใจละ จากตรงนี้
แล้วเอา rule ใน non-capturing group ออกมาไว้ข้างนอกด้วยการลบ
(?
: กับ)
ออกไป เหลือเข้าใจแล้วครับ ขออภัยฮะ
เกทเลย ทำไมล่ม
อันนี้ จำยากมาก จะหาไป แล้ว ก็อปมาวางเอา เลย ง่าย ดี 55
ใช้ regular expression มานานพึ่งรู้ว่าเขาเรียก RE กัน
ผมเรียก regex ไม่ก็ regexp
ผมติดมาจาก python ครับ
lewcpe.com, @wasonliw
ถ้าพิมพ์ผมใช้ re ครับ ถ้าพูดเรียกว่า regex
Regex = Reject
ใครเขียนมาในโค๊ดแล้วไม่มีเหตุผลรองรับและคอมเมนท์ประกอบที่ดีพอนี่ โดนผมไล่กลับไปแก้โค๊ดใหม่แน่นอนครับ ถือว่าเป็น Code Smell อันนึง
โค๊ดผมก็มี Regex นะ แต่น้อยมาก พยายามใช้ให้น้อยที่สุดเท่าที่เป็นไปได้ เพราะว่าตอนมีปัญหาจะหายากมากๆ ว่าเกิดจากอะไร
พวก firewall ใช้กันเยอะ (มากๆ) ครับ ใช้กรองกรณีต่างๆ เป็นข้อๆ ชัดเจน ไม่ได้ใช้ผสมไปกับโค้ด
อย่างโปรแกรมพวก fail2ban ที่ใช้ป้องกันการยิงรหัสผ่านก็เป็นโอเพนซอร์สยอดนิยมตัวนึงที่ใช้ regex หรืออย่างระบบ central log ที่ใช้ ELK stack ก็ต้องเขียน grok มา parse log กันเป็นปกติ
lewcpe.com, @wasonliw
นึกถึง quote ดัง...
Some people, when confronted with a problem, think
“I know, I'll use regular expressions.” Now they have two problems.
--- Jamie Zawinski (an early Netscape engineer)