allow only requests from domain and block other sources - laravel

I want to allow some routes to only respond to requests made by my front-end website, meaning block other sources like postman and allow only the request from domain of the front-end for security reasons.
Is it possible?
for example, I have a webpage to check the dynamic value of the link and verify if the token on link is on database or not, I can think of putting captcha so a bot can't check all possible combinations, but it's not 100% safe.

Laravel API Throttling
if your main problem is bots getting all combinations, the throttling middleware alongside using captchas will help you with that.
By default, all your API routes (in routes/api.php) allow for a maximum of 60 requests per minute per IP. You can modify this amount to your own need in app/Http/Kernel.php file by changing the throttle:api section:
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
changing it to throttle:30:1 for example will mean you will allow 30 requests per minute per ip.
if you only want some routes on your api to be throttled, you can use the middleware elsewhere:
Route::get('my-method', MyController::class)->middleware('throttle:30:1');
Create a middleware that limits hosts
if you want to limit exactly by domain, what you are looking for is probably a custom middleware. Middlewares allow you to inspect various request properties (including the request's host through $request->getHost()) and prevent any controllers or methods.
Although Laravel's default TrustHosts middleware provides a global host validation, you could create your own custom middleware for specific paths that would look like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class LocalOnly
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if($request->getHost() != 'localhost')
{
return response('', 400);
}
return $next($request);
}
}
Note: if you are creating new middlewares, you will need to register them. Laravel has its own guide on this here.
in this example, when used on any route, Laravel will reject any host other than localhost (so even 127.0.0.1 will be rejected)
Personally, I don't recommend doing this as the built-in throttling is a much more elegant solution but in case you really need to do it, here you go.

You can simply do this by creating middleware. I added some more efficient way to add restrications.
1- Create Middleware ApiBrowserRestricationMiddleware
2- Add this \App\Http\Middleware\ApiBrowserRestricationMiddleware::class in
App\Http\Kernel $middleware array
3- Add the code in ApiBrowserRestricationMiddleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ApiBrowserRestricationMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if(in_array($request->getHost(), ['127.0.0.1']) == false)
{
return response('', 400);
}
return $next($request);
}
}

Related

How do I block certain routes when not in Debug Mode in Laravel?

Is there a way to make a route APP_DEBUG exclusive in Laravel 8?
I can set routes in the PreventRequestsDuringMaintenance middleware exception list.
But that is only for when maintenance mode is on.
I know I can simply do an abort(403) on a Route if Debug mode is on but I'm using Laravel Web Console library which communicates with it's own route when executing commands. So I need to strictly block any requests to that route when in Debug mode.
I want to block certain routes when not in Debug mode. Does Laravel come with such option or do I need a third party library?
As James suggested it, I tinkered with middlewares and registered a global middleware to abort 403 if any uri matches one from a blacklist.
protected $middleware = [
...
// allows blocking some requests when debug mode is off
\App\Http\Middleware\PreventRequestsDuringProduction::class,
];
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
class PreventRequestsDuringProduction
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$blacklist = [
'console',
'laravelwebconsole/execute'
];
$uri = Route::getRoutes()->match($request)->uri;
if(\in_array($uri, $blacklist) && !env('APP_DEBUG')) {
abort(403);
}
return $next($request);
}
}

Laravel - limiting users for number of posts

Is there any way to limit users in my project for number of posts. For example, I want my users can create a maximum 10 posts each one. So one user has 10 posts? Is it something with hasMany or something else? Please help find a solution. Thank you
By definition
Middleware provide a convenient mechanism for filtering HTTP requests
entering your application. For example, Laravel includes a middleware
that verifies the user of your application is authenticated. If the
user is not authenticated, the middleware will redirect the user to
the login screen. However, if the user is authenticated, the
middleware will allow the request to proceed further into the
application.
To prevent user from adding more than 10 posts you need to create a middleware to protect your posts/create route
To create a new middleware, use the make:middleware Artisan command:
php artisan make:middleware CheckUserPostsNumber
This command will place a new CheckUserPostsNumber class within your app/Http/Middleware directory. In this middleware, we will only allow access to the posts/create route if the user posts number < than 10. Otherwise, you will redirect the user back to the home URI:
<?php
namespace App\Http\Middleware;
use Illuminate\Support\Facades\Auth;
use Closure;
class CheckUserPostsNumber
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::user()->posts->count() >= 10) {
return redirect('home');
}
return $next($request);
}
}
Assigning Middleware To Routes
you would like to assign middleware to specific routes, you should first assign the middleware a key in your app/Http/Kernel.php file. By default, the $routeMiddleware property of this class contains entries for the middleware included with Laravel. To add your own, append it to this list and assign it a key of your choosing:
// Within App\Http\Kernel Class...
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
//...
'checkUserPostsNumber' => 'App\Http\Middleware\checkUserPostsNumber'
];
Once the middleware has been defined in the HTTP kernel, you may use the middleware method to assign middleware to a route:
Route::get('posts/create', function () {
//
})->middleware('auth', 'checkUserPostsNumber');
Docs
if ($user->posts->count() >= 10) {
//
}

What is use of middleware in Laravel?

