คดีประหลาดของจุดที่หายไป
(tjaart.substack.com)- ในระบบที่สร้างอีเมล·ข้อความ SMS·PDF จากเทมเพลตเอกสาร เกิดปัญหาว่าเฉพาะเนื้อหาอีเมลที่ส่งให้ลูกค้าบางรายเท่านั้นที่ จุดหายไป
- ในต้นฉบับเทมเพลต·ตัวอย่างแสดงผล·PDF ยังมีจุดอยู่ แต่หลังจากแทนที่ ค่า placeholder เฉพาะลูกค้าด้วยข้อมูลจริงแล้ว จะเกิดซ้ำได้เฉพาะที่ความยาวบรรทัดบางแบบเท่านั้น
- SMTP client แบบกำหนดเองจำกัดความยาวบรรทัด ทำให้จุดถูกดันไปเป็นอักขระแรกของบรรทัดใหม่ และเซิร์ฟเวอร์ลบจุดนั้นออกเพราะกฎ dot transparency ของ SMTP
- การแก้ไขทำโดยให้ client เติมจุดเพิ่มอีกหนึ่งตัว หน้าบรรทัดที่ขึ้นต้นด้วยจุดและมีอักขระอื่นตามหลัง
- อีกทีมที่ใช้โค้ด SMTP client ชุดเดียวกันไม่ได้แพตช์ ทำให้อีเมลที่ลูกค้าบางรายเห็นค่าเบี้ยประกันรายเดือนเป็น $2700 แทนที่จะเป็น $27.00 ถูกส่งออกไป และบั๊กได้รับการแก้ไขทันที
ปัญหาที่เริ่มจากระบบเทมเพลตส่วนกลาง
- เมื่อราว 7 ปีก่อน ลูกค้ารายหนึ่งต้องการรวมเอกสารที่กระจัดกระจายอยู่ในเทมเพลต Microsoft Word หลายไฟล์ให้มาอยู่ในระบบเดียว
- วิธีเดิมคือพนักงานเปลี่ยน placeholder ในเอกสารเป็นชื่อ นามสกุล ฯลฯ ด้วยตัวเอง และเทมเพลตที่มีข้อกำหนดเก่า·โลโก้บริษัทเดิม·ฟอนต์ผิด ก็ถูกส่งต่อใช้งานปะปนกัน ทำให้จัดการได้ยาก
- ระบบใหม่ถูกจัดให้บริหารเทมเพลตเอกสารจากส่วนกลาง และสร้าง เอกสาร PDF, ข้อความ SMS, และเนื้อหาอีเมลจากเทมเพลตเหล่านั้น
- ข้อความต้อนรับลูกค้าใหม่ก็สามารถมีเทมเพลตต่างกันได้ตามช่องทางการส่ง
- เวอร์ชันอีเมลสามารถใช้ตาราง HTML และสไตล์พื้นฐานได้
- เวอร์ชันสำหรับส่งทางไปรษณีย์สามารถมีอินโฟกราฟิกได้
- เวอร์ชันข้อความ SMS ใส่ได้เพียงข้อความต้อนรับสั้น ๆ
จุดที่หายไปเฉพาะลูกค้าบางราย
- หลังระบบเดินงานไปได้หลายเดือน หรืออาจเกิน 1 ปี ผู้ดูแลคนหนึ่งแจ้งว่าในเนื้อหาอีเมลที่ส่งให้ลูกค้ารายหนึ่ง จุดหายไปหนึ่งตัว
- เมื่อส่งอีเมลเดียวกันให้ลูกค้ารายอื่น จุดไม่หายไป จึงดูไม่น่าใช่ข้อผิดพลาดของเทมเพลตธรรมดา
- ในซอร์สโค้ดของเทมเพลต จุดดังกล่าวมีอยู่จริง
- เมื่อนำเทมเพลตจากโปรดักชันมาคัดลอกในสภาพแวดล้อม local แล้วสร้างเนื้อหาอีเมลเป็นตัวอย่างแสดงผล ก็เห็นจุดอยู่ และเวอร์ชันสำหรับพิมพ์ที่สร้างจากเทมเพลตเดียวกันก็แสดงผลปกติ
- สภาพแวดล้อม local ใช้วิธีส่งอีเมลไปยัง localhost ที่พอร์ตหนึ่ง และตรวจอีเมลด้วย SMTP server ปลอมอย่าง SMTP4dev กับ Outlook
- ตอนตรวจอีเมลที่ส่งจาก local ครั้งแรกด้วย Outlook จุดก็แสดงผลปกติ
เงื่อนไขการเกิดซ้ำที่มาจากข้อมูลเฉพาะลูกค้า
- เทมเพลตจะแทนที่ ค่า placeholder เช่นชื่อและนามสกุลของลูกค้าด้วยข้อมูลจริงเมื่อสร้างอีเมล, PDF, และข้อความ SMS
- แม้จะเป็นเทมเพลตเดียวกัน เนื้อหาอีเมลที่ส่งให้ลูกค้าแต่ละรายก็อาจมีความยาวต่างกันได้
- เมื่อค้นหาค่า placeholder ที่ใช้จริงกับลูกค้ารายที่มีปัญหา แล้วส่งอีเมลอีกครั้งในสภาพแวดล้อม local ด้วยค่าเดียวกัน ก็พบอาการจุดหายไปใน Outlook
- ปัญหาขึ้นอยู่กับ ความยาวและเนื้อหาเฉพาะของเนื้อหาอีเมล ของลูกค้ารายนั้น
- ตอนแรกตรวจสอบว่าตัวอักษรจุดในเทมเพลตถูกเข้ารหัสหรือไม่ หรือเป็นอักขระอื่นที่ไม่ใช่จุดจริงหรือไม่ แต่ไม่ใช่สาเหตุ
- เมื่อเลื่อนตำแหน่งจุดในเทมเพลตไปหนึ่งตำแหน่ง Outlook ก็แสดงจุดอีกครั้ง ทำให้ตำแหน่งบรรทัดกลายเป็นเบาะแสของการเกิดซ้ำ
ความยาวบรรทัดของ SMTP และ dot transparency
- จากการดีบักพบว่าโค้ดที่บันทึกอีเมลลงฐานข้อมูลไม่ได้เปลี่ยนเทมเพลต นอกจากแทนที่ placeholder ด้วยข้อมูลลูกค้า
- หลังจากนั้นขอบเขตการตรวจสอบจึงแคบลงไปที่โค้ดของ cron job ซึ่งดึงรายชื่อผู้รับเป็นระยะแล้วส่งอีเมล
- โค้ดบางส่วนที่ cron job เรียกใช้นำมาจากโปรเจกต์ก่อนหน้า และในนั้นมีการทำ SMTP client แบบกำหนดเองรวมอยู่ด้วย
- ในโค้ดดังกล่าวมีฟังก์ชันที่ตัดบรรทัดเพื่อไม่ให้แต่ละบรรทัดของเนื้อหาอีเมลยาวเกินจำนวนอักขระที่กำหนด
- พฤติกรรมนี้เกี่ยวข้องกับข้อจำกัดความยาวบรรทัดในสเปก SMTP
- ความยาวรวมสูงสุดของบรรทัดข้อความคือ 1000 octets รวม
<CRLF> - จุดนำหน้าที่ถูกทำซ้ำเพื่อความโปร่งใสไม่ถูกนับรวมในการคำนวณนี้
- ค่านี้อาจเพิ่มขึ้นได้ด้วย SMTP Service Extensions
- ความยาวรวมสูงสุดของบรรทัดข้อความคือ 1000 octets รวม
- ในเนื้อหาอีเมลที่มีปัญหา จุดถูกย้ายไปเป็นอักขระแรกของบรรทัดถัดไปเพราะข้อจำกัดความยาวบรรทัด
- ในสเปก SMTP เครื่องหมายสิ้นสุด mail data จะถูกประมวลผลเป็นบรรทัดที่มีจุดเพียงตัวเดียว
- กฎ dot transparency ของ SMTP จัดการบรรทัดเนื้อหาที่ขึ้นต้นด้วยจุดเป็นกรณีพิเศษ
- SMTP client ต้องตรวจว่าอักขระแรกเป็นจุดหรือไม่ก่อนส่งบรรทัดข้อความเมล และถ้าเป็นจุด ต้องแทรกจุดเพิ่มอีกหนึ่งตัวที่ต้นบรรทัด
- SMTP server จะถือว่าบรรทัดที่มีจุดเพียงตัวเดียวเป็นจุดสิ้นสุดของข้อมูลเมล
- ถ้าอักขระแรกเป็นจุดและมีอักขระอื่นในบรรทัดเดียวกัน server จะลบอักขระแรกออก
- SMTP client แบบกำหนดเองไม่ได้ทำ การเพิ่มจุดอีกหนึ่งตัว เมื่อบรรทัดขึ้นต้นด้วยจุด และเมื่อ SMTP server ฝั่งรับลบจุดแรกออก จุดจริงในเนื้อหาจึงหายไป
การแก้ไขและผลกระทบที่ปรากฏในภายหลัง
- การแก้ไขคือให้ client เติมจุดเพิ่มอีกหนึ่งตัวเมื่อบรรทัดขึ้นต้นด้วยจุดและมีอักขระอื่นอยู่ในบรรทัดเดียวกัน
- เมื่อส่งแบบนี้ แม้ SMTP server ฝั่งรับจะลบจุดแรกออก จุดเดิมก็ยังคงอยู่ในเนื้อหา
- เมื่อส่งอีเมลต้นฉบับอีกครั้งจาก local ด้วยข้อมูลผู้รับชุดเดียวกับลูกค้าที่มีปัญหา จุดก็ไม่หายไปอีก
- หลัง deploy การแก้ไขแล้ว เนื่องจากมีการนำโค้ด SMTP client ชุดเดียวกันจากโปรเจกต์ก่อนหน้าไปใช้ จึงแจ้งบั๊กนี้ให้ทีมอื่นทราบด้วย
- ไม่กี่เดือนต่อมา ระบบของอีกทีมยังไม่ได้แพตช์ และได้ส่งอีเมลสำคัญหลายฉบับเพื่อแจ้งเบี้ยประกันรายเดือนใหม่ให้ลูกค้า
- ในอีเมลบางฉบับ จุดคั่นทศนิยม ของเบี้ยประกันรายเดือนไปอยู่พอดีที่ตำแหน่งอักขระแรกของบรรทัดและหายไป
- ผลคือ ลูกค้าบางรายได้รับอีเมลที่ดูเหมือนว่าเบี้ยประกันใหม่ไม่ใช่
$27.00แต่เป็น$2700 - บั๊กนี้ขึ้นอยู่กับความยาวของแต่ละบรรทัดในเนื้อหาอีเมล และเกิดเฉพาะกับลูกค้าบางรายที่ความยาวชื่อและนามสกุลลงตัวพอดี
- เพราะสาเหตุถูกระบุไว้แล้ว โค้ดของทีมนั้นจึงถูกแพตช์ทันที
2 ความคิดเห็น
เหมือนว่าในชื่อเรื่องจะตีความ
periodเป็น "ช่วงเวลา" แทนที่จะเป็น "จุด" นะครับ 555ความคิดเห็นจาก Hacker News
โค้ดนี้บางส่วนกำลังอิมพลีเมนต์ SMTP client เองโดยตรง และต้นเหตุพื้นฐานก็ดูเหมือนจะอยู่ตรงนั้น
การอิมพลีเมนต์โปรโตคอลให้ถูกต้องเป็นเรื่องยาก และบั๊กแบบที่กล่าวถึงในบทความก็พบได้บ่อย
ถ้าเป็นไลบรารี SMTP client ที่อิมพลีเมนต์อย่างถูกต้อง มันก็น่าจะเข้ารหัสตามข้อกำหนดของ SMTP ไม่ว่าจุดจะอยู่ตำแหน่งไหนในข้อความอินพุต และเลเยอร์เทมเพลตก็ไม่จำเป็นต้องสนใจ SMTP
การรับข้อความแล้วเข้ารหัสให้เป็นไปตามข้อกำหนด SMTP ไม่ใช่เรื่องยาก แต่ต้องรู้ตั้งแต่แรกว่าจำเป็นต้องทำขั้นตอนนั้น
ข้อผิดพลาดและช่องโหว่ด้านความปลอดภัยจำนวนมาก เช่น SQL injection และ XSS ก็เกิดจากความผิดพลาดแบบเดียวกัน คือ การต่อสตริงเข้าด้วยกัน
ในคิวรี SQL การ bind ค่ากับเทมเพลตคิวรีควรเกิดขึ้นใน “พื้นที่ SQL” ไม่ใช่พื้นที่สตริงที่ไม่มีชนิดข้อมูล และการสร้างสตริง SQL ที่ถูก serialize เองโดยตรงอย่าง
SELECT * FROM foo WHERE foo.bar =+$userDataเป็นวิธีที่ผิดเทมเพลต HTML ก็เช่นกัน หากจัดการในระดับ document tree แทนการแทนค่าเป็นสตริง ก็จะหลีกเลี่ยงช่องโหว่โง่ ๆ ได้
ถ้าต้องการหลีกเลี่ยงไม่ให้จุดหายไปในอีเมล ก็อย่าฉีดข้อความไร้โครงสร้างเข้าไประหว่างกลางของ SMTP pipeline และควรเคารพระดับ abstraction ที่กำลังทำงานอยู่
ในเรื่องที่เกี่ยวข้อง langsec ก็น่าดูเช่นกัน
เพราะนิยาม “บรรทัดที่มีจุดเพียงตัวเดียว” ให้เป็น control sequence ไม่ใช่บรรทัดตัวอักษรตามจริง จึงเกิดเรื่องแบบนี้ขึ้น
SMTP เป็นตัวอย่างของการออกแบบที่ซับซ้อนเกินจำเป็น และบั๊กในการอิมพลีเมนต์ก็สะท้อนความซับซ้อนนั้น
ไม่ได้แนะนำให้ไปอิมพลีเมนต์เอง แต่ SMTP ก็ไม่ควรยากถึงขนาดที่คนคนเดียวจะอิมพลีเมนต์ให้ถูกต้องได้ลำบาก
ที่เหลือเป็นน้ำหนักตาย แต่ก็ยังลาก dependency เพิ่มมาอีก 20 ตัวเพราะฟังก์ชันที่ไม่ได้ถูกเรียกใช้ แล้วไม่ทันไร codebase ก็พองขึ้นทีละหลาย MB และคุณก็ได้รับแจ้งเตือน CVE ของไลบรารีที่ไม่รู้ด้วยซ้ำว่ากำลังใช้อยู่
ส่วนทางเทคนิคคนอื่น ๆ อธิบายได้ดีกว่าแล้ว แต่มีเรื่องเล่าหนึ่งที่ทำให้นึกได้ว่าสิ่งเล็กน้อยอย่าง จุดหนึ่งตัว ท้ายประโยคสำคัญได้แค่ไหน
ในเยอรมนีที่ผมทำงานอยู่ เป็นเรื่องปกติที่จะขอ “Zeugnis” เมื่อออกจากงาน ซึ่งเป็นจดหมายรับรองที่ระบุหน้าที่ที่ทำและการประเมินพนักงาน และเป็นเอกสารสำคัญที่มักถูกขอตอนสมัครงาน
แน่นอนว่าพนักงานคงไม่ยอมรับข้อความอย่าง “คนนี้ขี้เกียจ อย่าจ้าง” จึงเกิดรหัสชนิดหนึ่งที่เรียกว่า “Zeugnissprache” ซึ่งปลอมตัวมาเป็นคำชม
หนึ่งในรหัสนั้นคือ หากประโยคสุดท้ายไม่มีจุด หมายความว่า “ให้เพิกเฉยต่อทุกอย่างที่เขียนไว้ที่นี่ คนนี้แย่มาก”
หลังออกจากงานครั้งก่อน ผมให้ทนายตรวจ Zeugnis ของผม และแม้การประเมินทั้งหมดจะเป็นบวก แต่ไม่รู้ว่าเพราะความสะเพร่าหรือไม่ ประโยคสุดท้ายกลับไม่มีจุด
ฝ่าย HR ไม่มีแรงจูงใจที่จะสร้างรหัสลับไว้ใช้ร่วมกับคู่แข่งที่อาจเกิดขึ้น และการสอน HR คนใหม่โดยยังรักษาให้เป็นความลับก็ฟังไม่ขึ้น
ตำนานแบบนี้เกิดขึ้นเพราะฝ่าย HR ไม่สามารถเขียนตรง ๆ เกินไปได้ว่าใครบางคนเป็นตัวปัญหา
ถ้าไม่มีเรื่องดี ๆ ให้เขียน ก็จะชมข้อดีธรรมดา ๆ เช่นการมาตรงเวลา ซึ่งใกล้เคียงกับสำนวนแบบ HR ว่า “bless their heart” มากกว่ารหัสลับ
ถ้าถูกฟ้อง ก็ยังมีทางเลี่ยงว่า “ท่านผู้พิพากษา นั่นเป็นคำชมครับ! เขามาตรงเวลาเสมอจริง ๆ!”
ไม่เข้าใจว่าทำไม cron job ที่ส่งอีเมลต้องอิมพลีเมนต์ SMTP client เอง
ใช้โปรแกรมอย่าง
mailของ mailutils ก็พอแล้วแม้มองจากแง่ deliverability การสร้างการโต้ตอบ SMTP แบบโครงกระดูกบน socket เองก็เป็นงานที่เกินตัวตั้งแต่แรก
ทุกวันนี้คุณส่งอีเมลโดยเชื่อมต่อไปยัง mail exchange host ใดก็ได้โดยตรงไม่ได้แล้ว โดยทั่วไปต้องเชื่อมต่อกับ SMTP relay host เฉพาะที่ ISP ให้มา
แบบนั้นก็ต้องอิมพลีเมนต์ทั้งการเชื่อมต่อ TLS, การยืนยันตัวตน และอื่น ๆ
ที่น่าขันเล็กน้อยคือ cron เองก็ส่งอีเมลเป็นอยู่แล้ว
เอาต์พุตของ cron job จะถูกส่งเป็นอีเมลไปยังเจ้าของ และ cron บางตัวสามารถเปลี่ยนที่อยู่ผู้รับได้ด้วยตัวแปรอย่าง
MAILTOใน crontabเหตุผลคือผู้ใช้ที่อยากใช้ “ของชิ้นนั้น” อาจกรอกที่อยู่ SMTP server ได้ แต่โปรแกรมไม่มีทางเชื่อได้ว่า outgoing MTA อย่าง sendmail ถูกตั้งค่าไว้อย่างถูกต้อง
ในบริษัทมีเซิร์ฟเวอร์จำนวนมากที่ส่ง system mail ไม่ได้ และสภาพนั้นก็มักอยู่นอกการควบคุมของทั้งคนเขียนโปรแกรมหรือผู้ใช้
การตั้งค่าเมลเป็นงานเฉพาะทางที่ต้องมีเงื่อนไขล่วงหน้าอย่าง DNS, การเข้ารหัส และนโยบาย จึงเกินจำเป็นมากหากเป้าหมายแค่ให้บางอย่างอย่างแอปกระเป๋าสตางค์ส่งเมลได้
“ติดต่อผู้ดูแลระบบของคุณ” มักไม่ใช่ทางเลือกที่เป็นจริง ไม่ว่าจะในองค์กรใหญ่หรือเล็ก
ไม่ใช่เหตุผลที่ดี แต่เป็นเหตุผลจริง
ถ้าเป็นภาษาทั่วไป แม้ไม่มีใน standard library ก็มักมี ไลบรารี SMTP client ที่ใช้ได้ดีอยู่มากมาย
ในนี้เห็นนิสัยแย่ ๆ ใหญ่ ๆ อยู่สองอย่าง
อย่างแรก ตามที่หลายคนชี้ไว้คือ อย่า implement มาตรฐานแบบลวก ๆ
ถ้าต้อง implement เอง ก็ต้องใส่ความระมัดระวังและความตั้งใจให้พอ หรือไม่ก็ใช้ไลบรารีที่มีอยู่แล้ว
อย่างที่สองคือ อย่า vendor dependency
ไลบรารีที่ใช้ควรอัปเดตอย่างสม่ำเสมอและทันท่วงที ไม่ใช่ทำเฉพาะ “ตอนจำเป็น”
ถ้าอัปเดตล่าช้าหรือหลีกเลี่ยงไปเลย แม้บั๊กที่ upstream แก้ไปแล้ว ก็อาจกลายเป็นปัญหาใหญ่สำหรับคนที่คิดว่าควรอัปเดตก็ต่อเมื่อเห็นปัญหาเองเท่านั้น
แบบนั้นเสถียรภาพของแอปพลิเคชันจะถูกเปิดให้กระทบจากการเปลี่ยนแปลงของ upstream และการเปลี่ยนแปลงนั้นอาจทำให้โค้ดพังได้
อาจไม่ได้รับแพตช์แก้ไขทันที แต่ผมคิดว่าดีกว่าที่จะรู้ว่าผมเปลี่ยนเพราะมีแพตช์ที่ผมต้องการ แทนที่จะนำความไม่เสถียรที่ไม่ต้องการและความเสี่ยงเพิ่มเติมเข้ามา
ผมอยู่ฝ่าย “ถ้ามันยังไม่พัง ก็อย่าไปซ่อม”
ทุกวันนี้ดูเหมือนหลายคนไม่ได้เรียนรู้ โปรโตคอลพื้นฐาน ด้วยการโต้ตอบกับเทอร์มินัลโดยตรง
SMTP ดูเหมือนถูกออกแบบมาโดยคำนึงถึงการโต้ตอบแบบ manual แบบนั้นตั้งแต่แรก และในฐานะคนที่เคยใช้งานแบบนั้นอยู่พักหนึ่ง “บรรทัดที่มีจุดเดียว” ซึ่งใช้จบข้อความนั้นฝังอยู่ในความทรงจำถาวรไปแล้ว
เรื่องการ escape ที่เกี่ยวข้องกันก็ดูเหมือนเป็นแนวคิดที่โปรแกรมเมอร์จำนวนมากไม่คุ้นเคย
เมื่อเห็นสถานการณ์ข้างต้น มีคนจำนวนมากที่ไม่ถามว่า “ถ้าอยากส่งอีเมลที่มีบรรทัดซึ่งมีแค่จุดเดียวล่ะ?” แต่ก็มีคนอีกกลุ่มใหญ่ที่จะรู้สึกว่าคำถามนั้นสมเหตุสมผลและเข้าใจง่ายมาก
ต้องมี dot stuffing
SMTP https://www.rfc-editor.org/rfc/rfc5321#section-4.5.2
POP3 ก็มี https://www.rfc-editor.org/rfc/rfc1939#page-8
นึกถึงประสบการณ์ที่เคย debug การ implement โปรโตคอลเครือข่าย โดยเฉพาะคือ AppleTalk NBP สำหรับคนยุคเก่า
เขียนโค้ดเสร็จหมดแล้ว แต่แพ็กเก็ตของผมถูกปฏิเสธ หรือก็คือถูก drop เงียบ ๆ ส่วนแพ็กเก็ตจาก implementation จริงของ Apple ผ่านได้
ผมเอาแพ็กเก็ตที่ดีและแพ็กเก็ตที่เสียขึ้นจอคอมพิวเตอร์แล้วเทียบกันทีละไบต์ แต่ก็หาปัญหาไม่เจอ และมันเหมือนกันเป๊ะตั้งแต่ต้นจนจบ รวมถึง checksum ด้วย
พอถึงเวลาเลิกงาน เลยตัดสินใจพิมพ์ของงี่เง่าพวกนั้นออกมาไว้ดูทีหลัง แต่ทันทีที่พิมพ์ออกมา ก็เห็นข้อผิดพลาด
เวอร์ชันของผมมีสองหน้า ส่วน implementation ที่ถูกต้องมีหนึ่งหน้า
ปัญหาคือไม่ได้เคลียร์บัฟเฟอร์ให้ถูกต้องก่อนส่งข้อมูล นั่นแหละ mbuf
คิดถึงทีไรก็ยังขำ
บรรทัด “We are happy to welcome you to our family.” ยังไม่เข้าใกล้ ข้อจำกัดความยาวบรรทัด เลย
น่าจะมีอะไรอย่างอื่นเกิดขึ้น เช่น ทั้งหมดอาจเป็นไฟล์แนบ HTML MIME จริง ๆ ก็ได้
อาจเป็นแบบนี้คือมี
... lots of text ...แล้วตามด้วยWe are happy to welcome you to our family.แต่ถ้าตัด HTML เป็นบรรทัดแบบสุ่มสี่สุ่มห้า แท็กจะพัง
บริษัทมีโอกาสสูงที่จะผ่านปัญหาเล็กน้อยนั้นไปโดยไม่รู้ตัวเลย
quoted-printable ควรมีขีดจำกัดสูงสุด 78 อักขระรวม CRLF แต่โดยทั่วไปไคลเอนต์อีเมลค่อนข้างผ่อนปรน
ถ้าเพิ่งเคยได้ยินเรื่อง dot stuffing เป็นครั้งแรก ก็คงยากจะเชื่อว่าในโลกอีเมลยังมีความสยองอะไรอื่นอีก
มีอย่างเช่น header folding, การใส่เครื่องหมายคำพูดให้ local part, และ IPv6 literal
พออ่านถึงตรงที่บอกว่าเป็นโค้ดไคลเอนต์ SMTP ที่ยืมมาจากโปรเจกต์ก่อนหน้า ผมก็คิดทันทีว่าตอนจบคงเป็นทีมอื่นยังไม่ได้แพตช์บั๊กนี้