{"id":"GHSA-v5ch-g375-82r6","kind":"vulnerability","aliases":["GHSA-v5ch-g375-82r6"],"related":[],"title":"Temporary signed URL expiration bypass","description":"### Impact\r\n\r\nWhen PHP's `max_input_vars` limit causes the `expires` query parameter to be dropped from the parsed query, Laravel's signed URL validation can still verify the signature against the raw query string. `signatureHasNotExpired()` then treats the URL as non-expiring, so an expired temporary signed URL may be accepted. Exploitation requires a request line long enough to exceed `max_input_vars`, which depends on server and proxy limits.\r\n\r\n### Patches\r\n\r\nThis issue has been patched in Laravel 12.72.0 and 13.41.0: https://github.com/laravel/framework/pull/62349. Users should upgrade to one of these versions or later.\r\n\r\n### Workarounds\r\n\r\n- Limit URL length at the web server. Default nginx and Apache settings already reject the long URL the attack needs.\r\n- If web server configuration is unavailable, add a middleware ahead of `signed` that returns an error when the raw query has more pairs than `max_input_vars` or when `expires` appears in the raw query but is missing from the parsed one.","summary":"When a long request exceeds PHP's max_input_vars limit, the expires parameter can be dropped from the parsed query while the signature is still checked against the raw query string. An attacker can then reuse an expired temporary signed URL and have it accepted.","severity":"medium","cvss":4.8,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"laravel/framework","fixed":["12.72.0"],"affected":"< 12.72.0","ecosystem":"composer"},{"name":"laravel/framework","fixed":["13.41.0"],"affected":">= 13.0.0, < 13.41.0","ecosystem":"composer"}],"ecosystems":["composer"],"tags":["laravel","package","php","signed-urls"],"not_affected_if":["no-signed-urls","short-url-limit"],"how_to_check":"- Check the Laravel version in `composer.lock` for `laravel/framework`.\n- Check the PHP `max_input_vars` setting with `php -i | grep max_input_vars`.\n- Check whether the app uses the `signed` middleware on routes that rely on expiring URLs.","fix":"- `laravel/framework` `12.72.0` for the 12.x line\n- `laravel/framework` `13.41.0` for the 13.x line","status":"open","published_at":"2026-11-11T13:52:08+00:00","first_seen_at":"2026-11-11T14:01:45+00:00","triaged_at":"2026-11-11T14:02:12+00:00","updated_at":"2026-11-11T14:02:12+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-11T14:02:12Z","model":"claude-haiku-5-5","output":{"fix":"- `laravel/framework` `12.72.0` for the 12.x line\n- `laravel/framework` `13.41.0` for the 13.x line","reach":"internet","title":"Laravel: expired signed URLs can be accepted when query parameters are dropped","impact":"medium","summary":"When a long request exceeds PHP's max_input_vars limit, the expires parameter can be dropped from the parsed query while the signature is still checked against the raw query string. An attacker can then reuse an expired temporary signed URL and have it accepted.","audience":"developers","category":"package","nicknames":[],"who_cares":["laravel","php","signed-urls"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"expired signed URLs are accepted, granting access they should not","analyst_note":"","how_to_check":"- Check the Laravel version in `composer.lock` for `laravel/framework`.\n- Check the PHP `max_input_vars` setting with `php -i | grep max_input_vars`.\n- Check whether the app uses the `signed` middleware on routes that rely on expiring URLs.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-signed-urls","short-url-limit"],"who_is_affected":"Applications built on laravel/framework that use signed temporary URLs (the `signed` middleware with `expires`) are exposed. The risk needs a request line long enough to exceed PHP's max_input_vars, which depends on server and proxy limits. Apps that do not use temporary signed URLs, or whose web server rejects long URLs, are less exposed.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.00078,"input_tokens":4873,"output_tokens":586,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'/laravel/framework/pull/62349.':155C '12.72.0':150C '13.41.0':152C '82r6':10A,15A 'accept':69B,122C 'add':193C 'ahead':196C 'alreadi':179C 'apach':177C 'appear':217C 'attack':57B,185C 'bypass':5A 'caus':78C 'check':50B 'configur':190C 'default':174C 'depend':136C 'drop':40B,85C 'enough':129C 'error':202C 'exceed':28B,131C 'expir':4A,36B,62B,80C,113C,116C,216C 'exploit':123C 'g375':9A,14A 'ghsa':7A,12A 'ghsa-v5ch-g375-82r6':6A,11A 'github.com':154C 'github.com/laravel/framework/pull/62349.':153C 'impact':70C 'input':32B,75C,133C,212C 'issu':144C 'laravel':18B,90C,149C 'laravel/framework':16A,17A 'later':165C 'length':169C 'limit':34B,77C,141C,167C 'line':127C 'long':26B,128C,182C 'max':31B,74C,132C,211C 'may':120C 'middlewar':195C 'miss':224C 'need':186C 'nginx':175C 'non':112C 'non-expir':111C 'one':160C,228C 'packag':19B 'pair':209C 'paramet':37B,82C 'pars':43B,88C,227C 'patch':142C,147C 'php':20B,29B,72C 'proxi':140C 'queri':44B,54B,81C,89C,103C,206C,221C 'raw':53B,102C,205C,220C 'reject':180C 'request':27B,126C 'requir':124C 'return':200C 'reus':60B 'server':138C,173C,189C 'set':178C 'sign':2A,22B,64B,92C,118C,198C 'signatur':47B,99C 'signaturehasnotexpir':105C 'signed-url':21B 'still':49B,96C 'string':55B,104C 'temporari':1A,63B,117C 'treat':107C 'unavail':192C 'upgrad':158C 'url':3A,23B,65B,93C,109C,119C,168C,183C 'user':156C 'v5ch':8A,13A 'valid':94C 'var':33B,76C,134C,213C 'verifi':97C 'version':163C 'web':172C,188C 'workaround':166C","nicknames":[],"mitigations":[],"who_affected":"Applications built on laravel/framework that use signed temporary URLs (the `signed` middleware with `expires`) are exposed. The risk needs a request line long enough to exceed PHP's max_input_vars, which depends on server and proxy limits. Apps that do not use temporary signed URLs, or whose web server rejects long URLs, are less exposed.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["laravelframework"],"ptokens":["framework","laravel"],"labels":["composer","laravel","medium","package","php","signedurls"],"campaign_type":null,"search_a":"'82r6':10A,15A 'bypass':5A 'expir':4A 'g375':9A,14A 'ghsa':7A,12A 'ghsa-v5ch-g375-82r6':6A,11A 'laravel/framework':16A,17A 'sign':2A 'temporari':1A 'url':3A 'v5ch':8A,13A","search_b":"'accept':69B 'attack':57B 'check':50B 'drop':40B 'exceed':28B 'expir':36B,62B 'input':32B 'laravel':18B 'limit':34B 'long':26B 'max':31B 'packag':19B 'paramet':37B 'pars':43B 'php':20B,29B 'queri':44B,54B 'raw':53B 'request':27B 'reus':60B 'sign':22B,64B 'signatur':47B 'signed-url':21B 'still':49B 'string':55B 'temporari':63B 'url':23B,65B 'var':33B","search_c":"'/laravel/framework/pull/62349.':155C '12.72.0':150C '13.41.0':152C 'accept':122C 'add':193C 'ahead':196C 'alreadi':179C 'apach':177C 'appear':217C 'attack':185C 'caus':78C 'configur':190C 'default':174C 'depend':136C 'drop':85C 'enough':129C 'error':202C 'exceed':131C 'expir':80C,113C,116C,216C 'exploit':123C 'github.com':154C 'github.com/laravel/framework/pull/62349.':153C 'impact':70C 'input':75C,133C,212C 'issu':144C 'laravel':90C,149C 'later':165C 'length':169C 'limit':77C,141C,167C 'line':127C 'long':128C,182C 'max':74C,132C,211C 'may':120C 'middlewar':195C 'miss':224C 'need':186C 'nginx':175C 'non':112C 'non-expir':111C 'one':160C,228C 'pair':209C 'paramet':82C 'pars':88C,227C 'patch':142C,147C 'php':72C 'proxi':140C 'queri':81C,89C,103C,206C,221C 'raw':102C,205C,220C 'reject':180C 'request':126C 'requir':124C 'return':200C 'server':138C,173C,189C 'set':178C 'sign':92C,118C,198C 'signatur':99C 'signaturehasnotexpir':105C 'still':96C 'string':104C 'temporari':117C 'treat':107C 'unavail':192C 'upgrad':158C 'url':93C,109C,119C,168C,183C 'user':156C 'valid':94C 'var':76C,134C,213C 'verifi':97C 'version':163C 'web':172C,188C 'workaround':166C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-11T13:52:08+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-v5ch-g375-82r6","url":"https://github.com/laravel/framework/security/advisories/GHSA-v5ch-g375-82r6","severity":"medium","published_at":"2026-11-11T13:52:08+00:00","modified_at":"2026-11-11T13:52:08+00:00","first_seen_at":"2026-11-11T14:01:45+00:00","fetched_at":"2026-11-11T14:47:03+00:00","note":null}],"documents":[{"id":126032,"url":"https://github.com/laravel/framework/security/advisories/GHSA-v5ch-g375-82r6","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}