I've spent a ton of time trying to fix this but haven't had any luck so far. My app isn't loading css because of a mixed content error (The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure stylesheet 'http://example.com/assets/css/magazine.min.css'. This request has been blocked; the content must be served over HTTPS). I know that I can load the assets by passing in true to the asset function but that would mean I would have to go to all the asset calls and change them. Is there a site wide setting I can configure so that it does https in production and http in local?
Thanks
You can create something like ForceHttps middleware and than create condition for environment inside of it, like this:
public function handle($request, Closure $next)
{
if (!\App::environment('local')) {
\URL::forceSchema('https');
}
return $next($request);
}
Than add it to some route group or globally if you want.
NOTE: I would suggest to resolve this on your web server, not in Laravel
Just use asset() helper to generate asset's URL. It will use the current protocol.
Do NOT force assets to be loaded by https, unless they are sensitive (which is almost never the case). That would be an overhead, because you usually care more of safe website content than safe assets. In other words, if you accept loading http website, you most likely accept http assets. Instead, consider using middleware to redirect http to https on each non-safe request.
This is the middleware I'm using myself:
public function handle($request, Closure $next)
{
if (!$request->secure()) {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
If you wish to use it, please remember to fire it BEFORE attaching any cookies, that is before Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse middleware.
I created app/Helpers/SiteHelpers.php containing a function that overrides the default asset() function.
<?php
/**
* Overrides the default asset() method, which generates an asset path for the application.
*
* #param string $path
* #param bool $secure
*
* #return string
*/
function asset ($path, $secure = null) {
if (Request::server('HTTP_X_FORWARDED_PROTO') == 'https' || Request::server('HTTPS') == 'on') {
$secure = TRUE;
}
return app('url')->asset($path, $secure);
}
then added it on bootstrap/autoload.php above require __DIR__.'/../vendor/autoload.php'; so it would look like below:
require __DIR__.'/../app/Helpers/SiteHelpers.php';
require __DIR__.'/../vendor/autoload.php';
this is flexible depending on whether you are serving your static content on http or https
Related
I am coding a Laravel API hosted on api.url.com for an application hosted on www.myurl.com on a server different to the Lravel API using Laravel Fortify.
The problem comes when the user verifies their email on the generated link, they are redirected not to the external application but again back to the API, but on their browser.
THe docs state that this configuration in the Authenticate.pgp Middleware would work:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return url(env('SPA_URL') . '/dashboard');
}
}
}
Where SPA_URL is set in the .env to www.myurl.com.
This redirects to api.url.com/www.myurl.com/dashboard Obviously causing a 404 response.
I then tried to call an action on a controller as such
public function redirectDashboard(Request $request){
return redirect()->away('www.myurl.com/dashboard');
}
This again redirects to api.url.com/www.myurl.com/dashboard Obviously causing a 404 response.
I have no clue why redirect()->away would still redirect to a url on the same API domain.
Any help would be appreciated, thank you.
The RedirectTo method will redirect to request.
Try to write new middleware
public function handle($request, Closure $next)
{
//check here if the user is authenticated
if ( ! $this->auth->user() )
{
// here you should redirect to another site
return redirect(env('SPA_URL') . '/dashboard');
}
return $next($request);
}
I finally realised what was happening with the redirect: I was using the redirect() helper function rather than the Redirect::class. The helper function appends the current domain to any url or away method call whereas the Redirect::class allows you to redirect to anywhere you need.
I have a page in my Laravel project which I need to make available for use throughout iframe. Not the whole app but only one single page available for iframing inside only one trusted domain.
I learned that Laravel has a middleware, which protects the whole app from being displayed via iframe, called FrameGuard
class FrameGuard
{
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('X-Frame-Options', 'SAMEORIGIN', false);
return $response;
}
}
But this is the framework's middleware, not project files, so can't change it. Even if I had decided to change it I would have to change handle method to
public function handle($request, Closure $next)
{
$response = $next($request);
//$response->headers->set('X-Frame-Options', 'ALLOW-FROM' https://example.com); obsolate
$response->headers->set('Content-Security-Policy' frame-ancestors 'self' https://www.example.com);
return $response;
}
Since it has now a different header would it be the cause of any problems in the future?
Also, if allowing only a single page to be used in iframe programmatically possible do I have to change other configurations like e.g. Nginx settings?
It feels like a lack of information on this topic on the internet. Any thoughts and contributions will be deeply appreciated.
Problem
I am creating a signed URL through laravels 'temporarySignedRoute' function copied and pasted from laravels signed routes documentation, on my web server the url comes out to a 403 invalid signature. The signed URL's work on my local machine but upon uploading it to my test server it gives the error shown below.
What I've tried
I have been searching for a solution for awhile now and have tried many that have been posted on stack overflow but nothing seems to solve my problem.
I have tried all of the following:
Forcing all routes to https through the AppServiceProvider, this forced all of the url's to be https but gave the same result.
I tried changing Nginx's configuration as described here.
I also tried adding the TrustProxies middleware with the proxies configured to '*' or all, like described in laravel's documentation on Trust Proxies, same result.
Stopped the web server from forcing the domain to https to test if it was just https causing it, same result.
-- UPDATE --
I went into vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php while trying to search around for any clues in the project. I decided to datadump (dd) out the tokens it was comparing on my local and hosted projects and this was the result. Code shown below as well.
public function hasValidSignature(Request $request, $absolute = true)
{
$url = $absolute ? $request->url() : '/'.$request->path();
$original = rtrim($url.'?'.Arr::query(
Arr::except($request->query(), 'signature')
), '?');
$expires = $request->query('expires');
$signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
dd($signature . " :: " . $request->query('signature', ''));
return hash_equals($signature, (string) $request->query('signature', '')) &&
! ($expires && Carbon::now()->getTimestamp() > $expires);
}
Local Project
Hosted Project
So the Token is not being passed through on my web server but is on my local. Also another thing I noticed is that the web servers token is always the same and never changes no matter what.
My current Nginx configuration can be viewed here.
My current virtual hosts configuration is here.
I think this might be the issue.
Try to change your location block in the config file to
try_files $uri $uri/ /index.php?$query_string;
I think you have missed the ? between index and query string.
Update this and make sure to reload your Nginx server.
sudo service nginx reload
Let me know if this works.
The post from Prince Kumar Barnwal fixed my issue back then, I recently rediscovered another version of this issue while attempting to use docker in the same setup and I thought I would post it here in case anyone has the same issue.
We are using a Proxy Server and certifying our docker laravel projects on that, all projects have the TrustProxies setup, then we forceHTTPS in the AppServiceProvider.php which makes AJAX calls work. This causes issues as the project itself is not encrypted but all urls it generates thinks it is since we are forcing it. When the proxy passes the URL in the request it is http, when the project expects HTTPS.
I found a solution to this, its not the most clean and theirs probably a better fix out there but I made a middleware that goes on all of the projects signed routes, basically it just changes the url in the request to be secure if the project is in production. This is not insecure as the proxy handles the security and this is ONLY on the signed routes.
Below is the middleware that goes ONLY on the signed routes.
<?php
namespace App\Http\Middleware;
use Closure;
class SecureSignedURL
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(config('app.env') === 'production' || config('app.env') === 'staging') {
$request->server->set('HTTPS', 'ON');
}
return $next($request);
}
}
My web.php with the middleware, its defined as secure_signed_url in my Kernel.php
Route::group(['middleware' => ['secure_signed_Url', 'signed']], function () {
route::get('/exampleurl1', 'TestController#route_exampleurl1_signed')->name('exampleurl1.signed');
route::get('/exampleurl2', 'Template\TemplateController#route_exampleurl2_signed')->name('exampleurl2.signed');
});
Again I don't know if this is the best fix, this is a very obscure bug in Laravel. If you have any suggestions for a better way of fixing this I'm open to suggestions.
My Old Github Issue that may help: https://github.com/laravel/framework/pull/31434
This is not a Nginx error. That's how laravel handles invalid signed urls if you check the Illuminate\Routing\Middleware\ValidateSignature it throws an Illuminate\Routing\Exceptions\InvalidSignatureException which aborts the request with a 403 status. What I did,
Created a custom signed middleware which extends the actual ValidateSignature
<?php
namespace App\Http\Middleware;
use App\Exceptions\InvalidEmailVerificationException;
use Closure;
use Illuminate\Routing\Middleware\ValidateSignature as MiddleWare;
class ValidateSignature extends Middleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($request->hasValidSignature()) {
return $next($request);
}
throw new InvalidEmailVerificationException;
}
}
which throws a custom App\Exceptions\InvalidEmailVerificationException
<?php
namespace App\Exceptions;
use Symfony\Component\HttpKernel\Exception\HttpException;;
class InvalidEmailVerificationException extends HttpException
{
/**
* Create a new exception instance.
*
* #return void
*/
public function __construct()
{
parent::__construct(403, 'Your email verification token seems to be invalid.');
}
}
then change the signed middleware in your App\Http\Kernel.php $routeMiddleware to 'signed' => \App\Http\Middleware\ValidateSignature::class,
in your 403.blade.php view, you can display the message as
#if ($exception instanceof \App\Exceptions\InvalidEmailVerificationException)
<h1 class="text-ginormous">Snap!</h1>
<h4 class="quick-error-description">Verification token expired.</h4>
#if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
#endif
<p class="error-description">{{ $exception->getMessage() }}</p>
<a href="{{ route('verification.resend') }}" class="button-solid button-error mb-3" data-no-pjax>Email me a new link</a>
#endif
I have a Laravel 5.2 app and inside the public_html folder I have some assets stored in a subfolder called metronic as I am using a theme called metronic and wanted to keep it all bundled in one place to make updating it easier.
I have linked to these assets using the URL::asset() method like so:
<script src="{!! URL::asset('metronic/global/plugins/fullCalendar/fullcalendar/fullcalendar.js') !!}"></script>
This generates the following URL:
http://www.mywebsite.com/metronic/global/plugins/fullCalendar/fullcalendar/fullcalendar.js
For some reason this is working fine locally but when I run it on my production server the request is going through Laravel. I have some middleware that checks whether or not to show the coming soon page and this is being run and so rather than the script being fetched the holding page is just being run. This is the content of the middleware:
<?php namespace App\Http\Middleware;
use Closure;
use Request;
use Route;
use Session;
class HoldingPageCheck {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(env('HOLDING_PAGE')==true && (!$request->is('coming-soon') && !$request->is('member/subscribe') && !$request->is('migrate') && !$request->is('contact') && $request->segment(1)!='admin')) {
Session::reflash();
return redirect(route('holding_page'));
}
return $next($request);
}
}
It doesn't make any sense to me why Laravel would be interfering with an asset request it should just bypass Laravel entirely shouldn't it when a request for a JS file is made?
Found the problem, it was because I was using camel case in the path to the script. Locally with xampp it didn't mind but obviously my live environment was case sensitive.
So I have an api-version middleware that I use in laravel 5 that I am trying to use in lumen. However it uses getAction() and setAction() in the middleware which isn't available to lumen ("yet"). If I do $request->route()[1] I can get the current route but updating that with the new route does no good. I have thought about cloning the request and modifying it but I can't tell what part of the request object I would need to "update".
Here are my routes:
$app->group(['middleware' => ['api-version']], function() use ($app) {
$app->get('users', '{api-namespace}\UsersController#index');
$app->get('vips/{per_page?}', '{api-namespace}\VipsController#index');
$app->get('vip/{id}/profile', '{api-namespace}\VipsController#showProfile');
$app->get('vip/{id}', '{api-namespace}\VipsController#show');
});
Can anyone tell me how i can update the request with my simple route update?
Found this in my search for a solution. Here's what I came up with using middleware in Lumen 5.4.
First, I created an ExtractApiVersionMiddleware that extracts the version from the Accept header. I used the accept header because not all headers passed are trusted and it is becoming "Best Practice": to pass API versions in this manner:
<?php
namespace App\Http\Middleware;
use Closure;
class ExtractApiVersionMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$app = app();
$version = "0";
foreach (explode(';', $request->header('accept')) as $frag) {
if (stristr($frag, "version=")) {
$version = str_replace("version=", "", $frag);
break;
}
}
if ($version != 0) {
$app->group(['namespace' => "App\Http\Controllers\V{$version}\Reductions"],
function () use ($app, $version) {
require base_path() . "/routes/web/v{$version}.php";
});
} else {
$app->group(['namespace' => 'App\Http\Controllers\V0'], function () use ($app, $version) {
require base_path() . "/routes/web/v0.php";
});
}
return $next($request);
}
}
Second, I namespaced my code according to versions (the legacy codebase hasn't died yet).
Third, I pass the version that the call uses in the Accept header
Accept: application/json;version=1
Fourth, I have separate route files for each supported version. So, instead of having a web.php for all my routes, I created a web folder under routes and I placed my version files in there v0.php & v1.php
So, when I make a request, the Middleware extracts the API version and based on the version number enables the appropriate route group. This keeps the versions clean and separate but still enables me to get 'creative' with my Models.
Hope this helps. I had to apply this solution because I wasn't sure about using Dingo API in pre-release form