Laravel Signed Routes throws InvalidSignatureException in production - php

Whenever I use visit a URL created with temporarySignedRoute() on production, it throws InvalidSignatureException. However, when in dev environment, it works. I have also tried using signedRoute() with no luck.
I am creating my URLs like this:
\URL::temporarySignedRoute(
'user.activate',
now()->addHours(200),
[ 'id' => $user->id ]
);
Example of URL: https://xx.xx/activate/5?expires=1537065074&signature=f57713b2fccea5ce17b7c1a3241e275d62f1994cd8004ea480cced2402081090
I have made sure that the APP_KEY is set. I'm not really sure what step I should take in order to fix this. I am using Laravel Forge for deployment. I am also using a Load Balancer but only one app server atm.
Why is temporarySignedRoute() throwing InvalidSignatureException in production?
Edit:
Logging the request, I get this data:
// [ $expired, $url, $query, $original, $signature ]
[
false,
"http://xx.xx/activate/13",{"expires":"1537076308","signature":"9d972a574137bf7e5f30120c3f539a81bca71875020c3476e3bfb230220a73f7"},
"http://xx.xx/activate/13?expires=1537076308",
"ed80a6ae717e806fd5f887224c2ac88f5fffb893ae284cc52688ba0690d3a9e1"
]
Notice that the URL starts with http:// but the actual link generated is using https://. The URL is logged with request()->url(). Which leads me to believe that the problem lies in the http-scheme.
In my AppServiceProvider.php I am forcing https like this:
if (env('APP_ENV') == 'production') {
\URL::forceScheme('https');
}
Why is request() receiving it as non-http?

This was caused by my load balancer not sending HTTPS calls to the server. Meaning the server was hashing a different URL-scheme (http) than the original (https).

I ran into the same issue and Fredrik's answer helped.
When I dumped the request while generating the signed url, I saw that my server was requesting from "http" rather than "https" even though the site was served over "https". I had to change my Cloudflare settings to force https on requests...

Related

Symfony 5.3, Mercure and Vue -> SSL connect error for "https://localhost:8000/.well-known/mercure"

I'm trying to make a publish with the Hub Interface but everytime I get this error
SSL connect error for "https://localhost:8000/.well-known/mercure"
I'm running mercure with the Symfony Local Web Server, so mercure it's running on https://localhost:8000/.well-known/mercure. I have checked and it is running but I can't manage to make a publish. I'm making this call from a controller which is called from Vue.
$update = new Update(
[
sprintf("/conversaciones/%s", $conversacion->getId()),
sprintf("/conversaciones/%s", $recipiente->getUsuario()->getNombre()),
],
$mensajeSerializado,
true
);
$this->hubInterfacePublisher->publish($update);
Some comment on other question from Stackoverflow mentioned this:
Mercure with symfony not working with vue
Mecure dont support self signed certificates and you have to add verify_peer: false in config/dev/framework.yaml under http_client.default_configuration
Tryed it, but didn't work for me. Also, for what I have read, it is not recommended to set it false HTTP Certificates
I also tryed to change https to http on my .env, but I got a 401 Unauthorized.
I have checked on symfonycast tutorials and they have the .env enviroments on localhost:8000 so I think that can't be it, but I don't know what else I can try.
For what I know it has to do with CORS. But don't know how to fix it.
Any help is appreciated,
Thank you :)

Really force http to https in Laravel

I have this Laravel App which I'm deplying to Heroku.
I have followed all of the steps until I encountered a problem relating some assets (asset('css/app.css'), for example) refering to http urls, instead of https urls.
I solved that by adding
if(config('app.env')==='production'){
\URL::forceScheme('https');
}
in the boot method of my AppServiceProvider.php file, and it worked.
But now I have encountered another http related problem that the previous code couldn't solve.
I am fetching my data using simplePaginate() function like so
public function index(Question $question){
$answers = $question->answers()->with('user');
return $answers->simplePaginate(3);
}
This code returns me a response with my 3 answers, as well as with a property called 'next_page_url'
which is, still, plain http (not https as i need it to be).
What can I do for this to be https as Heroku requires?
Heroku's load balancing setup means the indication of whether the request is HTTP or HTTPS comes from the X-Forwarded-Proto header. (Laravel also needs the X-Forwarded-For header to get the users' real IP addresses, incidentally.)
By default, Laravel doesn't trust these headers (as in a different setup it might come from a malicious client), so none of the requests will be detected as HTTPS. You can fix this by configuring the Laravel trusted proxy to trust the header.
In the default config, just setting $proxies = '*', will do the trick, and is safe on Heroku because the load balancers can't be bypassed by end users.
The correct way is to change the URL of your app to https://example.com in the configuration file (.env file as an example). Just write APP_URL=https://example.com
But, when you use Heroku - their balancers can route your requests to https://yourDomain.com to your application over HTTP. So, the Laravel app receives the request to http://yourDomain.com and decides that you need a response with HTTP links.
As #seejayoz said you need to configure trusted proxies list for your app.
I think you can use withPath (or setPath alias) :
$pagi=$answers->simplePaginate(3);
$pagi->withPath("https://link/xxx/");
return $pagi;

