Server tidak melihat pengunjung, hanya melihat request
GET), path, header Host, dan beberapa header lain yang menggambarkan konteks pengunjung. Dua header yang paling penting di kasus ini adalah User-Agent dan Referer.
User-Agent adalah teks yang dikirim browser untuk menyebut dirinya sendiri, misalnya Chrome di Android atau Safari di iPhone. Dari string ini, script mudah menebak apakah pengunjung memakai HP. Referer (ejaannya memang begitu di standar HTTP) berisi alamat halaman asal pengunjung sebelum klik. Kalau pengunjung datang dari hasil pencarian Google, browser modern biasanya hanya mengirim origin-nya, misalnya https://www.google.com/, bukan URL pencarian lengkap. Itu sudah cukup bagi malware yang cuma mencari kata google di header tersebut.
Selain header, server juga tahu IP asal koneksi TCP, plus cookie yang pernah disimpan di browser. Di PHP, semua ini tersedia sebagai variabel seperti _SERVER['HTTP_USER_AGENT'], _SERVER['HTTP_REFERER'], dan $_SERVER['REMOTE_ADDR'].
Sekarang bandingkan dua skenario. Pemilik situs mengetik alamat di laptop, jadi request-nya tidak membawa Referer dan User-Agent-nya desktop. Pengunjung yang klik dari Google di HP membawa Referer dari Google dan User-Agent mobile. Bagi server, ini dua request yang berbeda, dan malware cukup memberi jawaban berbeda untuk keduanya.
Di titik mana filternya bekerja
.htaccess dievaluasi sebelum PHP dipanggil. Penyerang bisa menambahkan RewriteCond yang mencocokkan %{HTTP_REFERER} dan %{HTTP_USER_AGENT}, lalu RewriteRule dengan flag [R=302] yang mengarah ke domain judi. Response redirect keluar langsung dari web server tanpa menyentuh WordPress sama sekali. Nginx tidak membaca .htaccess, jadi di server Nginx lapisan ini hanya mungkin lewat konfigurasi server itu sendiri, yang butuh akses lebih tinggi.
Lapisan kedua adalah PHP. Kode berbahaya disisipkan di awal file yang selalu dimuat, misalnya index.php, wp-config.php, functions.php milik theme, atau file yang di-load lewat auto_prepend_file. Kode itu memeriksa variabel $_SERVER tadi, lalu memanggil header('Location: ...') dan berhenti. Hasilnya sama seperti lapisan pertama dari sisi pengunjung, yaitu status 301 atau 302 dengan header Location.
Lapisan ketiga terjadi di browser. Server mengirim HTML normal dengan status 200, tapi di dalamnya ada JavaScript yang membaca document.referrer dan navigator.userAgent, lalu mengubah window.location. Script ini sering disimpan di database (opsi plugin, konten post, atau widget), jadi file situs bisa terlihat bersih.
Di atas filter dasar itu, biasanya ada kondisi tambahan yang membuat masalah ini makin sulit direproduksi. Sebagian script memasang cookie setelah redirect pertama, sehingga kunjungan kedua dari browser yang sama tampil normal. Ada yang mengecualikan pengguna yang sedang login ke WordPress, jadi admin tidak pernah melihatnya. Ada juga yang mencatat IP yang sudah pernah di-redirect. Lalu untuk Googlebot, script sering menyajikan halaman berisi kata kunci judi tanpa redirect. Teknik ini disebut cloaking, dan inilah sebabnya judul atau deskripsi situs di hasil pencarian ikut berubah. Tujuannya sederhana: menangkap trafik dari pencarian selama mungkin tanpa ketahuan pemilik situs.
Meniru request pengunjung dengan curl
example.com dengan domain dan path yang memang muncul di hasil pencarian Google. Sebaiknya jangan hanya menguji homepage, karena sebagian malware hanya aktif di halaman artikel.
BASH
# Contoh User-Agent Chrome Android, sesuaikan bila perlu
UA='Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36'
# 1. Seperti pemilik situs mengetik alamat langsung
curl -s -o /dev/null -D - https://example.com/contoh-artikel/
# 2. Seperti pengunjung HP yang klik dari Google
curl -s -o /dev/null -D - -A "$UA" -e 'https://www.google.com/' https://example.com/contoh-artikel/
# 3. Seperti Googlebot, untuk melihat indikasi cloaking (cek isi title-nya)
curl -s -A 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' https://example.com/contoh-artikel/ | grep -i '<title>'-A mengisi User-Agent, -e mengisi Referer, -D - mencetak header response ke layar, dan -o /dev/null membuang body supaya output tetap ringkas. Kita sengaja memakai GET biasa, bukan -I. -I mengirim request HEAD, dan sebagian script atau konfigurasi hanya bereaksi pada GET, jadi hasil HEAD bisa terlihat bersih padahal tidak.
Kalau tes kedua memicu redirect, output-nya kira-kira seperti ini. Ini contoh format output, bukan dari kasus nyata:
TEXT
HTTP/1.1 302 Found
Date: ...
Server: ...
Location: https://domain-judi.example/
Content-Type: text/html; charset=UTF-8Location ke domain asing. Berarti redirect terjadi di sisi server, di lapisan rewrite atau PHP. Kalau ingin tahu lebih jauh soal membaca rantai header redirect, polanya mirip dengan yang dibahas di Melacak Sumber Redirect Loop HTTPS Lewat Header Respons curl. Coba juga variasi kecil, misalnya Referer dari https://www.google.co.id/ atau User-Agent iPhone. Kombinasi yang memicu redirect itu adalah petunjuk tentang kondisi yang ditulis penyerang.
Kalau header bersih, periksa isi HTML-nya
Location, jangan buru-buru menyimpulkan situsnya aman. Redirect lewat JavaScript tidak terlihat di header, dan curl tidak menjalankan JavaScript. Kita perlu mengambil body-nya dan mencari pola yang mencurigakan.
BASH
# Cari potongan script yang membaca referrer atau memindahkan halaman
curl -s --compressed -A "$UA" -e 'https://www.google.com/' https://example.com/contoh-artikel/ | grep -ioE '.{0,60}(document.referrer|window.location|atob|fromCharCode|eval).{0,60}' | head -n 20
# Daftar script eksternal yang dimuat halaman
curl -s --compressed -A "$UA" -e 'https://www.google.com/' https://example.com/contoh-artikel/ | grep -ioE '<script[^>]*src=[^>]*>'window.location. Yang perlu dicurigai adalah kode yang di-obfuscate (disamarkan dengan atob, fromCharCode, atau string acak yang panjang), atau kode yang membandingkan referrer dengan kata google.
Perintah kedua menampilkan semua script eksternal. Perhatikan domain yang tidak kamu kenal. Isi script eksternal tidak ikut terambil oleh curl, jadi ambil URL-nya dan periksa secara terpisah dengan cara yang sama.
Kenapa hasil curl bersih belum tentu berarti situs bersih
?cek=1 kadang membuat request melewati cache, tapi ini tergantung konfigurasi cache-nya. Sebagian cache memang mengabaikan query string.
Ketiga, verifikasi Googlebot. Malware yang lebih rapi tidak percaya pada User-Agent saja. Ia mengecek apakah IP pengirim benar-benar milik Google. Tes ketiga tadi tidak bisa memalsukan IP. Untuk melihat apa yang benar-benar diterima Googlebot, gunakan fitur inspeksi URL di Google Search Console, yang bisa menampilkan HTML hasil render versi Google.
Keempat, kondisi yang lebih acak, misalnya hanya aktif di jam tertentu atau hanya untuk sebagian kecil request. Kalau keluhan pengunjung konsisten tapi tes kita selalu bersih, ulangi tes beberapa kali dan periksa access log. Cari request dengan referer Google yang mendapat status 302, karena access log mencatat status yang benar-benar dikirim ke pengunjung asli.
Dari sisi support, langkah pembuktian ini penting karena mengubah percakapan. Keluhannya tidak lagi sekadar katanya pengunjung dilempar ke situs judi, tapi menjadi request dengan Referer Google dan User-Agent Android mendapat 302 ke domain tertentu. Bukti seperti ini lebih mudah diterima pemilik situs dan lebih mudah ditindaklanjuti tim teknis.
Jadi, sebelum membuka file manager atau menjalankan scanner, buktikan dulu redirect-nya dengan curl dan catat kombinasi header yang memicunya, status code, serta isi Location. Kalau redirect muncul di header, mulai cari dari .htaccess lalu file PHP yang selalu dimuat. Kalau redirect hanya muncul di body sebagai JavaScript, kemungkinan besar sumbernya ada di database atau di script eksternal. Setelah itu, perlakukan situs sebagai situs yang sudah disusupi, bukan sekadar salah konfigurasi. Artinya, cari celah masuknya juga, jangan hanya menghapus kode redirect-nya.