Blog

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!

Wsparcie istniejącego systemu

Potrzebujesz pomocy z działającą aplikacją?

Pomagam firmom rozwijać działające systemy, porządkować wdrożenia i dodawać nowe funkcje bez dokładania chaosu do projektu.

Komentarze (0)
Zaloguj się, aby dodać komentarz

Musisz być zalogowany, aby dodać komentarz.

Zaloguj się

Potrzebujesz kogoś, kto weźmie odpowiedzialność za kolejny krok?

Porozmawiajmy o Twoim projekcie i określmy zakres, który ma sens dla Twoich celów.