Laravel security best practices for authn/authz, validation, CSRF, mass assignment, file uploads, secrets, rate limiting, and secure deployment.
Laravel uygulamalarını yaygın güvenlik açıklarına karşı korumak için kapsamlı güvenlik rehberi.
VerifyCsrfToken, güvenlik başlıkları için SecurityHeaders).auth:sanctum, $this->authorize, policy middleware).UploadInvoiceRequest).RateLimiter::for('login')).URL::temporarySignedRoute + signed middleware).APP_DEBUG=falseAPP_KEY ayarlanmalı ve tehlikeye girdiğinde döndürülmelidirSESSION_SECURE_COOKIE=true ve SESSION_SAME_SITE=lax ayarlayın (veya hassas uygulamalar için strict)SESSION_HTTP_ONLY=true ayarlayınSESSION_SAME_SITE=strict kullanınÖrnek route koruması:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum')->get('/me', function (Request $request) {
return $request->user();
});
Hash::make() ile parolaları hash'leyin ve asla düz metin saklamayınuse Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
$validated = $request->validate([
'password' => ['required', 'string', Password::min(12)->letters()->mixedCase()->numbers()->symbols()],
]);
$user->update(['password' => Hash::make($validated['password'])]);
$this->authorize('update', $project);
Route seviyesi zorlama için policy middleware kullanın:
use Illuminate\Support\Facades\Route;
Route::put('/projects/{project}', [ProjectController::class, 'update'])
->middleware(['auth:sanctum', 'can:update,project']);
$fillable veya $guarded kullanın ve Model::unguard() kullanmaktan kaçınınDB::select('select * from users where email = ?', [$email]);
{{ }}){!! !!} sadece güvenilir, temizlenmiş HTML için kullanınVerifyCsrfToken middleware'ini etkin tutun@csrf ekleyin ve SPA istekleri için XSRF token'ları gönderinSanctum ile SPA kimlik doğrulaması için, stateful isteklerin yapılandırıldığından emin olun:
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost')),
final class UploadInvoiceRequest extends FormRequest
{
public function authorize(): bool
{
return (bool) $this->user()?->can('upload-invoice');
}
public function rules(): array
{
return [
'invoice' => ['required', 'file', 'mimes:pdf', 'max:5120'],
];
}
}
$path = $request->file('invoice')->store(
'invoices',
config('filesystems.private_disk', 'local') // bunu public olmayan bir disk'e ayarlayın
);
throttle middleware'i uygulayınuse Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->ip()),
Limit::perMinute(5)->by(strtolower((string) $request->input('email'))),
];
});
Bekleyen hassas sütunlar için encrypted cast'leri kullanın.
protected $casts = [
'api_token' => 'encrypted',
];
Başlıkları ayarlamak için örnek middleware:
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
final class SecurityHeaders
{
public function handle(Request $request, \Closure $next): Response
{
$response = $next($request);
$response->headers->add([
'Content-Security-Policy' => "default-src 'self'",
'Strict-Transport-Security' => 'max-age=31536000', // tüm subdomain'ler HTTPS olduğunda includeSubDomains/preload ekleyin
'X-Frame-Options' => 'DENY',
'X-Content-Type-Options' => 'nosniff',
'Referrer-Policy' => 'no-referrer',
]);
return $response;
}
}
config/cors.php'de origin'leri kısıtlayın// config/cors.php
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
'allowed_origins' => ['https://app.example.com'],
'allowed_headers' => [
'Content-Type',
'Authorization',
'X-Requested-With',
'X-XSRF-TOKEN',
'X-CSRF-TOKEN',
],
'supports_credentials' => true,
];
use Illuminate\Support\Facades\Log;
Log::info('User updated profile', [
'user_id' => $user->id,
'email' => '[REDACTED]',
'token' => '[REDACTED]',
]);
composer audit çalıştırınGeçici, kurcalamaya dayanıklı bağlantılar için signed route'ları kullanın.
use Illuminate\Support\Facades\URL;
$url = URL::temporarySignedRoute(
'downloads.invoice',
now()->addMinutes(15),
['invoice' => $invoice->id]
);
use Illuminate\Support\Facades\Route;
Route::get('/invoices/{invoice}/download', [InvoiceController::class, 'download'])
->name('downloads.invoice')
->middleware('signed');