Zabezpiecz swoją aplikację Laravel: OWASP Top 10 + Więcej

Laravel jest szybki, potężny i elegancki — ale czy Twoje aplikacje są bezpieczne?

Niezależnie czy jesteś juniorem, czy samodzielnym twórcą wdrażającym aplikacje Laravel + Inertia.js, zrozumienie OWASP Top 10 i innych realnych zagrożeń bezpieczeństwa jest kluczowe.

Ten przewodnik przeprowadzi Cię przez:

  • ✅ OWASP Top 10 — z prawdziwymi przykładami dla Laravel + React
  • ✅ Jak naprawić każdy problem (Gate, Policy, Middleware, Fortify, Sanctum itd.)
  • ✅ Dodatkowe zagrożenia spoza OWASP (równie niebezpieczne)
  • ✅ Ostateczną Checklistę Bezpieczeństwa Produkcyjnego Laravel

📚 Spis treści


Czym jest OWASP?

Open Web Application Security Project (OWASP) to fundacja non-profit, której celem jest poprawa bezpieczeństwa oprogramowania. Ich flagowy projekt — OWASP Top 10 — to lista 10 najpoważniejszych zagrożeń bezpieczeństwa aplikacji webowych.

Laravel daje Ci świetne narzędzia do zapobiegania większości z nich, ale musisz ich używać poprawnie.


OWASP Top 10 z przykładami dla Laravel


1. Naruszenie Kontroli Dostępu (A01)

Problem: Użytkownicy uzyskują dostęp do danych lub funkcji, do których nie powinni mieć dostępu.

// ❌ Trasa admina dostępna dla każdego zalogowanego
Route::get('/admin/users', [UserController::class, 'index']);

// ✅ Użyj Policy
$this->authorize('viewAny', User::class);

// ✅ Przykład Policy
public function viewAny(User $user)
{
    return $user->is_admin;
}

// Lub Gate:
Gate::define('access-admin-panel', fn(User $user) => $user->is_admin);

Wskazówka: Dla bardziej zaawansowanej lub wielokrotnie wykorzystywanej logiki dostępu najlepiej stworzyć własny middleware. W Laravel 12 rejestracja middleware została przeniesiona z app/Http/Kernel.php do bootstrap/app.php.

Jak utworzyć i zarejestrować własny middleware w Laravel 12:

  1. Utwórz middleware:
php artisan make:middleware EnsureUserIsAdmin
  1. Zarejestruj alias middleware w pliku bootstrap/app.php:
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(...)
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,
        ]);
    })
    // ...
    ->create();
  1. Użyj aliasu middleware w trasach lub kontrolerach:
// W routes/web.php
Route::middleware(['admin'])->group(function () {
    Route::get('/admin/users', [UserController::class, 'index']);
});

// Lub na pojedynczej trasie
Route::get('/admin/dashboard', [DashboardController::class, 'index'])->middleware('admin');

// Lub w konstruktorze kontrolera
public function __construct()
{
    $this->middleware('admin');
}

Uwaga: Jeśli aktualizujesz projekt z Laravel 10 lub starszego, pamiętaj, że rejestracja middleware odbywa się teraz w bootstrap/app.php, a nie w app/Http/Kernel.php.


2. Błędy Kryptograficzne (A02)

Problem: Wrażliwe dane, takie jak hasła czy tokeny, są przechowywane w niezaszyfrowanej formie.

// ❌ Nigdy nie przechowuj surowego hasła
User::create(['password' => $request->password]);

// ✅ Zawsze używaj hash()
use Illuminate\Support\Facades\Hash;

User::create([
    'password' => Hash::make($request->password),
]);

// 🔐 Użyj też Crypt::encryptString() dla tokenów lub sekretów API
use Illuminate\Support\Facades\Crypt;
$encrypted = Crypt::encryptString($token);

Lepsza praktyka: Zamiast ręcznie szyfrować i odszyfrowywać dane, możesz użyć Eloquent Attribute Casting. W Laravelu 11 i nowszych używaj metody casts() zamiast właściwości $casts:

// W modelu User.php
protected function casts(): array
{
    return [
        'api_token' => 'encrypted',
        'settings' => 'encrypted:array', // dla tablicy
    ];
}

Dzięki temu Laravel automatycznie zaszyfruje i odszyfruje pole przy zapisie/odczycie.

Uwaga: Od Laravel 11+ casty atrybutów definiuje się przez metodę casts(), a nie przez właściwość $casts. To daje większą elastyczność i czytelność modeli. Zobacz oficjalną dokumentację po więcej przykładów i zaawansowane użycie.

Co warto haszować lub szyfrować?

  • Hasła użytkowników (haszować — zawsze przez Hash::make())
  • Tokeny API, refresh tokeny (szyfrować — np. przez cast 'encrypted')
  • Klucze API, sekrety integracji
  • Wrażliwe dane użytkownika (np. PESEL, NIP, adresy, jeśli wymagają dodatkowej ochrony)
  • Dane konfiguracyjne, które nie powinny być jawne w bazie