I'm not clear with the concept of middleware in Laravel. What does laravel middleware do? Please provide an example if possible.
Middleware is something that is placed between two requests.
Suppose that you need to make sure that when user access to a specific group of routes he/she is authenticated.
There are two option:
Add in every controller the code to check if user is logged in ( in this example we do not consider a parent controller )
Use a middleware
In the first case you should write in each controller the same code.
With the middleware you have a piece of code that you can re-use in multiple section of your application.
Let's suppose that we want to create a Middleware that need to check if the user is logged in:
namespace App\Http\Middleware;
use Closure;
class UserIsLoggedIn
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!auth()->user()) {
return redirect('home');
}
return $next($request);
}
}
Now with this code we can check our user where we need.
First of all since this is a custom middleware you need to register it in the app/Http/Kernel.php file in the $routeMiddleware property:
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
// ...
'isLoggedIn => \App\Http\Middleware\UserIsLoggedIn::class,
];
Let's assume that you have a group of routes that need to check the user is logged in:
Route::get('admin/profile', function () {
//
})->middleware('isLoggedIn');
Now all the routes in this group will check if the user is logged otherwise he will be redirect to home.
Now assume that you have another controller that need to make sure that the user is logged in, now you can re-use the middleware to do that:
class MyController extend Controller {
function __construct(){
$this->middleware('isLoggedIn');
}
}
So middleware help you to organize the login and re-use pieces of code for specific tasks.
Laravel has a lot of documentation about middleware that you can find here

Laravel 5.2 - event handling - log a user login via remember me

I am using the default laravel 5.2 authorization library.
I am using the remember me functionality with a 24 hour time out.
I need to log user_id with IP address in an audit table after each user visit to the webste.
I have done this using the EventServiceProvider, listening for the login event
and then using the request object to identify the IP and persisting to the database.
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
This works for all logins where the login screen is used with username and password supplied by the user.
However if the user accesses the website via the session cookie (ie- login within 24 hours of the previous login) the login is not recorded. So it must follow a different path within the authorization library.
In the API documentation (https://laravel.com/api/5.2/Illuminate/Auth/Events.html) there is no event like for example 'LogInViaCookie'.
I have tried adding a method to AuthController-
/**
* Add audit.
*
* #param $request
* #param $user
*/
protected function authenticated(Request $request, $user)
{
try
{
$audit = Audit::create(['internet_protocol' => $request->ip,
'uid' => $user->id,
'general' => 'viaCookie']);
$audit->save();
}catch(\Exception $e){
Log::error($e->getMessage());
}
}
This authenticated method, as I understand it, should be fired from the AuthenticatesUsers trait (line 115)-
if (method_exists($this, 'authenticated')) {
return $this->authenticated($request, Auth::guard($this->getGuard())->user());
}
However, it appears this method is not fired when logging in via cookie.
How can I listen for and capture this type of 'LogInViaCookie' event to update my audit table?
UPDATE 30/05/16 21:21
Rifki- That sounds like a good solution. I've tried implementing but I can't get it to work.
This is my viaCookie.php-
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class viaCookie
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::viaRemember()) {
dd('logged in via cookie');
}
return $next($request);
}
}
And my Kernel.php update-
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\viaCookie::class,
],
Am I missing something? dd isn't being fired.

How to prevent Laravel Routes from being accessed directly (i.e. non-ajax requests)

In my project, I am using Laravel purely as a backend api and all frontend is handled by Angular javascript. At the moment, the Laravel routes can be accessed directly and it will cough out all the data in Json that shows in the browser. I want to put a restriction on it so Laravel only responds to Ajax requests and nothing else.
I read this post here which has a solution for Laravel 4 that is by adding a restriction in filter.php. But as of Laravel 5.1, filters are no longer used and I believe Middleware can be used to do the same. However, I am not sure how to go ahead changing the Laravel 4 solution in that SO answer from filter to Middleware.
Can someone share your ideas on how to prevent Laravel 5.1 routes from being accessed directly please?
Laravel 4 solution using filter.php:
In filter.php declare this filter:
Route::filter('isAJAX', function()
{
if (!Request::AJAX()) return Redirect::to('/')->with(array('route' => Request::path()));
});
Then put all your routes that you only want accessible via AJAX into a group. In your routes.php:
Route::group(array('before' => 'isAJAX'), function()
{
Route::get('contacts/{name}', ContactController#index); // Or however you declared your route
... // More routes
});
Create the middleware file app/Http/Middleware/OnlyAjax.php with this content:
<?php
namespace App\Http\Middleware;
class OnlyAjax
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, \Closure $next)
{
if ( ! $request->ajax())
return response('Forbidden.', 403);
return $next($request);
}
}
Then register your middleware in the file app/Http/Kernel.php
<?php namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
protected $middleware = [
//... your original code
];
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
//... your original code
'ajax' => \App\Http\Middleware\OnlyAjax::class,
];
}
And finally attach the middleware to any route or group of routes you want to make only accessible via AJAX. i.e:
/// File: routes/web.php
// Single route
Route::any('foo', 'FooController#doSomething')->middleware('ajax');
// Route group
Route::middleware(['ajax'])->group(function () {
// ...
});

Resources