security: add HTTP security headers, rate limiting on sensitive routes, and file upload extension validation
This commit is contained in:
@@ -94,7 +94,7 @@ class CartController extends Controller
|
||||
$validated = $request->validate([
|
||||
'model_name' => 'nullable|string|max:255',
|
||||
'model_url' => 'nullable|url|max:1000',
|
||||
'model_file' => 'nullable|file|max:51200', // 50MB
|
||||
'model_file' => 'nullable|file|extensions:stl,3mf,obj,step,stp,zip|max:51200', // 50MB
|
||||
'size' => 'required|in:klein,medium,groot',
|
||||
'material' => 'required|string',
|
||||
'color' => 'required|string',
|
||||
|
||||
@@ -106,7 +106,7 @@ class ShopController extends Controller
|
||||
'preferred_material' => 'nullable|string|max:50',
|
||||
'preferred_color' => 'nullable|string|max:50',
|
||||
'dimensions' => 'nullable|string|max:100',
|
||||
'model_file' => 'nullable|file|max:51200', // max 50MB (stl, step, obj, zip)
|
||||
'model_file' => 'nullable|file|extensions:stl,3mf,obj,step,stp,zip|max:51200', // max 50MB (stl, step, obj, zip)
|
||||
]);
|
||||
|
||||
$filePath = null;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class AddSecurityHeaders
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request and append modern security headers.
|
||||
*
|
||||
* @param Closure(Request): (Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Middleware\AddSecurityHeaders;
|
||||
use App\Http\Middleware\CheckWebshopOffline;
|
||||
use App\Http\Middleware\EnsureUserIsAdmin;
|
||||
use Illuminate\Foundation\Application;
|
||||
@@ -16,6 +17,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
$middleware->trustProxies(at: '*');
|
||||
$middleware->web(append: [
|
||||
AddSecurityHeaders::class,
|
||||
CheckWebshopOffline::class,
|
||||
]);
|
||||
$middleware->alias([
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
<span>Over deze 3D print</span>
|
||||
</h3>
|
||||
<div class="text-xs text-slate-600 leading-relaxed space-y-1.5 font-medium">
|
||||
{!! nl2br($product->description) !!}
|
||||
{!! nl2br(strip_tags($product->description, '<p><br><strong><b><em><i><u><ul><ol><li><h3><h4>')) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+10
-6
@@ -20,8 +20,12 @@ use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// Health & Diagnostics
|
||||
// Health & Diagnostics (Alleen toegankelijk in debug-modus of voor ingelogde admins)
|
||||
Route::get('/health-debug', function () {
|
||||
if (! config('app.debug') && ! (auth()->check() && auth()->user()->isAdmin())) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$dbPath = config('database.connections.sqlite.database');
|
||||
|
||||
$diagnostics = [
|
||||
@@ -78,23 +82,23 @@ Route::get('/winkelwagen/aantal', [CartController::class, 'count'])->name('cart.
|
||||
|
||||
// Afrekenen & Bevestiging
|
||||
Route::get('/afrekenen', [CheckoutController::class, 'index'])->name('checkout.index');
|
||||
Route::post('/afrekenen', [CheckoutController::class, 'store'])->name('checkout.store');
|
||||
Route::post('/afrekenen', [CheckoutController::class, 'store'])->name('checkout.store')->middleware('throttle:10,1');
|
||||
Route::get('/bestelling-ontvangen/{orderNumber}', [CheckoutController::class, 'success'])->name('checkout.success');
|
||||
|
||||
// Authenticatie & Klantenportaal
|
||||
Route::middleware('guest')->group(function () {
|
||||
Route::get('/inloggen', [LoginController::class, 'showLoginForm'])->name('login');
|
||||
Route::post('/inloggen', [LoginController::class, 'login'])->name('login.submit');
|
||||
Route::post('/inloggen', [LoginController::class, 'login'])->name('login.submit')->middleware('throttle:5,1');
|
||||
Route::redirect('/login', '/inloggen');
|
||||
|
||||
Route::get('/registreren', [RegisterController::class, 'showRegistrationForm'])->name('register');
|
||||
Route::post('/registreren', [RegisterController::class, 'register'])->name('register.submit');
|
||||
Route::post('/registreren', [RegisterController::class, 'register'])->name('register.submit')->middleware('throttle:6,1');
|
||||
Route::redirect('/register', '/registreren');
|
||||
|
||||
Route::get('/wachtwoord-vergeten', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request');
|
||||
Route::post('/wachtwoord-vergeten', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email');
|
||||
Route::post('/wachtwoord-vergeten', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email')->middleware('throttle:3,1');
|
||||
Route::get('/wachtwoord-resetten/{token}', [ForgotPasswordController::class, 'showResetForm'])->name('password.reset');
|
||||
Route::post('/wachtwoord-resetten', [ForgotPasswordController::class, 'reset'])->name('password.update');
|
||||
Route::post('/wachtwoord-resetten', [ForgotPasswordController::class, 'reset'])->name('password.update')->middleware('throttle:5,1');
|
||||
});
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
|
||||
@@ -303,4 +303,12 @@ class AdminContentTest extends TestCase
|
||||
$guestOnlineResponse = $this->get(route('home'));
|
||||
$guestOnlineResponse->assertStatus(200);
|
||||
}
|
||||
|
||||
public function test_site_sends_modern_security_headers(): void
|
||||
{
|
||||
$response = $this->get('/');
|
||||
$response->assertHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response->assertHeader('X-Content-Type-Options', 'nosniff');
|
||||
$response->assertHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user