FRAMESCAN
← Blog

Laravel Framework: Temporary Signed URL Path Confusion (GHSA-crmm-hgp2-wgrp) and CRLF Injection in Default Email Rule (GHSA-5vg9-5847-vvmq)

FrameScan · 2026-07-22

TL;DR

Two high-severity issues affect Laravel 13: a temporary signed URL path confusion bug (affects 13.0.0–13.11.x; fixed in 13.12.0) and a CRLF injection in the default email validation rule (affects 13.0.0–13.9.x; fixed in 13.10.0). If you use Laravel 13, upgrade to at least the fixed versions immediately. The most conservative move is to go to 13.12.0 or later. Your next scheduled FrameScan run already checks for these and will alert you if affected.

At-a-glance

ID Impact Severity Affected versions Fixed version
GHSA-crmm-hgp2-wgrp Signed links can be misrouted high >=13.0.0,<13.12.0 13.12.0
GHSA-5vg9-5847-vvmq Email validation allows CRLF injection high >=13.0.0,<13.10.0 13.10.0

GHSA-crmm-hgp2-wgrp — Laravel Framework: Temporary Signed URL Path Confusion

An attacker could cause a signed link to resolve to an unintended path, potentially bypassing protections tied to the expected URL. This can lead to unauthorized access to actions gated by temporary or signed links. Treat this as high priority for apps relying on signed routes for account or resource access.

Technical details

Advisory summary: Temporary Signed URL Path Confusion in laravel/framework. The risk comes from discrepancies between what is signed and what is later matched or resolved. The fix ensures the signature correctly binds to the intended path and parameters.

Illustrative example — not laravel/framework source:

<?php
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;

// Vulnerable pattern (illustrative): concatenating path segments after signing
// The signature covers the base URL, but not a later-added segment.
Route::get('/download/{file}', function (Request $request, string $file) {
    $base = URL::temporarySignedRoute('secure.download.base', now()->addMinutes(10));
    // Anti-pattern: appending user-controlled path after signing
    return redirect($base . '/' . ltrim($file, '/'));
})->name('download');

// Fixed pattern: include every variable path component in the signature
Route::get('/secure-download/{file}', function (Request $request, string $file) {
    // Sign the exact route with all parameters
    $url = URL::temporarySignedRoute('secure.download', now()->addMinutes(10), [
        'file' => $file,
    ]);
    return redirect($url);
})->name('secure.download');

// On the receiving end, enforce validation of the signature
Route::get('/secure-download/{file}', function (Request $request, string $file) {
    // Request only proceeds if signature is valid
    return response()->download(storage_path('app/' . $file));
})->middleware('signed')->name('secure.download');

The fixed approach signs the full, intended route and uses the signed middleware to reject tampered paths.

GHSA-5vg9-5847-vvmq — Laravel Framework: CRLF injection in default email rule

If a crafted value passes the default email validation and is later used in email-related contexts, newline characters could allow header injection. That can lead to spoofed headers or unintended recipients. Prioritize updating if you validate user emails and pass them to mailers.

Technical details

Advisory summary: CRLF injection in the default email rule in laravel/framework. The issue centers on the validator allowing carriage return/line feed characters, which can break email header boundaries. The fixed version tightens validation to block such control characters.

Illustrative example — not laravel/framework source:

<?php
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Mail;
use Illuminate\Http\Request;

// Before: relying on a permissive rule and passing value to mail headers
function sendInvite(Request $request): void {
    $data = $request->only(['email']);

    // Potentially permissive validation
    $validator = Validator::make($data, [
        'email' => ['required', 'email'],
    ]);

    if ($validator->fails()) {
        abort(422, 'Invalid email');
    }

    // Risk: using un-sanitized value directly in headers
    Mail::raw('Welcome!', function ($message) use ($data) {
        $message->to($data['email']);
    });
}

// After: upgraded framework (13.10.0+) rejects CRLF; additionally sanitize defensively
function sendInviteSafe(Request $request): void {
    $data = $request->only(['email']);

    // Stricter validation remains illustrative; rely primarily on the fixed framework
    $validator = Validator::make($data, [
        'email' => ['required', 'email'],
    ]);

    if ($validator->fails()) {
        abort(422, 'Invalid email');
    }

    // Defensive hardening: strip control chars before use
    $cleanEmail = preg_replace("/[\r\n]+/", '', $data['email']);

    Mail::raw('Welcome!', function ($message) use ($cleanEmail) {
        $message->to($cleanEmail);
    });
}

Upgrading to the fixed version closes the validator gap; the additional sanitation is a belt-and-suspenders measure.

Remediation

If you are on Laravel 13 and affected by both ranges, upgrading directly to 13.12.0 satisfies both fixes.

Call to action

Is your site exposed? Scan it for $1