Pamiętaj:

  • Haszowanie (np. haseł) jest jednokierunkowe — nie można odzyskać oryginału.
  • Szyfrowanie (np. tokenów, sekretów) jest dwukierunkowe — można odszyfrować, gdy jest taka potrzeba.

3. Wstrzykiwanie (A03)

Problem: Dane wejściowe użytkownika modyfikują zapytania SQL, polecenia powłoki lub inne komendy.

// ❌ Surowy SQL podatny na wstrzyknięcia
DB::select("SELECT * FROM users WHERE email = '{$email}'");

// ✅ Używaj Query Buildera lub Eloquent
User::where('email', $email)->first();

Wskazówki:

  • Zawsze waliduj dane wejściowe przez Form Requesty.
  • Używaj zapytań parametryzowanych.
  • Escapuj wyjście w Blade przez {{ }} (nigdy nie używaj {!! !!} bez sanitizacji).

4. Niebezpieczny Projekt (A04)

Problem: Brak zabezpieczeń już na etapie projektowania.

Przykłady:

  • Brak rate limiting
  • Brak weryfikacji e-mail
  • Brak MFA przy usuwaniu konta
// ✅ Ogranicz logowanie
Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:5,1');
  • Użyj Laravel Fortify do weryfikacji e-mail i 2FA.
  • Projektuj zgodnie z zasadą najmniejszych uprawnień i defense in depth.

5. Błędna Konfiguracja Bezpieczeństwa (A05)

Problem: Niebezpieczne ustawienia w środowisku produkcyjnym.

  • APP_DEBUG=true ujawnia stack trace
  • .env dostępny, jeśli /public nie jest web rootem

✅ Rozwiązania:

  • APP_DEBUG=false w .env
  • Użyj php artisan config:cache
  • Udostępniaj tylko /public przez Nginx lub Apache
  • Ustaw odpowiednie uprawnienia na storage i .env

6. Podatne Komponenty (A06)

Problem: Używanie nieaktualnych lub podatnych zależności.

✅ Regularnie audytuj zależności:

composer audit
npm audit fix
  • Pinuj wersje w composer.json i package.json.
  • Monitoruj wydania Laravel, Inertia, Sanctum itd.
  • Usuwaj nieużywane paczki.

7. Błędy Identyfikacji i Uwierzytelniania (A07)

Problem: Błędna logika uwierzytelniania.

// ❌ Hasło nie jest haszowane
User::create(['password' => $request->password]);

// ✅ Używaj Fortify do logowania i resetu haseł.
  • Wymuszaj silne hasła i polityki haseł.
  • Używaj wbudowanego scaffoldu uwierzytelniania Laravel.
  • Blokuj konto po wielu nieudanych próbach logowania.

8. Błędy Integralności Oprogramowania i Danych (A08)

Problem: Zmodyfikowane pliki, niepodpisane zasoby, przerwane CI/CD.

<!-- ❌ Niezweryfikowany skrypt -->
<script src="https://cdn.example.com/react.js"></script>

<!-- ✅ Użyj integrity hash -->
<script src="..." integrity="sha384-..." crossorigin="anonymous"></script>
  • Używaj tylko zaufanych, przypiętych paczek.
  • Podpisuj artefakty wdrożeniowe, jeśli to możliwe.
  • Używaj podpisanych URLi Laravel dla wrażliwych akcji.

9. Błędy Logowania i Monitoringu Bezpieczeństwa (A09)

Problem: Brak możliwości wykrycia lub prześledzenia podejrzanej aktywności.

✅ Loguj nieudane logowania, resety haseł, podejrzane zachowania:

Log::warning('Login failed', [
    'email' => $request->email,
    'ip' => $request->ip(),
]);
  • Używaj Laravel Telescope lub narzędzi zewnętrznych (Sentry, ELK).
  • Ustaw alerty na podejrzaną aktywność.
  • Regularnie przeglądaj logi.

10. SSRF — Fałszowanie Żądań po Stronie Serwera (A10)

Problem: Aplikacja wykonuje żądania do adresów podanych przez użytkownika.

// ❌ Użytkownik podaje dowolny URL
Http::get($request->input('url'));

// ✅ Whitelistuj zaufane domeny:
$request->validate([
    'url' => 'required|url|regex:/^https:\/\/api\.mojadomena\.pl/',
]);

Http::get($request->input('url'))->throw();
  • Nigdy nie pobieraj dowolnych URLi od użytkownika.
  • Używaj allow-list i ścisłej walidacji.

Poza OWASP: Dodatkowe Zagrożenia

⚠️ 1. Masowe Przypisywanie (Mass Assignment)