Laravel 419 Error - VerifyCsrfToken issue

I have multiple Laravel sites hosted on the same server. With the latest site I've created, the contact form refuses to submit without throwing a 419 error. I have set up the routing in my web.php file just like the other websites, which have live, working contact forms, and I'm generating and sending the token exactly the same way - with {{ csrf_field() }}.
I found an answer to a similar question stating that you can disable Csrf checking by adding entries to the $except array in app/Http/Middleware/VerifyCsrfToken.php. I have verified that this does indeed resolve the 419 error:
protected $except = [
'contact',
'contact*',
];
But of course I wish to keep the Csrf functionality, and I only updated the $except array for troubleshooting value.
Does anyone know what may be different about the new Laravel environment that would have this 419 behavior despite passing the generated token? I have tried updating a number of ENV settings and toggling different things, but nothing other than modifying the $except array has had any influence on the issue.
Update
Since there has been a bit of discussion so far, I figured I'd provide some additional info and code.
First, this is an ajax form, but don't jump out of your seat just yet. I have been testing the form both with and without ajax. If I want to test with ajax, I just click the button that's hooked up to the jQuery listener. If not, I change or remove the button's ID, or run $("#formName").submit(); in the console window.
The above (ajax, old-fashioned submit, and the jquery selector with .submit();) all result in the exact same response - a 419 error.
And for the sake of completeness, here's my ajax code which is working on all of the other websites I'm hosting. I define a postData array to keep it all tidy, and I added a console.log() statement directly after it to (again) confirm that token is generated just fine and is being passed correctly with the request.
var postData = {
name: $("#name").val(),
email: $("#email").val(),
message: $("#message").val(),
_token: $("input[name=_token]").val()
};
console.log(postData);
$.post("/contact", postData, function (data) {
...
Any ideas? Could there be a configuration issue with my ENV or another file?
Progress Update!
Because the other sites are working just fine, I cloned an old site and simply overwrote the files that I changed for the new website, and bam! It's working now. Doing a little bit more digging, I ran php artisan --version on the cloned version of the site versus the non-working version, and here are the results:
Working Version: Laravel Framework 5.7.3
Non-working Version: Laravel Framework 5.7.9
Perhaps this is a bug with Laravel? Or perhaps some packages on my server are out of date and need to be updated to work with the new version of Laravel?
TLDR: This post contains lots of potential issues and fixes; it is intended for those scouring for related bonus information when stuck.
I just encountered this error using Laravel Sanctum in what looks like improperly setup middleware. Sanctum uses the auth:sanctum middleware for the guard, which is some kind of extension of the auth guard of which Laravel uses as the default, but session is handled by the web middleware group.
I can't exactly verbalize some of this internal-Laravel stuff; I am more experienced with JavaScript than PHP at the moment.
In my api.php file, I had the login/register/logout routes, and in my Kernel.php file, I copied \Illuminate\Session\Middleware\StartSession::class, from the web group into the api group.
I had to do that to fix my login unit test that was throwing an error about "Session store not on request". Copying that allowed me my postJson request to work in the unit test, but sometime later, I started seeing 419 CSRF error posting from the JavaScript app (which is bad because it worked fine earlier).
I started chasing some filesystem permission red-herring in the /storage/framework/sessions folder, but the issue wasn't that (for me).
I later figured out that with Laravel Sanctum and the default AuthenticatesUsers trait, you must use the web guard for auth, and the auth:sanctum middleware for protected routes. I was trying to use the api guard for auth routes and that was central to my 419 errors with the AuthenticatesUsers trait.
If anyone gets 419 while CSRF was working or should work, I recommend doing some \Log::debug() investigations at all the key points in your system where you need these to work:
Auth::check()
Auth::user()
Auth::logout()
If you get strange behaviour with those, based on my observations, there is something wrong with your config related to sessions or something wrong with your config related to web, api guards.
The guards have bearing on the AuthManager guard which maintains state over multiple requests and over multiple unit tests.
This is the best description I found, which took over a week for me to discover:
Method Illuminate\Auth\RequestGuard::logout does not exist Laravel Passport
As a random final example, if your session is somehow generating the CSRF token using data from the web middleware group while your routes are set to use api, they may interpret the received CSRF incorrectly.
Besides that, open Chrome dev tools and goto the Applications tab, and look at the cookies. Make sure you have the XSRF-TOKEN cookie as unsecure (ie: not httpOnly).
That will allow you to have an Axios request interceptor such as this:
import Cookies from 'js-cookie';
axios.interceptors.request.use(async (request) => {
try {
const csrf = Cookies.get('XSRF-TOKEN');
request.withCredentials = true;
if (csrf) {
request.headers.common['XSRF-TOKEN'] = csrf;
}
return request;
} catch (err) {
throw new Error(`axios# Problem with request during pre-flight phase: ${err}.`);
}
});
That is how my current Laravel/Vue SPA is working successfully.
In the past, I also used this technique here:
app.blade.php (root layout file, document head)
<meta name="csrf-token" content="{{ csrf_token() }}">
bootstrap.js (or anywhere)
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
const token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
In my opinion, most problems will stem from an incorrect value in one or more of these files:
./.env
./config/auth.php
./config/session.php
Pay close attention to stuff like SESSION_DOMAIN, SESSION_LIFETIME, and SESSION_DRIVER, and like I said, filesystem permissions.
Check your nginx access.log and/or error.log file; they might contain a hint.
just found your issue on the framework repo.
It is not a laravel issue, your installation is missing write permissions on the storage folder, thus laravel can't write session, logs, etc.
You get a 419 error because you can't write to the files, thus you can't create a sessionn, thus you can't verify the csrf token.
Quick fix: chmod -R 777 storage
Right fix: move your installation to a folder where nginx/apache/your user can actually write.
If you are using nginx/apache, move you app there and give the right permissions on the project (chown -R www-data: /path-to-project)
If you are using php artisan serve, change it's permissions to your user: chown -R $(whoami) /path-to-project
You get it, let writers write and you're good.
Probably your domain in browser address bar does not match domain key in config/session.php config file or SESSION_DOMAIN in your env file.
I had the same issue, but the problem in my case was https. The form was on http page, but the action was on https. As a result, the session is different, which is causing the csrf error.
run this command
php artisan key:generate
I used the same app name for staging and prod, being staging a subdomain of prod. After changing name of app in staging it worked
We had this issue, it turned out that our sessions table wasn't correct for the version of Laravel we were using. I'd recommend looking to see if it's being populated or remaining empty (like ours was).
If it's empty, even when you have people visiting the site, I'd say that's what the issue is.
(If you're not using a database to store your sessions, obviously I'd suggest checking wherever you are instead.)

Route with dot (IP address) not found, returns 404

I use Lumen 5.4.
This is how my route is setup:
$app->get('/ip/{ip}', GeoIpController::class . '#show');
The {ip} route parameter should be an IP address, with dots in it. However, it seems there is a problem when a route has dots in it. It returns a 404 not found error.
I am aware I could pass the IP address in as a simple GET request parameter, but want the IP to be part of the URL and to be handled like a route parameter.
For testing purposes, I use php -S localhost:8080 -t public to serve the application.
This is a limitation on PHP's built in server, not with Lumen (or Laravel, or Slim, or any other frameworks/apps with a router). You can view the PHP bug report here.
Basically, if the URL has a dot in the url after the script name, the built-in server treats the request as a static file request, and it never actually attempts to run through the application.
This request should work fine on a real web server (apache, nginx), but it will fail when run on PHP's built-in development web server.

Queued route() urls has wrong domain in URL

I have a Laravel 5.1 application that has many clients with their own unique subdomains and databases.
On app loading, my middlewares resolves the client and sets the app.url (config/app.php) accordingly. It all works great, in the browser. All urls generated by route() has the correct subdomain for current client.
But, stuff queued (Redis in my case) will always defaults to a domain URL of "localhost".
So if I send a welcome email where the text template contains route('account') it will from the queue generate a "http://localhost/account" URL. This is of course not correct.
I've found the line that probably does this, it's in the Illuminate\Foundation\Bootstrap\SetRequestForConsole class:
$url = $app->make('config')->get('app.url', 'http://localhost'); <---
As far as I can see, I can't really "hook" into anything before that.
Info: For each Queued command (closure) I have, I always send with it who the client is that's queing. That way I set the client before the queued command is fired. It loads the right database connection. But changing the default route() server name appears rather difficult!
I've experimented with extending the UrlGenerator class, but it appears that this is completely ignored for queued commands and only works on HTTP requests.
I've also tried adding this before the queues command is fired:
app('url')->forceRootUrl($client->getClientUrl());
It did not work. (but does work with HTTP requests)
Anyone here have an idea for how to set my own default domain for route() in CLI mode?
In your environment config (.env) file, add an entry:
APP_URL='http://www.example.com'
Then in your config/app.php file change the application url from localhost:
----------------
Application URL
----------------
...
...
//'url' => 'http://localhost',
'url' => env('APP_URL'),
...
These two changes worked for me when I used the database queue driver in Laravel 5.1.
I case you have sub domain and your want to make route url with sub-domain so in this case there might be problem.
I have used this in my app. config/app.php file
...
...
//'url' => 'http://localhost',
'url' => url('/'),
...

Categories