Laravel 5.7 signed route returns 403 invalid signature Laravel 5.7 signed route returns 403 invalid signature laravel laravel

Laravel 5.7 signed route returns 403 invalid signature


After debugging UrlGenerator::hasValidSignature(), i ended by DD the variables inside UrlGenerator.php like this:

public function hasValidSignature(Request $request, $absolute = true)    {        $url = $absolute ? $request->url() : '/'.$request->path();        //dd($url);        $original = rtrim($url.'?'.Arr::query(            Arr::except($request->query(), 'signature')        ), '?');        dd($original);        $expires = Arr::get($request->query(), 'expires');        $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));        return  hash_equals($signature, (string) $request->query('signature', '')) &&               ! ($expires && Carbon::now()->getTimestamp() > $expires);    }

the $original variable showed me what was actually happening with my URL, and showed this:

https://example.com/report/1/1?expires=1546586977&settings%5Bincrementing%5D=1&settings%5Bexists%5D=1&settings%5BwasRecentlyCreated%5D=0&settings%5Btimestamps%5D=1&profile%5Bincrementing%5D=1&profile%5Bexists%5D=1&profile%5BwasRecentlyCreated%5D=0&profile%5Btimestamps%5D=1&user%5Bincrementing%5D=1&user%5Bexists%5D=1&user%5BwasRecentlyCreated%5D=0&user%5Btimestamps%5D=1

as you can see there are parameters after the expires parameter, those parameter where aded after the route creation, and that was the problem, this happened because i had a middleware sharing some information to the views like this:

UserDataMiddleware.php<?phpnamespace App\Http\Middleware;use Closure;use Illuminate\Support\Facades\Auth;use App\User;use App\Setting;use App\UserProfile;use Illuminate\Support\Facades\View;class UserData{    /**     * Handle an incoming request.     *     * @param  \Illuminate\Http\Request  $request     * @param  \Closure  $next     * @return mixed     */    public function handle($request, Closure $next)    {        if (Auth::check()) {            $settings = Setting::where('user_id', Auth::user()->id)->first();            $profile = UserProfile::where('user_id', Auth::id())->first();            $user = Auth::user();            View::share('settings', $settings); //Another way to share variables, with the View::share            View::share('profile', $profile);            //Now we need to share owr variables trough the REQUEST to our controllers            $request->merge([                'settings' => $settings,                'profile' => $profile,                'user' => $user            ]);        }        return $next($request);    }}

this middleware was inside the middleware groups, so that was the problem hopefully if someone in the future experiments this, then it could check that first.


Try below code:

class TrustProxies extends Middleware{    protected $proxies = '*';    protected $headers = Request::HEADER_X_FORWARDED_ALL;}


I just had this problem and turns out that empty parameters in the URL will never validate. So when you do this:

URL::temporarySignedRoute('newsletter.verify', now()->addDays(3), ['name' => $name, 'email' => $email])

but name is an empty string (because it's not mandatory), URL will get generated with name= as part of query string, but this code inside Laravel

$original = rtrim($url.'?'.Arr::query(Arr::except($request->query(), 'signature')), '?');

will not return the empty name, hence the URL was 'altered' and validation fails. The commonly used middleware ConvertEmptyStringsToNull might have something to do with this.