โ† Semua pembelajaran / Laravel Nol โ†’ Enterprise
Fase 4 ยท Autentikasi & Keamanan

Rate limiting

Tanpa pembatasan laju, satu skrip bisa menebak password sepanjang malam, mengeruk seluruh isi katalogmu, atau membuat database bekerja keras untuk permintaan yang tidak pernah dilihat manusia.

Sumber asli laravel.com Resmi Rangkuman ~5 menit baca

Intisari

  • Definisikan limiter bernama di AppServiceProvider::boot(), pakai lewat middleware throttle:nama.
  • Batas bisa berbeda per pengguna: pengguna berlangganan mendapat kuota lebih besar dari tamu.
  • Melampaui batas menghasilkan 429 beserta header Retry-After.
  • Butuh cache bersama. Dengan driver file, tiap kontainer punya hitungannya sendiri.
  • Di belakang ALB, pembatasan per-IP hanya benar kalau trusted proxy sudah dikonfigurasi.

Mendefinisikan limiter

// AppServiceProvider::boot()
use Illuminate\Cache\RateLimiting\Limit;

RateLimiter::for('api', fn (Request $r) =>
    Limit::perMinute(60)->by($r->user()?->id ?: $r->ip()));

// Batas berbeda per jenis pengguna
RateLimiter::for('unggah', function (Request $r) {
    return $r->user()?->berlangganan
        ? Limit::perMinute(100)->by($r->user()->id)
        : Limit::perMinute(10)->by($r->user()?->id ?: $r->ip());
});

// Beberapa batas sekaligus: cepat dalam semenit, dan total dalam sehari
RateLimiter::for('pencarian', fn (Request $r) => [
    Limit::perMinute(30)->by($r->ip()),
    Limit::perDay(1000)->by($r->ip()),
]);

// Respons kustom
RateLimiter::for('login', fn (Request $r) =>
    Limit::perMinute(5)
        ->by(Str::lower($r->input('email')).'|'.$r->ip())
        ->response(fn () => response()->json([
            'message' => 'Terlalu banyak percobaan. Coba lagi sebentar.',
        ], 429)));
Route::middleware('throttle:api')->group(...);
Route::post('/login', ...)->middleware('throttle:login');
Route::post('/unggah', ...)->middleware('throttle:unggah');

// Tanpa limiter bernama: 60 permintaan per 1 menit
Route::middleware('throttle:60,1')->group(...);

Kunci pembatas untuk login sebaiknya menggabungkan email dan IP. Kalau kuncinya hanya email, penyerang bisa mengunci akun orang lain hanya dengan mengirim password salah berulang kali โ€” penolakan layanan terhadap pengguna sah. Kalau hanya IP, satu kantor yang berbagi IP akan saling mengganggu. Menggabungkan keduanya menyeimbangkan kedua risiko itu.

Yang wajib dibatasi

EndpointSaranAlasan
Login5 / menit per email+IPSerangan tebak password
Lupa password3 / menit per IPMembanjiri kotak masuk orang lain
Registrasi3 / menit per IPPembuatan akun massal
Pencarian30 / menitQuery mahal; sasaran empuk pengerukan
Unggah berkas10 / menitBiaya penyimpanan dan pemrosesan
API publik60 / menit per tokenKeadilan antar konsumen
Endpoint yang memanggil layanan berbayarSeketat mungkinSetiap permintaan adalah uang

Memakainya di luar HTTP

use Illuminate\Support\Facades\RateLimiter;

// Batasi pemanggilan API pihak ketiga dari dalam job
$dieksekusi = RateLimiter::attempt(
    key: 'sinkron-marketplace',
    maxAttempts: 10,
    callback: fn () => $klien->kirim($data),
    decaySeconds: 60,
);

if (! $dieksekusi) {
    $this->release(RateLimiter::availableIn('sinkron-marketplace'));   // job dikembalikan ke antrean
}

Pola release() ini penting untuk worker: alih-alih menggagalkan job karena penyedia membatasi laju, job dikembalikan ke antrean dan dicoba lagi tepat setelah kuotanya pulih.

Dua syarat supaya benar-benar bekerja

SyaratKalau tidak dipenuhi
Cache bersama (Redis)Batas 60/menit dengan 4 kontainer menjadi 240/menit
Trusted proxy dikonfigurasiSemua permintaan terlihat berasal dari IP load balancer โ€” satu pengguna berisik mengunci semua orang

Rate limiting aplikasi bukan pertahanan DDoS. Ia bekerja setelah permintaan sampai ke PHP, jadi serangan volumetrik tetap menghabiskan koneksi dan CPU-mu. Untuk itu, lapisan yang tepat ada di depan: AWS WAF beserta aturan berbasis laju di CloudFront (Fase 9). Rate limiter Laravel menangani penyalahgunaan pada tingkat logika bisnis โ€” dan itu tetap perlu.

Latihan: pasang throttle:login pada rute loginmu, lalu kirim enam permintaan salah berturut-turut dengan curl -i. Amati header X-RateLimit-Remaining turun, lalu status berubah jadi 429 beserta Retry-After.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.