# Your Temporary Signed URLs May Not Be Expiring

> A Laravel advisory published in June means temporary signed URLs on the local filesystem driver can be parsed ambiguously, so an expired link can keep working and a request can resolve to something you did not intend. Moderate severity, but with an unusually long tail.

**Author:** Matt King | **Published:** August 7, 2026 | **Category:** Vulnerability

---

A signed URL is a promise. It says this link, and only this link, works until this moment and then stops. Applications lean on that promise for invoice downloads, export files, one-time upload targets, and anything else where you want to hand somebody a URL without handing them an account.

GHSA-crmm-hgp2-wgrp, published 8 June 2026, is about that promise being weaker than advertised on the local filesystem driver.

It scores 4.2, which is moderate, and I want to be straight that this is not a drop-everything advisory in the way the [CRLF injection in the email rule](/blog/laravel-email-rule-crlf-injection) was. What earns it a post is the tail. A signature that fails open does not announce itself, and the URLs you handed out last quarter are still out there.

## What the advisory says

Temporary signed URLs generated by the local filesystem driver can be parsed ambiguously. The URL verifies, because the signature is intact, but the path the application resolves from it is not necessarily the path that was signed.

The advisory names three consequences:

Expired temporary URLs remaining valid. This is the one that matters most, because expiry is the entire reason you reached for a temporary URL instead of a permanent one.

Requests resolving to unintended resources. A link issued for one file resolving to a different file.

Upload variants writing to incorrect destinations. If you use temporary signed URLs as upload targets, the write can land somewhere other than intended.

It is filed under CWE-116, improper encoding or escaping of output, which is the giveaway that this is a parsing and normalisation problem rather than a cryptographic one. The signature scheme is fine. The disagreement is about what got signed.

Affected: Laravel 13.0.0 up to but not including 13.12.0, and anything below 12.61.1. Fixed in 13.12.0 and 12.61.1.

## Why moderate undersells the tail

CVSS scores an individual exploitation event. It does not score how long the consequences persist, and that is where this one is unusual.

Most vulnerabilities stop mattering the moment you patch. This one issues artefacts. Every temporary URL your application generated while running an affected version is a token that is already in the world, in browser histories, in email inboxes, in support tickets, in server logs, in whatever chat application your users pasted it into. Patching stops you producing more. It does nothing about the ones you already produced.

If your temporary URLs had a 15 minute expiry and you patch today, the exposure closes almost immediately, and that describes most applications. If you generated 30 day links to customer invoices, you have a month of tokens whose expiry behaviour you can no longer fully vouch for. Same advisory, quite different problem, and the score is the same in both cases.

## Finding out whether you are exposed

Start with the disk configuration, because the advisory only concerns the local driver:

```bash
grep -A6 "'disks'" config/filesystems.php
```

Then find the call sites:

```bash
grep -rn "temporaryUrl\|temporarySignedRoute\|signedRoute\|hasValidSignature" app/ routes/ resources/
```

`Storage::disk('local')->temporaryUrl(...)` is the exact pattern in scope. `URL::temporarySignedRoute(...)` is the routing equivalent and worth reviewing at the same time even though the advisory is specific about the filesystem driver.

The grep that catches people out is the one they do not run. Media library packages, file managers, and admin panels generate these on your behalf, and none of that appears in a search of your own application code. If you have a package that serves private files, check how it builds its URLs.

## Upgrading

```bash
composer require laravel/framework:^13.12.0 --update-with-dependencies
# or on the 12 branch
composer require laravel/framework:^12.61.1 --update-with-dependencies
```

Both of these also carry the email rule fix, so a single upgrade clears both June advisories. That is the sensible move even if you only care about one of them.

Confirm what you ended up with, since a constraint elsewhere in your dependency tree can hold the framework back without saying so:

```bash
composer show laravel/framework | grep versions
composer audit
```

## The awkward part: existing links

Upgrading does not retract anything you already issued.

For short-expiry links this resolves itself within the hour and there is nothing to do. For long-lived links over anything sensitive you have a decision to make, and it is a genuine trade-off rather than an obvious call.

Rotating `APP_KEY` invalidates every signature your application has ever issued, immediately and completely. It also invalidates every encrypted cookie, logs out every session, and breaks anything else you have encrypted with it, so it is disruptive in ways that reach well beyond this advisory. The mechanics, including the parts that break, are in [how to rotate a leaked Laravel APP_KEY without breaking your app](/blog/laravel-app-key-rotation-guide).

The narrower option, if your temporary URLs point at a small set of resources, is to move those files so the paths no longer resolve. Crude, effective, and much less disruptive than a key rotation.

For most applications the honest answer is that the files behind these links are invoices and exports belonging to the person who requested them, the practical risk is low, and patching plus letting the existing links expire is proportionate. Make that decision deliberately rather than by default, and write down why, because that note is what you will want during your next audit.

## Worth doing anyway

Two habits that make this class of issue smaller next time.

Set expiries in minutes, not days. Long expiries are almost always a workaround for a UX problem, usually that the link is emailed and the user might not read the email promptly. A short-lived link plus a regeneration route solves the same problem without leaving month-long tokens lying around.

Do not rely on the signature as your only authorisation check. A signed URL proves the link came from you. It does not prove the person holding it is the person you gave it to, because links get forwarded. For anything genuinely sensitive, check the session as well as the signature. That belt-and-braces approach is what turns an advisory like this from an incident into a note in your changelog, and it is the same reasoning we apply in [Laravel authorization done right](/blog/laravel-authorization-gates-policies).

StackShield tracks the framework version your production deployment reports along with the misconfigurations around it, so a missed patch shows up on a schedule rather than when someone thinks to check. [Run a free scan](/free-scan) to see what yours currently looks like from outside.

---

## Frequently Asked Questions

### What is GHSA-crmm-hgp2-wgrp?

A Laravel framework advisory published on 8 June 2026 describing path confusion in temporary signed URLs generated by the local filesystem driver. It carries a CVSS score of 4.2, rated moderate, and is classified under CWE-116, improper encoding or escaping of output. Affected versions are 13.0.0 up to but not including 13.12.0, and anything below 12.61.1. The fixes are in 13.12.0 and 12.61.1.

### What does path confusion mean for a signed URL?

It means a URL can be parsed differently at verification time than it was at generation time. The signature still validates, but the components the application derives from the URL are not the ones the signer intended. The practical consequences named in the advisory are expired temporary URLs remaining usable, requests resolving to unintended resources, and upload variants writing to the wrong destination.

### Does this affect S3 or other cloud storage drivers?

The advisory is specific to the local filesystem driver. Cloud drivers such as S3 generate their own presigned URLs using the provider SDK rather than Laravel signed-route machinery, so they are not in scope for this issue. That said, plenty of applications use the local driver in staging or for a subset of buckets while using S3 in production, so check per disk rather than assuming your production configuration covers everything.

### How do I know if my application uses temporary signed URLs?

Search your codebase for temporaryUrl, signedRoute, temporarySignedRoute, and hasValidSignature. Storage::temporaryUrl on a local disk is the specific pattern the advisory concerns. Note that packages can generate these on your behalf, so a clean grep of your own code is not a complete answer if you use media library or file management packages.

### Do I need to invalidate existing signed URLs after upgrading?

Upgrading fixes generation and verification going forward, but any URLs already issued remain valid until they expire on their own terms, and the whole point of this advisory is that some of them may not expire as expected. If you have issued long-lived temporary URLs over sensitive files, rotating APP_KEY invalidates every existing signature at once. That is disruptive and breaks other things, so weigh it against how sensitive the exposed files actually are.