Problem: Atakujący mogą ustawić dowolny atrybut modelu, jeśli nie ograniczysz pól dozwolonych do masowego przypisania.

// ❌ Niebezpieczne: pozwala na ustawienie wszystkich pól z requesta
User::create($request->all());

// ✅ Użyj $fillable, by jawnie określić dozwolone pola
class User extends Model {
    protected $fillable = ['name', 'email', 'password'];
}

// Lub $guarded, by zablokować wybrane pola
class User extends Model {
    protected $guarded = ['is_admin', 'role'];
}

// ✅ Bezpieczniej: przekazuj tylko dozwolone pola
User::create($request->only(['name', 'email', 'password']));

Dobre praktyki:

  • Zawsze definiuj $fillable lub $guarded w modelach.
  • Nigdy nie używaj $request->all() do masowego przypisania.
  • Waliduj dane wejściowe przez Form Requesty.

🔎 2. Cross-Site Scripting (XSS)

Problem: Dane wejściowe użytkownika są renderowane jako HTML/JS, co pozwala na wstrzyknięcie skryptów.

W Blade:

// ❌ Podatne: wyświetla surowy HTML
{!! $userInput !!}

// ✅ Bezpieczne: escapuje wyjście
{{ $userInput }}

W Inertia/React:

// ❌ Podatne: użycie dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userInput }} />

// ✅ Bezpieczne: renderuj jako tekst lub sanitizuj
<div>{userInput}</div>
// lub użyj DOMPurify jeśli musisz renderować HTML
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />

Dobre praktyki:

  • Nigdy nie ufaj danym wejściowym w HTML.
  • Zawsze escapuj wyjście w Blade ({{ }}) i sanitizuj w React jeśli renderujesz HTML.
  • Używaj bibliotek typu DOMPurify do sanitizacji.

🌐 3. Błędna Konfiguracja CORS

Problem: Zezwolenie na wszystkie domeny (*) naraża API na ataki cross-origin.

Przykład (config/cors.php):

// ❌ Niebezpieczne
'allowed_origins' => ['*'],

// ✅ Tylko zaufane domeny
'allowed_origins' => ['https://twojadomena.pl', 'https://admin.twojadomena.pl'],

Dobre praktyki:

  • Nigdy nie używaj * dla API wymagających autoryzacji.
  • Ograniczaj CORS tylko do zaufanych domen.

🕓 4. Błędna Konfiguracja Sesji

Problem: Niezabezpieczone ciasteczka sesyjne mogą zostać przechwycone lub zmanipulowane.

Przykład (config/session.php):

// ✅ Bezpieczne ustawienia sesji
'cookie_secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => 'lax', // lub 'strict' dla większego bezpieczeństwa

Dobre praktyki:

  • Zawsze używaj HTTPS w produkcji.
  • Ustawiaj ciasteczka jako secure, http_only i same_site.
  • Rotuj identyfikatory sesji po zalogowaniu.

🗂️ 5. Otwarte Przekierowania

Problem: Przekierowanie do adresu z danych wejściowych użytkownika może umożliwić phishing.

// ❌ Podatne
return redirect($request->input('next'));

// ✅ Tylko wewnętrzne adresy
$next = $request->input('next');

if ($next && Str::startsWith($next, '/')) {
    return redirect($next);
}

return redirect('/dashboard');

Dobre praktyki:

  • Nigdy nie przekierowuj do dowolnych adresów z danych wejściowych.
  • Zawsze waliduj lub sanitizuj adresy przekierowań.

🗄️ 6. Niebezpieczne Uploady Plików

Problem: Użytkownicy mogą uploadować niebezpieczne pliki (np. skrypty PHP, malware).

// ✅ Waliduj typ i rozmiar pliku
$request->validate([
    'avatar' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
]);

// ✅ Przechowuj poza katalogiem publicznym jeśli to możliwe
$path = $request->file('avatar')->store('avatars', 'private');

Dobre praktyki:

  • Zawsze waliduj typ, rozmiar i zawartość pliku.
  • Przechowuj uploady poza katalogiem publicznym jeśli to możliwe.
  • Nigdy nie wykonuj ani nie serwuj uploadowanych plików jako kod.

🛑 7. Ujawnienie .env

Problem: Jeśli web root jest źle ustawiony, plik .env może być dostępny z internetu, co grozi wyciekiem sekretów.

Dobre praktyki:

  • Zawsze ustawiaj web root na katalog public/.
  • Nigdy nie commituj plików .env do repozytorium.
  • Używaj zmiennych środowiskowych do sekretów w produkcji.

Śledź mnie na LinkedIn, aby otrzymywać więcej wskazówek o Laravel i DevOps!

Czy chciałbyś dowiedzieć się więcej o bezpieczeństwie aplikacji? Daj znać w komentarzach poniżej!

Komentarze (0)
Zostaw komentarz

© 2026 Wszelkie prawa zastrzeżone.