บั๊กของ Tracking-Link ที่ทำให้ Signed URLs ใช้งานไม่ได้

บั๊กสามารถซ่อนอยู่ในโค้ดของคุณได้เป็นเวลานาน มันทำงานได้ปกติกับลิงก์ส่วนใหญ่ มันเงียบสนิทกับลิงก์การตลาด แต่กลับทำให้ลิงก์ที่สำคัญที่สุดพังลง

ผมพบข้อผิดพลาดนี้ในเครื่องมือของผมที่ชื่อ mail-history มันทำให้ Signed URLs ใช้งานไม่ได้ ซึ่ง Signed URLs เหล่านี้คือลิงก์ที่ใช้สำหรับการยืนยันอีเมลหรือการดาวน์โหลดที่ปลอดภัย

นี่คือสิ่งที่เกิดขึ้น

เครื่องมือของผมจะเขียน HTML ของอีเมลใหม่เพื่อติดตามการคลิก โดยจะนำ URL ต้นฉบับมาเข้ารหัส (encrypt) และเปลี่ยนให้เป็นลิงก์ติดตาม เมื่อผู้ใช้คลิก เครื่องมือจะถอดรหัส (decrypt) URL และส่งพวกเขาไปยังปลายทางที่แท้จริง

ปัญหาเริ่มขึ้นเมื่อผมดึง URL ออกมาจาก HTML ที่ถูกเรนเดอร์ (rendered HTML) แล้ว

Laravel และ Blade จะทำการ escape HTML attributes โดยเครื่องหมาย ampersand (&) ใน URL จะกลายเป็น HTML entity ซึ่งจะเปลี่ยนจาก & เป็น &

สำหรับลิงก์ทั่วไป เบราว์เซอร์จะแก้ไขเรื่องนี้ให้โดยอัตโนมัติ คุณจึงไม่สังเกตเห็นความแตกต่างเลย

แต่เครื่องมือของผมไม่ได้ส่งลิงก์ไปยังเบราว์เซอร์ มันส่งสตริง (string) ไปยังฟังก์ชันถอดรหัส

หาก Signed URL ของคุณมีลักษณะดังนี้: https://example.com/verify?expires=123&signature=abc

HTML จะมีลักษณะดังนี้: https://example.com/verify?expires=123&signature=abc

เครื่องมือของผมเข้ารหัสเวอร์ชันที่มี & เมื่อผู้ใช้คลิก เครื่องมือจะถอดรหัสและส่งพวกเขาไปยังเวอร์ชันที่มี &

Laravel เห็น & และคิดว่า signature ไม่ถูกต้อง ทำให้การยืนยันล้มเหลว

วิธีแก้ไขใช้โค้ดเพียงบรรทัดเดียว ผมใช้ html_entity_decode เพื่อเปลี่ยน & กลับเป็น & ก่อนที่จะเริ่มการเข้ารหัส

สิ่งนี้ช่วยให้มั่นใจได้ว่า URL ที่ถอดรหัสแล้วจะตรงกับ Signed URL ต้นฉบับแบบ byte-for-byte

บทเรียนสำหรับงานของคุณ:

  • หากคุณดึงข้อมูลจาก HTML ที่ถูกเรนเดอร์มาใช้ในฟังก์ชัน ให้สันนิษฐานไว้ก่อนว่าข้อมูลนั้นถูก escape แล้ว
  • เบราว์เซอร์อาจให้อภัยความผิดพลาดได้ แต่การเข้ารหัสและการตรวจสอบ signature ไม่เช่นนั้น
  • เมื่อคุณใช้การแก้ไขด้วยโค้ดเพียงบรรทัดเดียว ให้เขียน test สำหรับมันด้วย

ผมเขียน test ที่ตรวจสอบการมีอยู่ของ & โดยเฉพาะ หากนักพัฒนาในอนาคตลบการแก้ไขของผมออกในระหว่างการทำความสะอาดโค้ด (cleanup) test จะล้มเหลวทันที

Source: https://dev.to/nasrulhazim/the-tracking-link-bug-that-only-breaks-signed-urls-38c