The Tracking-Link Bug That Breaks Signed URLs

A bug can be dangerous if it only breaks the things that matter most.

I found a bug in mail-history that works perfectly for normal links but fails for signed URLs. It breaks email verification links and signed download links. These are the links where one wrong character causes Laravel to reject the request.

Here is how it happened.

mail-history tracks email clicks by rewriting your HTML. It changes every link to route through a redirect endpoint first. This endpoint records the click and then sends the user to the real destination.

To do this, the system encrypts the original URL into a tracking link.

The problem starts when the system pulls the URL from the rendered HTML. By the time the code reads the email body, Laravel has already escaped the HTML. An ampersand (&) in a link becomes &.

A normal link like https://example.com/page works fine. It has no ampersands.

But a signed URL looks like this: https://example.com/email/verify/1/abc?expires=123&signature=deadbeef

In the HTML, it becomes: https://example.com/email/verify/1/abc?expires=123&signature=deadbeef

The code encrypts that & string. When the user clicks, the system decrypts it and sends them to a URL containing &. Laravel tries to verify the signature, but the signature fails because the characters do not match the original.

The fix is a single line of code. You must decode the HTML entities before you encrypt the URL.

$originalUrl = html_entity_decode($matches[2], ENT_QUOTES | ENT_HTML5);

This turns & back into & before the encryption happens. The decrypted link now matches the original signed URL byte for byte.

I also added a test to prevent this from happening again. The test checks that the decrypted URL contains a real & and not &.

Small fixes like this are easy to lose during future code cleanups. Always write a test that names the specific failure.

Lessons for you:

  • If you extract data from rendered HTML, assume it is escaped.
  • A browser will fix escaped characters for you. Encryption and redirects will not.
  • Use tests to protect one-line fixes.

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