Laravel Shieldv1.2.x
Konfigurasi
Semua opsi Shield ada di satu file: config/shield.php. File itu muncul setelah
Anda menjalankan vendor:publish --tag=shield-config.
Setiap kunci punya dua cara diatur:
- Langsung di
config/shield.php— untuk nilai yang spesifik untuk aplikasi Anda. - Lewat environment variable — untuk nilai yang berbeda tiap server (staging dan produksi, misalnya). Ini yang kami rekomendasikan.
Konsep yang perlu Anda pahami dulu
Section titled “Konsep yang perlu Anda pahami dulu”Tiga mode Shield
Section titled “Tiga mode Shield”mode menentukan seberapa keras Shield bertindak:
| Mode | Yang terjadi pada request mencurigakan |
|---|---|
observe |
Dicatat sebagai kejadian. Tidak ada yang diblokir dan tidak ada challenge. |
challenge |
Ban diturunkan menjadi challenge — pengunjung diminta membuktikan bahwa dia manusia. |
enforce |
Diblokir sepenuhnya, dan IP-nya diban. |
Selalu mulai dari observe. Naikkan ke mode yang lebih ketat setelah Anda
memastikan tidak ada yang salah sasaran.
Denylist dan allowlist
Section titled “Denylist dan allowlist”- Rule (denylist) = pola yang harus diblokir. Shield sudah punya banyak, dan Anda bisa menambah atau menyesuaikan sendiri.
- Allowlist = daftar yang dikecualikan dari pemeriksaan. Gunakan hemat.
Threshold (ambang batas)
Section titled “Threshold (ambang batas)”Shield tidak memakai satu aturan “tolak atau tidak”. Setiap sinyal menambah skor, lalu skor itu dibandingkan dengan ambang batas:
| Kunci | Env | Default | Arti |
|---|---|---|---|
thresholds.challenge |
SHIELD_THRESHOLD_CHALLENGE |
10 |
Skor mulai menimbulkan challenge. |
thresholds.ban |
SHIELD_THRESHOLD_BAN |
20 |
Skor mulai menimbulkan ban sementara. |
thresholds.strong_ban |
SHIELD_THRESHOLD_STRONG_BAN |
30 |
Skor menimbulkan ban dengan durasi terpanjang. |
Ketiganya wajib naik berurutan. Nilai yang sama atau turun akan ditolak saat
boot dengan InvalidConfigException, karena membuat keputusan jadi tidak dapat
diprediksi. Menurunkan challenge berarti lebih sedikit challenge terbit;
menaikkan ban membuat ban lebih jarang tapi lebih lambat.
Jadi beberapa sinyal lemah yang kemunculan bersama bisa menjatuhkan, sementara satu sinyal lemah saja belum cukup untuk memblokir.
| Kunci | Env | Default | Keterangan |
|---|---|---|---|
enabled |
SHIELD_ENABLED |
true |
Master switch. false mematikan Shield sepenuhnya. |
mode |
SHIELD_MODE |
observe |
observe | challenge | enforce. |
app_id |
SHIELD_APP_ID |
my-app |
Namespace cache. Wajib diubah kalau dua aplikasi berbagi cache. |
response_code |
SHIELD_RESPONSE_CODE |
404 |
Kode HTTP saat memblokir. 404 menyembunyikan keberadaan firewall. |
decode_depth |
— | 2 |
Berapa kali URL di-decode (0–3). Naikkan hanya kalau ada aturan tak terdeteksi. |
fail_mode |
SHIELD_FAIL_MODE |
open |
open atau closed saat database/cache mati. Lihat catatan di bawah. |
rule_version |
— | 1.0.0 |
Versi aturan, dicatat di setiap kejadian. |
fail_mode saat infrastruktur mati
Section titled “fail_mode saat infrastruktur mati”Kalau database atau cache sedang tidak bisa dijangkau, Shield harus memutuskan satu hal: biarkan request lewat, atau blokir semuanya?
| Nilai | Perilaku | Kapan dipakai |
|---|---|---|
open |
Request normal tetap lewat. Aturan kritis tetap diblokir karena tidak butuh database. | Hampir selalu benar. |
closed |
Semua request diblokir sampai infrastruktur pulih. | Hanya kalau Anda lebih siap menerima downtime daripada risiko gagal. |
Ban dan eskalasi
Section titled “Ban dan eskalasi”| Kunci | Default | Keterangan |
|---|---|---|
ban.durations |
[15, 60, 360, 1440] |
Durasi ban dalam menit, untuk offense ke-1, 2, 3, dan 4. |
escalation.step |
5 |
Skor tambahan per offense (maksimal 5 offense yang dihitung). |
Artinya ban.durations di atas berarti:
| Offense | Durasi ban |
|---|---|
| 1 | 15 menit |
| 2 | 1 jam |
| 3 | 6 jam |
| 4 | 24 jam |
| 5 ke atas | 24 jam + flag manual_review |
Challenge
Section titled “Challenge”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
challenge.driver |
SHIELD_CHALLENGE |
turnstile |
turnstile | recaptcha | null. |
challenge.turnstile.site_key |
SHIELD_TURNSTILE_SITE_KEY |
'' |
Kunci publik untuk widget. |
challenge.turnstile.secret_key |
SHIELD_TURNSTILE_SECRET_KEY |
'' |
Kunci rahasia untuk verifikasi server. |
challenge.recaptcha.site_key |
SHIELD_RECAPTCHA_SITE_KEY |
'' |
Sama, untuk reCAPTCHA. |
challenge.recaptcha.secret_key |
SHIELD_RECAPTCHA_SECRET_KEY |
'' |
Sama, untuk reCAPTCHA. |
challenge.turnstile.verify_url |
SHIELD_TURNSTILE_VERIFY_URL |
endpoint Cloudflare | Alamat server verifikasi. |
challenge.recaptcha.verify_url |
SHIELD_RECAPTCHA_VERIFY_URL |
endpoint Google | Alamat server verifikasi. |
null berarti challenge dilewati begitu saja tanpa memanggil pihak ketiga.
Berguna untuk development dan test otomatis — jangan dipakai di produksi.
Secret tidak pernah disimpan ke database maupun ditulis ke log.
Perilaku (rate limit berbasis waktu)
Section titled “Perilaku (rate limit berbasis waktu)”Sinyal di sini menangkap bot yang menghindari pola statis: yang memakai URL sah, tetapi dengan volume yang tidak wajar.
| Kunci | Env | Default | Keterangan |
|---|---|---|---|
behavior.unique_uri_limit |
SHIELD_BEHAVIOR_UNIQUE_URI_LIMIT |
25 |
Batas URI berbeda dalam satu jendela waktu. |
behavior.window_seconds |
SHIELD_BEHAVIOR_WINDOW_SECONDS |
60 |
Panjang jendela waktu, dalam detik. |
behavior.not_found_limit |
SHIELD_BEHAVIOR_NOT_FOUND_LIMIT |
20 |
Batas respons 404 dalam satu jendela. |
behavior.missing_referer_signal |
SHIELD_BEHAVIOR_MISSING_REFERER |
true |
Sinyal lemah (+2) untuk POST tanpa header Referer. |
behavior.scanner_user_agents |
— | 13 nama alat | User-Agent alat scanner keamanan. |
behavior.scanner_ua_signal |
SHIELD_BEHAVIOR_SCANNER_UA_SIGNAL |
4 |
Skor untuk User-Agent scanner. |
behavior.suspicious_user_agents |
— | [] |
Tambahan penanda klien yang ingin Anda awasi. |
behavior.path_rate_limit |
SHIELD_BEHAVIOR_PATH_RATE_LIMIT |
30 |
Batas request per path biasa dalam jendela. |
behavior.sensitive_path_rate_limit |
SHIELD_BEHAVIOR_SENSITIVE_PATH_RATE_LIMIT |
8 |
Batas lebih ketat untuk endpoint login. |
behavior.sensitive_paths |
— | 7 path bawaan | Path yang memakai batas ketat. |
Counter perilaku disimpan di cache dengan key yang di-namespace oleh app_id,
jadi dua aplikasi yang berbagi Redis tidak saling menimpa.
Tambahkan path login Anda sendiri kalau tidak ada di daftar bawaan:
'behavior' => [ 'sensitive_paths' => [ '/login', '/admin/login', '/api/login', '/masuk', // path lokal aplikasi Anda '/auth/token', // endpoint API Anda ],],Bot dan crawler
Section titled “Bot dan crawler”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
bots.mode |
SHIELD_BOT_MODE |
observe |
off | observe | challenge. |
bots.known_agents |
— | googlebot, bingbot, … |
Daftar crawler yang dikenali. |
bots.verification.enabled |
SHIELD_BOT_VERIFICATION_ENABLED |
true |
Cek IP asli crawler sebelum dipercaya. |
bots.verification.ttl_hours |
SHIELD_BOT_VERIFICATION_TTL_HOURS |
24 |
Lama hasil verifikasi disimpan. |
bots.verification.hostnames |
— | .googlebot.com, … |
Suffix hostname yang sah untuk tiap crawler. |
bots.verification.ip_ranges |
— | [] |
CIDR resmi crawler. Lihat catatan di bawah. |
bots.unverified_claim_signal |
SHIELD_BOT_UNVERIFIED_CLAIM_SIGNAL |
4 |
Skor untuk klaim crawler yang gagal diverifikasi. |
Aturan dan inspeksi body
Section titled “Aturan dan inspeksi body”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
rules.packs.wordpress |
SHIELD_RULES_PACK_WORDPRESS |
false |
Rule khusus plugin WordPress. |
rules.packs.injection |
SHIELD_RULES_PACK_INJECTION |
true |
Rule SQLi, XSS, LFI, dan command injection. |
rules.skip_paths |
— | [] |
Prefix path yang dikecualikan dari inspeksi body dan sinyal perilaku. |
inspection.body.enabled |
SHIELD_INSPECTION_BODY_ENABLED |
true |
Periksa juga isi body request. |
inspection.body.max_bytes |
SHIELD_INSPECTION_BODY_MAX_BYTES |
65536 |
Batas body yang dibaca (64 KB). |
rules.skip_paths
Section titled “rules.skip_paths”Beberapa endpoint wajar mengirim pola yang dianggap berbahaya:
- endpoint webhook yang menerima JSON berisi kode atau markup,
- rich text editor yang menyimpan HTML mentah,
- gerbang API yang melayani banyak klien dari satu IP (M2M).
Rule XSS bernilai 12, sedangkan ambang challenge bawaannya 10 — jadi payload JSON yang sah bisa memicu challenge. Untuk endpoint seperti itu, daftarkan path-nya:
'rules' => [ 'skip_paths' => [ '/oauth/token', '/api/webhooks', '/admin/reports/datatable', ],],Yang tidak berubah: signature di URI tetap dijalankan. /.env atau
?file=../../etc/passwd di path yang sama tetap diblokir.
Klien API dan M2M
Section titled “Klien API dan M2M”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
api.paths |
— | [] |
Prefix path yang selalu dibalas JSON. |
api.detect_accept |
SHIELD_API_DETECT_ACCEPT |
true |
Deteksi otomatis dari header Accept. |
Tanpa ini, aplikasi API Anda bisa menerima halaman HTML saat Shield memblokir sebuah request — dan klien API Anda akan gagal dengan pesan yang membingungkan.
| Kasus | Status | Header | Bentuk body |
|---|---|---|---|
| Blokir | response_code (default 404) |
X-Shield-Blocked: <rule id> |
{ error, app_id, rule_id, decision, score } |
| Challenge | 401 |
X-Shield-Challenge: 1 |
{ error, app_id, challenge_url } |
Status blokir sengaja tetap mengikuti response_code supaya keberadaan firewall
tidak terkonfirmasi ke penyerang. Yang berubah hanya bentuk responsnya.
Logging dan privasi
Section titled “Logging dan privasi”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
logging.level |
SHIELD_LOG_LEVEL |
suspicious |
all | suspicious | blocked. |
logging.bypass_events |
SHIELD_LOG_BYPASS_EVENTS |
true |
Catat juga allowlist dan fail-closed. |
logging.retention_days |
SHIELD_LOG_RETENTION_DAYS |
30 |
Umur data sebelum dibersihkan. |
privacy.sensitive_query_parameters |
— | token, password, passwd, key, secret, code, auth |
Disamarkan jadi *** sebelum disimpan. |
Pencocokan nama parameter bersifat case-insensitive dan sudah menangani kunci
yang di-percent-encode, sehingga access%5Ftoken ikut disamarkan. Tambahkan
nama parameter khusus aplikasi Anda di sini. Parameter yang tidak terdaftar
akan tersimpan apa adanya, jadi pastikan tidak ada yang lupa.
Apa arti masing-masing level:
| Level | Yang disimpan |
|---|---|
suspicious |
Semua keputusan selain “Izinkan”: challenge, amati, blokir, dan ban. |
blocked |
Hanya blokir dan ban. |
all |
Setiap request, termasuk yang diizinkan. |
shield:prune sudah dijadwalkan otomatis setiap hari oleh Shield. Aplikasi Anda
tetap wajib menjalankan php artisan schedule:run setiap menit.
| Kunci | Env | Default | Keterangan |
|---|---|---|---|
admin.enabled |
SHIELD_ADMIN_ENABLED |
false |
Nyalakan Admin API. |
admin.middleware |
— | ['web','auth'] |
Middleware untuk route admin. |
admin.authorize |
SHIELD_ADMIN_AUTHORIZE |
'' |
Nama Gate yang wajib dimiliki. |
admin.prefix |
— | shield |
Prefix URL route admin. |
admin.enabled tidak aktif secara default dan sebaiknya tetap begitu.
Kalau Anda menyalakannya, isi juga admin.authorize — kalau tidak, setiap
pengguna yang sekadar login bisa mengelola ban. Detailnya ada di
Admin API.
Performa dan tampilan
Section titled “Performa dan tampilan”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
performance.max_uri_length |
SHIELD_MAX_URI_LENGTH |
2048 |
Batas panjang URL. |
performance.ban_cache_ttl_seconds |
SHIELD_BAN_CACHE_TTL |
30 |
Lama ban aktif disimpan di cache. |
views.blocked |
SHIELD_VIEW_BLOCKED |
shield::blocked |
Tampilan halaman blokir. |
views.challenge |
SHIELD_VIEW_CHALLENGE |
shield::challenge |
Tampilan halaman challenge. |
branding.title |
SHIELD_BRANDING_TITLE |
Ganadev Laravel Shield |
Judul di halaman blokir dan challenge. |
branding.accent_color |
SHIELD_BRANDING_ACCENT |
#22d3ee |
Warna aksen. |
branding.background_color |
SHIELD_BRANDING_BG |
#0b1220 |
Warna latar. |
branding.show_rule_id |
SHIELD_BRANDING_SHOW_RULE_ID |
false |
Tampilkan rule id di halaman blokir. |
Trusted cookie
Section titled “Trusted cookie”| Kunci | Env | Default | Keterangan |
|---|---|---|---|
trusted.enabled |
— | true |
Ingat pengunjung yang sudah lolos challenge. |
trusted.ttl_minutes |
SHIELD_TRUSTED_TTL_MINUTES |
60 |
Lama cookie trusted berlaku. |
Cookie trusted terikat pada IP, User-Agent, dan prefix jaringan asal, jadi
tidak bisa dipinjam dari komputer atau IP lain. Trusted cookie hanya melewati
keputusan challenge; ban aktif dan signature critical tetap ditegakkan.
Nilai moderat seperti 30 sampai 120 menit biasanya cukup. Makin panjang, makin lama cookie itu tetap berlaku di browser yang tertinggal di komputer bersama.
Allowlist
Section titled “Allowlist”'allowlist' => [ 'hosts' => [], 'paths' => [], 'ips' => [],],Daftar ini mengecualikan request dari pemrosesan. Tetap ingat: tidak pernah mengecualikan aturan kritis.
Aturan penulisan allowlist.paths
Section titled “Aturan penulisan allowlist.paths”| Entri | Hasil |
|---|---|
'' atau '/' |
Ditolak. Pencocokan prefix akan mengecualikan seluruh request. |
Tanpa / di depan |
Ditolak. 'admin' harus ditulis '/admin'. |
| Mengandung query string | Ditolak. Contohnya '/admin?debug=1'. |
| Ada spasi di sekitar | Otomatis di-trim. |
Untuk mengecualikan satu host penuh, pakai allowlist.hosts atau
allowlist.ips, bukan allowlist.paths.
Validasi ini dijalankan saat aplikasi boot. Nilai yang salah akan membuat
aplikasi gagal start dengan pesan InvalidConfigException — lebih baik gagal
dini daripada berjalan dengan allowlist yang keliru.
api.paths dan rules.skip_paths mengikuti aturan yang sama, kecuali keduanya
menolak nilai '/' dan string kosong.
Contoh lengkap untuk produksi
Section titled “Contoh lengkap untuk produksi”SHIELD_ENABLED=trueSHIELD_MODE=challengeSHIELD_APP_ID=toko-onlineSHIELD_CHALLENGE=turnstileSHIELD_TURNSTILE_SITE_KEY=0x4AAAAAAA...SHIELD_TURNSTILE_SECRET_KEY=0x4AAAAAAA...SHIELD_BOT_MODE=observeSHIELD_LOG_LEVEL=suspiciousSHIELD_LOG_RETENTION_DAYS=14SHIELD_ADMIN_ENABLED=falseSHIELD_BRANDING_SHOW_RULE_ID=falseNaikkan SHIELD_MODE ke enforce setelah beberapa hari mode challenge
berjalan tanpa false positive yang berarti.
Powered by PT Ganadev Multi Solusi