Skip to content

Commit 22a8b2b

Browse files
committed
Use more standardized rate limiting in Laravel; apply limits to auth routes
1 parent f77932a commit 22a8b2b

File tree

3 files changed

+96
-43
lines changed

3 files changed

+96
-43
lines changed

app/Http/Kernel.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class Kernel extends HttpKernel
112112
'bindings' => SubstituteBindings::class,
113113
'recaptcha' => VerifyReCaptcha::class,
114114
'node.maintenance' => MaintenanceMiddleware::class,
115-
116115
// API Specific Middleware
117116
'api..key' => AuthenticateKey::class,
118117
];

app/Providers/RouteServiceProvider.php

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace Pterodactyl\Providers;
44

5+
use Illuminate\Http\Request;
56
use Illuminate\Support\Facades\Route;
7+
use Illuminate\Cache\RateLimiting\Limit;
8+
use Illuminate\Support\Facades\RateLimiter;
69
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
710

811
class RouteServiceProvider extends ServiceProvider
@@ -17,43 +20,86 @@ class RouteServiceProvider extends ServiceProvider
1720
protected $namespace = 'Pterodactyl\Http\Controllers';
1821

1922
/**
20-
* Define the routes for the application.
23+
* Define your route model bindings, pattern filters, etc.
2124
*/
22-
public function map()
25+
public function boot()
2326
{
24-
Route::middleware(['web', 'auth', 'csrf'])
25-
->namespace($this->namespace . '\Base')
26-
->group(base_path('routes/base.php'));
27-
28-
Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
29-
->namespace($this->namespace . '\Admin')
30-
->group(base_path('routes/admin.php'));
31-
32-
Route::middleware(['web', 'csrf'])->prefix('/auth')
33-
->namespace($this->namespace . '\Auth')
34-
->group(base_path('routes/auth.php'));
35-
36-
Route::middleware(['web', 'csrf', 'auth', 'server', 'node.maintenance'])
37-
->prefix('/api/server/{server}')
38-
->namespace($this->namespace . '\Server')
39-
->group(base_path('routes/server.php'));
40-
41-
Route::middleware([
42-
sprintf('throttle:%s,%s', config('http.rate_limit.application'), config('http.rate_limit.application_period')),
43-
'api',
44-
])->prefix('/api/application')
45-
->namespace($this->namespace . '\Api\Application')
46-
->group(base_path('routes/api-application.php'));
47-
48-
Route::middleware([
49-
sprintf('throttle:%s,%s', config('http.rate_limit.client'), config('http.rate_limit.client_period')),
50-
'client-api',
51-
])->prefix('/api/client')
52-
->namespace($this->namespace . '\Api\Client')
53-
->group(base_path('routes/api-client.php'));
54-
55-
Route::middleware(['daemon'])->prefix('/api/remote')
56-
->namespace($this->namespace . '\Api\Remote')
57-
->group(base_path('routes/api-remote.php'));
27+
$this->configureRateLimiting();
28+
29+
$this->routes(function () {
30+
Route::middleware(['web', 'auth', 'csrf'])
31+
->namespace("$this->namespace\\Base")
32+
->group(base_path('routes/base.php'));
33+
34+
Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
35+
->namespace("$this->namespace\\Admin")
36+
->group(base_path('routes/admin.php'));
37+
38+
Route::middleware(['web', 'csrf'])->prefix('/auth')
39+
->namespace("$this->namespace\\Auth")
40+
->group(base_path('routes/auth.php'));
41+
42+
Route::middleware(['web', 'csrf', 'auth', 'server', 'node.maintenance'])
43+
->prefix('/api/server/{server}')
44+
->namespace("$this->namespace\\Server")
45+
->group(base_path('routes/server.php'));
46+
47+
Route::middleware(['api', 'throttle:api.application'])
48+
->prefix('/api/application')
49+
->namespace("$this->namespace\\Api\\Application")
50+
->group(base_path('routes/api-application.php'));
51+
52+
Route::middleware(['client-api', 'throttle:api.client'])
53+
->prefix('/api/client')
54+
->namespace("$this->namespace\\Api\\Client")
55+
->group(base_path('routes/api-client.php'));
56+
57+
Route::middleware(['daemon'])->prefix('/api/remote')
58+
->namespace("$this->namespace\\Api\\Remote")
59+
->group(base_path('routes/api-remote.php'));
60+
});
61+
}
62+
63+
/**
64+
* Configure the rate limiters for the application.
65+
*/
66+
protected function configureRateLimiting()
67+
{
68+
// Authentication rate limiting. For login and checkpoint endpoints we'll apply
69+
// a limit of 10 requests per minute, for the forgot password endpoint apply a
70+
// limit of two per minute for the requester so that there is less ability to
71+
// trigger email spam.
72+
RateLimiter::for('authentication', function (Request $request) {
73+
if ($request->route()->named('auth.post.forgot-password')) {
74+
return Limit::perMinute(2)->by($request->ip());
75+
}
76+
77+
return Limit::perMinute(10);
78+
});
79+
80+
// Configure the throttles for both the application and client APIs below.
81+
// This is configurable per-instance in "config/http.php". By default this
82+
// limiter will be tied to the specific request user, and falls back to the
83+
// request IP if there is no request user present for the key.
84+
//
85+
// This means that an authenticated API user cannot use IP switching to get
86+
// around the limits.
87+
RateLimiter::for('api.client', function (Request $request) {
88+
$key = optional($request->user())->uuid ?: $request->ip();
89+
90+
return Limit::perMinutes(
91+
config('http.rate_limit.client_period'),
92+
config('http.rate_limit.client')
93+
)->by($key);
94+
});
95+
96+
RateLimiter::for('api.application', function (Request $request) {
97+
$key = optional($request->user())->uuid ?: $request->ip();
98+
99+
return Limit::perMinutes(
100+
config('http.rate_limit.application_period'),
101+
config('http.rate_limit.application')
102+
)->by($key);
103+
});
58104
}
59105
}

routes/auth.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@
1515
Route::get('/password', 'LoginController@index')->name('auth.forgot-password');
1616
Route::get('/password/reset/{token}', 'LoginController@index')->name('auth.reset');
1717

18-
// Login endpoints.
19-
Route::post('/login', 'LoginController@login')->middleware('recaptcha');
20-
Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.login-checkpoint');
18+
// Apply a throttle to authentication action endpoints, in addition to the
19+
// recaptcha endpoints to slow down manual attack spammers even more. 🤷‍
20+
//
21+
// @see \Pterodactyl\Providers\RouteServiceProvider
22+
Route::middleware(['throttle:authentication'])->group(function () {
23+
// Login endpoints.
24+
Route::post('/login', 'LoginController@login')->middleware('recaptcha');
25+
Route::post('/login/checkpoint', 'LoginCheckpointController')->name('auth.login-checkpoint');
2126

22-
// Forgot password route. A post to this endpoint will trigger an
23-
// email to be sent containing a reset token.
24-
Route::post('/password', 'ForgotPasswordController@sendResetLinkEmail')->middleware('recaptcha');
27+
// Forgot password route. A post to this endpoint will trigger an
28+
// email to be sent containing a reset token.
29+
Route::post('/password', 'ForgotPasswordController@sendResetLinkEmail')
30+
->name('auth.post.forgot-password')
31+
->middleware('recaptcha');
32+
});
2533

2634
// Password reset routes. This endpoint is hit after going through
2735
// the forgot password routes to acquire a token (or after an account

0 commit comments

Comments
 (0)