Email header injection adalah celah keamanan yang memungkinkan attacker menyisipkan konten berbahaya ke email melalui input yang tidak disanitasi. PHP mail() function tidak melakukan sanitasi otomatis pada header parameters. Artikel ini anatomical breakdown serangan, code example vulnerability, dan step-by-step prevention di PHP native dan Laravel.
Email header injection terjadi ketika input dari user disisipkan langsung ke parameter header fungsi mail() di PHP tanpa sanitasi. Hasilnya? Attacker bisa menambah header arbitrary ke email yang dikirim dari server Anda.
Ini bukan bug baru. OWASP sudah mencantumkan Email Header Injection sebagai risiko keamanan seit 2013. Namun celah ini masih banyak ditemukan di aplikasi production karena tidak menyebabkan error yang mencolok. Email tetap terkirim, tidak ada crash, tidak ada warning di log.
Perbedaannya dengan bug lain: header injection tidak membuat aplikasi Anda error. Email tetap sampai. Tapi isinya sudah disisipkan konten yang attacker mau. Email header injection terjadi di layer yang berbeda dari masalah SMTP biasa.
Daftar Isi
Kenapa Email Header Injection Bisa Terjadi di PHP
Fungsi mail() di PHP punya signature seperti ini:
mail(string $to, string $subject, string $message, array|string $headers)
Parameter keempat adalah headers. Jika Anda membangun header dari input user seperti ini:
$headers = "From: " . $_POST['from_email'];
mail($to, $subject, $message, $headers);
Attacker bisa mengisi field dengan nilai:
[email protected]\r\nBcc: [email protected]
Karakter \r\n adalah CRLF sequence yang dalam konteks email berarti “header baru dimulai”. Server email akan mengartikan ini sebagai awal dari header baru bernama Bcc. Akibatnya email yang Anda kirim akan BCC ke address attacker tanpa Anda sadari.
CRLF injection ini sudah violates RFC 5322 Section 2.1 yang menyatakan bahwa setiap line dalam email header harus dipisahkan oleh CRLF, dan header field tidak boleh mengandung CRLF di luar konteks pemisahan header dari body.
Attacker bisa memanfaatkan karakter newline untuk melompat dari header yang Anda intend ke header baru yang attacker sisipkan. SMTP server yang menerima email ini memproses semua header secara berurutan.
Anatomi Serangan: Apa yang Bisa Attacker Lakukan
Dengan satu CRLF injection yang berhasil, attacker punya beberapa opsi.
Menyisipkan recipient tersembunyi. Attacker menambahkan header Bcc: atau Cc: dengan address mereka. Email yang seharusnya hanya sampai ke user, sekarang juga dikirim ke attacker. Untuk user yang menerima, email terlihat normal.
Mengubah subject line. Subject yang Anda set bisa di-overwrite oleh attacker yang menyisipkan Subject: baru setelah CRLF. Email tetap sampai tapi dengan subject yang berbeda.
Mengirim email phishing dari server Anda. Dengan From: yang diset ulang oleh attacker, email phishing tampak seolah-olah berasal dari server Anda. Reputasi server Anda yang terdampak, bukan domain attacker. Email yang sudah di-inject bisa masuk spam karena filter spam menandai header yang mencurigakan.
Melampirkan payload berbahaya. Attacker bisa menambah header Content-Type dan menyisipkan konten HTML berbahaya. Filter spam yang menerima email dari server Anda akan menandai server ini sebagai suspicious.
Semua ini terjadi tanpa satu pun error di log aplikasi Anda. Email terkirim dengan status 200 OK. Tidak ada exception. Tidak ada crash.
Code Vulnerability yang Harus Anda Cek di Aplikasi
Vulnerability paling umum ditemukan di beberapa pola kode yang sering developer tulis tanpa sadar.
Pola 1: Input user langsung jadi header
// VULNERABLE
$headers = "From: " . $_POST['email'];
mail($to, $subject, $message, $headers);
Pola 2: Input user di subject line tanpa sanitasi
// VULNERABLE
$subject = $_POST['subject'];
mail($to, $subject, $message, $headers);
Subject line dalam RFC 5322 juga tidak boleh mengandung CRLF. Namun banyak aplikasi yang treat subject sebagai freeform text dan langsung pass ke mail().
Pola 3: Custom header dari database tanpa validation
// VULNERABLE
$custom_header = $db->getValue("SELECT header FROM email_templates WHERE id=?", [$id]);
mail($to, $subject, $message, $custom_header);
Jika template berasal dari user input yang disimpan di database, celah yang sama tetap ada.
Pola 4: Multiple header dari array tanpa sanitasi
// VULNERABLE
$headers = [];
foreach ($_POST as $key => $value) {
if (strpos($key, 'header_') === 0) {
$headers[] = ucfirst(str_replace('header_', '', $key)) . ": " . $value;
}
}
mail($to, $subject, $message, implode("\r\n", $headers));
Attacker bisa mengirim POST request dengan field header_From, header_Bcc, atau header_Reply-To yang berisi address attacker.
Apakah Laravel Mail Aman dari Header Injection?
Laravel menggunakan SwiftMailer sebagai mail driver-nya. SwiftMailer secara default melakukan escaping pada header values menggunakan RFC 2047 encoding. Jika Anda menggunakan Laravel Mail facility secara benar, risiko header injection berkurang secara signifikan.
Namun ada kondisi di mana developer bisa inadvertently membuat vulnerability baru.
Menerapkan custom header secara langsung. Jika Anda menambahkan header menggunakan setHeader() secara langsung dengan nilai dari user input:
// POTENTIALLY VULNERABLE
$message->getHeaders()->setHeader('X-Custom-Header', $userInput);
Nilai $userInput yang tidak disanitasi tetap bisa menjadi vektor serangan.
Override Laravel Mail config dengan nilai eksternal. Beberapa aplikasi meng-override konfigurasi mail driver dengan nilai dari environment atau database yang tidak divalidasi. Ini membuka celah yang sama dengan pola vulnerable di atas.
Laravel Mail yang digunakan secara idiomatic dengan Mailable class dan protected properties cenderung aman karena SwiftMailer akan menghandle escaping. Yang perlu diwaspadai adalah ketika developer depart dari pola idiomatic dan bermain dengan header secara langsung.
Regex Sanitization untuk Email Header Input
Langkah pertama prevention adalah sanitasi input yang akan dipakai sebagai header values. Anda perlu menolak setiap input yang mengandung CRLF sequence.
Pattern yang Anda butuhkan:
function sanitizeHeaderValue(string $input): string {
// Hapus semua karakter CRLF
$sanitized = preg_replace('/[\r\n]/', '', $input);
return $sanitized;
}
Fungsi ini remove semua karakter \r (carriage return) dan \n (line feed) dari input. Ini adalah langkah pertama yang akan block sebagian besar serangan.
Untuk email address validation yang lebih lengkap:
function sanitizeEmailAddress(string $email): string|false {
// Trim dan sanitasi
$email = trim(preg_replace('/[\r\n]/', '', $email));
// Validasi format email
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return false;
}
return $email;
}
Setelah Anda sanitasi, baru email address itu aman untuk dipakai di header.
Untuk header yang mengikuti standar CAN-SPAM seperti From, Reply-To, dan List-Unsubscribe, selalu validasi bahwa address yang dimasukkan adalah email legitimate. Penggunaan address yang tidak divalidasi bisa membuat email melanggar CAN-SPAM compliance dan meningkatkan spam score.
Untuk subject line, pendekatan sanitasi yang sama berlaku:
function sanitizeSubject(string $subject): string {
// Batasi panjang, remove CRLF, trim whitespace
$subject = trim(preg_replace('/[\r\n]/', '', $subject));
return substr($subject, 0, 200);
}
Panjang subject line dalam RFC 5322 tidak punya batas strict, tapi 200 karakter sudah lebih dari cukup untuk use case manapun.
Prevention dengan Library yang Sudah Ada
Daripada mengimplementasi sanitasi sendiri, gunakan library yang sudah di-audit.
PHPMailer adalah library yang secara default melakukan sanitasi pada semua header values. Library ini secara aktif dimaintain dan sudah melalui security audit. Jika aplikasi Anda menggunakan PHPMailer, risiko header injection turun drastis asalkan Anda tidak bypass mekanisme security yang sudah built-in.
SwiftMailer yang menjadi basis Laravel Mail juga melakukan escaping secara default. SwiftMailer akan meng-encode header values yang mengandung karakter khusus sesuai RFC 2047.
Symfony Mailer yang menjadi default mailer di Symfony 5 ke atas dan tersedia untuk Laravel via package punya pendekatan serupa dengan SwiftMailer.
Jika aplikasi Anda saat ini menggunakan mail() native, pertimbangkan untuk migrasi ke salah satu library ini. Effort migrasi sebanding dengan security improvement yang Anda dapat.
Checklist untuk Mendeteksi Vulnerability di Aplikasi Anda
Gunakan checklist ini untuk audit cepat.
- Cek setiap penggunaan fungsi mail() di codebase Anda
- Identifikasi apakah ada parameter yang berisi user input
- Jika ada, cek apakah ada sanitasi preg_replace(‘/[\r\n]/’, ‘’, …) sebelum input masuk ke mail()
- Cek apakah aplikasi menggunakan SwiftMailer atau PHPMailer. Jika ya, pastikan tidak ada custom header manipulation yang bypass library safety
- Cek template email yang disimpan di database. Validasi apakah ada sanitasi sebelum disimpan
- Cek email subject: apakah user bisa set subject tanpa panjang limit dan tanpa CRLF sanitasi
- Test dengan payload sederhana: kirim input yang mengandung “\r\nBcc: [email protected]” dan verifikasi apakah email benar-benar sampai ke address attacker
Mitigasi di Level SMTP dengan KIRIM.EMAIL
Semua mitigation di level aplikasi tidak berarti jika SMTP provider Anda tidak melakukan validasi tambahan di sisi server. KIRIM.EMAIL Dev menambahkan layer validasi pada inbound email dari client sebelum diproses lebih lanjut, yang membantu menangkap payload mencurigakan lebih awal.
Jika Anda mengirim email transaksional dengan PHP atau Laravel, menggunakan SMTP provider yang punya reputasi bagus akan membantu. Ketika email keluar dari server KIRIM.EMAIL, header sudah terstruktur dengan benar dan tidak ada CRLF injection yang tersisa.
Langkah terakhir yang tidak boleh dilupakan: selalu test email yang dikirim dari aplikasi Anda dengan mengecek raw email headers. Ini satu-satunya cara untuk verify bahwa tidak ada header tambahan yang disisipkan tanpa Anda sadari.
Jika Anda butuh SMTP server yang secara default menangani autentikasi email dengan benar dan bisa diintegrasikan dengan Laravel atau PHP native tanpa perlu khawatir soal header security, KIRIM.EMAIL Dev menyediakan endpoint SMTP yang sudah dikonfigurasi dengan benar untuk aplikasi production di Indonesia.
FAQ
Kenapa email header injection bisa terjadi di PHP?
PHP mail() function tidak melakukan validasi atau sanitasi otomatis pada parameter header. Jika developer membangun header string dari user input tanpa sanitasi, attacker bisa menyisipkan karakter CRLF (\r\n) untuk menambah header arbitrary. Karakter newline ini dalam konteks email berarti “header baru dimulai”, sehingga attacker bisa menyisipkan Bcc, Cc, atau Subject baru tanpa terdeteksi.
Apa bedanya header injection dengan body injection?
Header injection terjadi ketika attacker menambah atau memodifikasi header email dengan menyisipkan CRLF sequence di parameter header. Body injection terjadi ketika attacker menambahkan konten di body email dengan menyisipkan CRLF di antara header dan body. Keduanya memanfaatkan ketidak-validasi input yang sama, tapi body injection lebih sulit dieksploitasi karena body email tidak diproses sebagai headers.
Bagaimana cara mendeteksi apakah aplikasi saya vulnerable?
Cek setiap penggunaan mail(), SwiftMailer, atau PHPMailer di codebase Anda. Identifikasi apakah ada user input yang masuk langsung ke parameter headers, subject, atau from address. Test dengan payload “\r\nBcc: [email protected]” dan verifikasi apakah email sampai ke address attacker. Jika ya, aplikasi Anda vulnerable.
Apakah Laravel mail() function aman dari header injection secara default?
Laravel menggunakan SwiftMailer yang secara default melakukan escaping pada header values menggunakan RFC 2047 encoding. Namun risiko tetap ada jika developer melakukan custom header manipulation dengan nilai dari user input secara langsung. Penggunaan Laravel Mail dengan idiomatic pattern (Mailable class) cenderung aman. Yang harus dihindari adalah setHeader() dengan nilai user input yang tidak disanitasi.
Apa regex yang tepat untuk sanitasi email header input?
Pattern yang benar adalah preg_replace(‘/[\r\n]/’, ‘’, $input). Fungsi ini menghapus semua karakter carriage return (CR) dan line feed (LF) dari input. Jangan gunakan preg_replace(‘/[\r\n]/’, ’ ‘, $input) karena spasi tidak menghilangkan celah keamanan. CRLF harus di-hapus sama sekali, bukan di-replace dengan karakter lain.
Bagaimana implementasi yang benar untuk custom email headers di PHP?
Gunakan library seperti SwiftMailer atau PHPMailer yang secara otomatis melakukan escaping pada header values. Jika harus menggunakan mail() native, selalu sanitasi input dengan preg_replace(‘/[\r\n]/’, ‘’, $input) sebelum memasukkan ke parameter header. Pisahkan header name dan header value, validasi email address dengan filter_var(FILTER_VALIDATE_EMAIL), dan jangan pernah membangun header string dengan konkatenasi langsung dari user input.
Apakah menggunakan library SwiftMailer atau PHPMailer menjamin keamanan dari header injection?
SwiftMailer dan PHPMailer secara default menyediakan proteksi terhadap header injection untuk use case normal. Namun guarantee ini hanya berlaku jika Anda tidak bypass mekanisme security library dengan melakukan setHeader() secara langsung menggunakan nilai yang tidak divalidasi. Library ini memitigasi risiko dari penggunaan idiomatic, tapi tidak bisa melindungi jika developer secara aktif circumvent safety mechanism.
Hasbi Putra adalah Head of Marketing di KIRIM.EMAIL, email delivery infrastructure untuk developer dan tim IT di Indonesia. KIRIM.EMAIL mengirim lebih dari 11 juta email per hari dengan server yang sepenuhnya berlokasi di Indonesia.
- Cara Monitor Email Sent Logs dan Audit Trail di Sistem Transaksional untuk Debugging dan Compliance - April 29, 2026
- Cara Debug Email Sending dengan Email Leak Testing untuk Identifikasi Privacy Leak di Aplikasi Web - April 28, 2026
- Cara Setup Email Queue dengan Background Worker di Node.js untuk Aplikasi High-Volume - April 27, 2026
