Keamanan seringkali menjadi afterthought dalam development — ditangani "nanti sebelum launch" atau setelah ada insiden. Padahal, sebagian besar celah keamanan umum bisa dicegah dengan praktik coding yang benar sejak awal. Artikel ini membahas OWASP Top 10 (kerentanan web paling kritis menurut Open Web Application Security Project) dalam konteks aplikasi Laravel.

A01: Broken Access Control

Pengguna bisa mengakses resource atau melakukan aksi yang seharusnya tidak diizinkan. Ini adalah kerentanan nomor 1 di OWASP Top 10 2021.

// ❌ Berbahaya — tidak ada authorization
public function edit(Post $post): View
{
    return view('posts.edit', compact('post'));
}

// ✅ Selalu authorize
public function edit(Post $post): View
{
    $this->authorize('update', $post); // Pastikan PostPolicy terdaftar
    return view('posts.edit', compact('post'));
}

// PostPolicy.php
public function update(User $user, Post $post): bool
{
    return $user->id === $post->author_id || $user->hasRole('admin');
}

Aktifkan authorization di semua route yang mengubah data. Jangan hanya sembunyikan tombol di UI — selalu validasi di backend.

A02: Cryptographic Failures

Data sensitif disimpan atau ditransmisikan tanpa enkripsi yang memadai.

// ❌ Jangan pernah simpan password plaintext atau MD5
$user->password = md5($request->password); // SALAH BESAR

// ✅ Gunakan bcrypt/argon2 via Laravel
$user->password = Hash::make($request->password);

// ✅ Selalu verifikasi dengan Hash::check
if (! Hash::check($request->password, $user->password)) {
    return back()->withErrors(['password' => 'Password salah']);
}

Pastikan pula semua komunikasi menggunakan HTTPS, dan data sensitif di database dienkripsi menggunakan Laravel's Encryption (encrypt()/decrypt()) atau $casts dengan encrypted:.

A03: Injection (SQL Injection)

Laravel's Eloquent dan Query Builder secara default menggunakan prepared statements, sehingga aman dari SQL injection. Masalah muncul saat Anda bypass mekanisme ini:

// ❌ Rentan SQL injection
$posts = DB::select("SELECT * FROM posts WHERE title = '{$request->title}'");

// ❌ whereRaw tanpa binding juga berbahaya
Post::whereRaw("title = '{$request->title}'")->get();

// ✅ Gunakan parameter binding
$posts = DB::select('SELECT * FROM posts WHERE title = ?', [$request->title]);

// ✅ whereRaw dengan binding
Post::whereRaw('title = ? AND status = ?', [$request->title, 'published'])->get();

// ✅ Cara terbaik — gunakan Eloquent Query Builder
Post::where('title', $request->title)->where('status', 'published')->get();

A04: Insecure Design

Kerentanan yang berasal dari arsitektur yang tidak mempertimbangkan keamanan. Contoh: API yang terlalu banyak expose data.

// ❌ Return semua field model — expose data sensitif
return response()->json($user);

// ✅ Gunakan API Resource untuk kontrol data yang dikembalikan
return new UserResource($user);

// UserResource.php
public function toArray(Request $request): array
{
    return [
        'id'         => $this->id,
        'name'       => $this->name,
        'avatar_url' => $this->avatar_url,
        // Tidak ada: email, password, remember_token, dll.
    ];
}

A05: Security Misconfiguration

Konfigurasi default yang tidak aman di production.

# .env production wajib
APP_ENV=production
APP_DEBUG=false        # Jangan pernah true di production!
APP_URL=https://domain.com

# Selalu rotate key sebelum pertama deploy
php artisan key:generate

# Konfigurasi security headers di Nginx
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()";

A06: Vulnerable and Outdated Components

# Audit dependency PHP
composer audit

# Audit dependency JavaScript
npm audit
npm audit fix

# Update dependency secara berkala
composer update --with-dependencies
npm update

A07: Identification and Authentication Failures

// Rate limiting untuk login — cegah brute force
RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->ip());
});

// Implementasi 2FA untuk akun admin
// Gunakan package seperti: pragmarx/google2fa-laravel

// Session regeneration setelah login (sudah otomatis di Laravel)
// Pastikan session di-invalidate saat logout
public function logout(Request $request): RedirectResponse
{
    Auth::logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();
    return redirect('/');
}

A08: Software and Data Integrity Failures

// ❌ Jangan unserialize data dari user input
$data = unserialize($request->data); // Sangat berbahaya

// ✅ Gunakan JSON yang aman
$data = json_decode($request->data, true);

// Verifikasi integritas file upload
public function upload(Request $request): JsonResponse
{
    $request->validate([
        'file' => ['required', 'file', 'mimes:pdf,docx', 'max:10240'],
    ]);
    // Simpan di private storage, bukan public
    $path = $request->file->store('uploads', 'private');
}

A09: Mass Assignment

Celah yang sering diabaikan developer Laravel pemula:

// ❌ Berbahaya — user bisa inject field is_admin
$user->update($request->all());

// ✅ Gunakan $fillable di model
class User extends Model
{
    protected $fillable = ['name', 'email', 'avatar_url'];
    // Field 'is_admin', 'role', dll. tidak bisa di-mass assign
}

// ✅ Atau gunakan validated() dari Form Request
$user->update($request->validated());

A10: Server-Side Request Forgery (SSRF)

// ❌ Berbahaya — user bisa request ke internal network
$response = Http::get($request->url);

// ✅ Validasi URL yang diizinkan
$request->validate([
    'url' => ['required', 'url', 'not_regex:/^https?:\/\/(localhost|127\.|10\.|192\.168\.)/i'],
]);

// Atau whitelist domain yang diizinkan
$allowedDomains = ['api.trusted-partner.com', 'cdn.example.com'];
$host = parse_url($request->url, PHP_URL_HOST);
if (! in_array($host, $allowedDomains, true)) {
    abort(422, 'URL tidak diizinkan');
}

Laravel Security Checklist

  • CSRF protection aktif (default di semua web routes Laravel)
  • APP_DEBUG=false di production
  • Semua route yang sensitif menggunakan authorization
  • File upload divalidasi tipe, ukuran, dan disimpan di private storage
  • Rate limiting di route auth dan API
  • Dependency diaudit secara berkala dengan composer audit
  • Security headers dikonfigurasi di Nginx/Apache
  • Password menggunakan bcrypt/argon2 via Hash::make()

Keamanan bukan fitur yang ditambahkan di akhir — ini adalah praktik yang dijalankan setiap hari. Sebagian besar kerentanan di atas bisa dicegah dengan satu kebiasaan sederhana: jangan pernah trust input dari user tanpa validasi dan sanitasi yang proper.