Lewati ke konten

Shield Corev1.2.x

Ban & Risk Decay

Ban adalah bagian dari Shield yang paling mudah disalahpahami. Dua hal yang paling sering tidak terduga:

  1. Melepas ban tidak mereset riwayat.
  2. Ban tidak langsung menolak — ia menaikkan jadi challenge dulu.

Keduanya disengaja, dan keduanya dijelaskan di bawah.

enum BanStatus: string
{
case Active = 'active';
case Expired = 'expired';
case Released = 'released';
case ManualBlock = 'manual_block';
}
Status Arti
active Sedang berlaku dan ditegakkan.
expired Sudah lewat expires_at sendiri.
released Dilepas manual oleh operator.
manual_block Diblokir manual, biasanya karena alasan di luar sistem.

Perhatikan bahwa expired dan released tetap ada di database. Keduanya hanya berbeda dari mana asalnya.

BanRecord menyimpan lebih dari sekadar status:

Field Arti
violationCount Berapa kali request mencurigakan datang selama ban ini aktif.
offenseCount Berapa kali IP ini pernah kena ban, lintas riwayat.
riskScore Skor terakhir yang dihitung.
challengePassedAt Kapan challenge terakhir berhasil dilalui.

Pembeda antara violationCount dan offenseCount penting:

  • violationCount bertambah selama ban aktif. Ini menunjukkan seberapa gigih orang itu mencoba.
  • offenseCount bertambah setiap kali ban baru dibuat. Ini yang menentukan durasi ban berikutnya.
public function durationFor(int $offenseCount, array $durationsMinutes): int
{
$index = max(0, $offenseCount - 1);
return $durationsMinutes[min($index, count($durationsMinutes) - 1)];
}

Dengan ban.durations bawaan [15, 60, 360, 1440]:

Offense ke- Durasi
1 15 menit
2 1 jam
3 6 jam
4 24 jam
5 ke atas 24 jam (entri terakhir dipakai terus)

Perhatikan min($index, count - 1). Setelah daftar habis, durasi terakhir dipakai selamanya. Menambah entri baru di tengah daftar mengubah durasi ban yang sedang berlaku — hati-hati kalau Anda memutuskan menambahnya saat production.

public function nextOffenseCount(?BanRecord $previous): int
{
if ($previous === null) {
return 1;
}
return $previous->offenseCount + 1;
}

Melepas ban hanya mengubah status jadi released dan mengisi released_at. Penghitungnya tetap utuh.

Konsekuensinya: orang yang dilepas secara manual akan kembali dengan durasi yang lebih lama kalau dia offense lagi. Ini disengaja — kalau riwayat direset setiap kali operator membantu, ban berhenti jadi hukuman.

Buat Anda yang mengoperasikan: melepas ban bukan berarti mengampuni. Kalau Anda ingin IP yang sama untuk sementara tidak diblokir lagi, itu perkara lain daripada melepas ban.

RiskDecay punya dua method, dengan jendela waktu berbeda.

decayOffenseCount(
int $offenseCount,
\DateTimeImmutable $lastSeen,
\DateTimeImmutable $now,
int $decayWindowSeconds = 86400, // 24 jam
int $decayPerWindow = 1,
): int

Satu offense hilang setiap 24 jam tanpa aktivitas. Jadi IP yang diblokir hari Senin dan tidak kembali sampai Rabu pagi punya offenseCount satu lebih kecil.

Ini yang mencegah penghakiman selamanya. Tanpa pelusan, satu kesalahan konfigurasi di bulan Maret akan membuat IP itu kena ban 24 jam selamanya di bulan Juni.

decayScore(
int $score,
\DateTimeImmutable $lastSeen,
\DateTimeImmutable $now,
int $decayWindowSeconds = 3600, // 1 jam
int $decayPerWindow = 10,
): int

Sepuluh poin hilang setiap jam tanpa aktivitas.

Dua jendela itu punya alasan berbeda: skor mereda cepat supaya request berikutnya tidak langsung kena ban, sementara offense mereda lambat supaya riwayat tetap berarti.

Keduanya tidak pernah turun di bawah nol:

return max(0, $offenseCount - ($windows * $decayPerWindow));

Nilai yang meluruh tidak ditulis ulang ke database. ShieldEngine menghitungnya saat dibutuhkan:

$offenseCount = $activeBan === null ? 0 : $this->riskDecay->decayOffenseCount(
$activeBan->offenseCount,
$activeBan->lastSeenAt,
$this->clock->now(),
);

Jadi kalau Anda membuka dashboard dan melihat angka lama di sana, itu bukan berarti pelusan tidak jalan — cuma belum ada request baru dari IP itu untuk menghitung ulang.

public const MANUAL_REVIEW_THRESHOLD = 5;

Setelah lima offense, ban baru disimpan dengan metadata manual_review: true:

$metadata = ['manual_review' => $this->banPolicy->requiresManualReview($offenseCount)];

Perlu ditegaskan: metadata ini tidak mengubah cara ban berakhir. Ban tetap kedaluwarsa sesuai jadwalnya, dan pada offense kelima durasi sudah mentok di nilai terakhir ban.durations (bawaannya 1440 menit / 24 jam). Yang dilakukan metadata tersebut hanya penanda bagi aplikasi Anda — dan bagi siapa pun yang membaca tabel ban — bahwa IP ini layak diperiksa manusia sebelum dikembalikan.

Tujuannya jelas: pola serangan otomatis yang sophisticated biasanya memerlukan banyak percobaan. Lima offense dari IP yang sama layak dilihat oleh manusia sebelum dikembalikan.

Ban aktif di-cache selama performance.ban_cache_ttl_seconds, bawaannya 30 detik.

Ini menjelaskan gejala yang sering dikeluhkan: “ban sudah saya lepas tapi IP masih diblokir”. Cache-nya belum kedaluwarsa. php artisan cache:clear menyelesaikannya.

Nilai 30 detik dipilih sebagai kompromi: cukup untuk menutup celah di mana beberapa request paralel lolos sebelum ban tercatat, dan cukup pendek supaya tindakan manual terasa langsung.

// Melepas ban aktif.
$engine->releaseBan($ip, $reason, $actor);
// Melepas ban DAN mencatat bahwa challenge berhasil dilalui.
// Riwayat tetap tersimpan.
$engine->markChallengePassed($ip);
// Membuat cookie tepercaya setelah challenge.
$cookie = $engine->issueTrustedCookie($request);

releaseBan() dan markChallengePassed() mengembalikan false kalau BanRepositoryInterface null atau tidak ada ban aktif untuk IP itu. Cek nilai kembalinya sebelum mengabarui UI.

Kalau request mencocokkan rule critical, ban yang sedang aktif tidak menghinderinya. Ban berlaku pada request biasa; signature berlaku pada segala hal.

Durasi dibaca dari urutan offense, bukan dari skor

Section titled “Durasi dibaca dari urutan offense, bukan dari skor”

IP dengan skor 45 dan offense pertama tetap dapat ban 15 menit. Yang menentukan durasi hanya berapa kali dia sudah pernah kena ban.

violationCount adalah sinyal, bukan hukuman

Section titled “violationCount adalah sinyal, bukan hukuman”

Penyerang yang coba 200 kali selama ban aktif punya violationCount 200. Itu berguna untuk laporan dan untuk melihat pola, tapi tidak mengubah durasi.

Kalau Anda menghapus, offenseCount ikut hilang dan durasi ban berikutnya kembali ke 15 menit. Gunakan release, jangan delete.

Powered by PT Ganadev Multi Solusi