Untuk kode keamanan, jangan buat token Anda dengan cara ini: $token = md5(uniqid(rand(), TRUE));
Coba ini:
Menghasilkan Token CSRF
PHP 7
session_start();
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
Catatan: Salah satu proyek open source perusahaan saya adalah inisiatif untuk mendukung random_bytes()
dan random_int()
menjadi proyek PHP 5. Ini berlisensi MIT dan tersedia di Github dan Composer sebagai paragonie / random_compat .
PHP 5.3+ (atau dengan ext-mcrypt)
session_start();
if (empty($_SESSION['token'])) {
if (function_exists('mcrypt_create_iv')) {
$_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
} else {
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
}
}
$token = $_SESSION['token'];
Memverifikasi Token CSRF
Jangan hanya menggunakan ==
atau bahkan ===
, gunakan hash_equals()
(hanya PHP 5.6+, tetapi tersedia untuk versi sebelumnya dengan pustaka hash-compat ).
if (!empty($_POST['token'])) {
if (hash_equals($_SESSION['token'], $_POST['token'])) {
// Proceed to process the form data
} else {
// Log this as a warning and keep an eye on these attempts
}
}
Melangkah Lebih Jauh dengan Token Per-Form
Anda selanjutnya dapat membatasi token agar hanya tersedia untuk formulir tertentu dengan menggunakan hash_hmac()
. HMAC adalah fungsi hash berkunci tertentu yang aman digunakan, bahkan dengan fungsi hash yang lebih lemah (misalnya MD5). Namun, saya merekomendasikan menggunakan keluarga SHA-2 dari fungsi hash sebagai gantinya.
Pertama, buat token kedua untuk digunakan sebagai kunci HMAC, lalu gunakan logika seperti ini untuk merendernya:
<input type="hidden" name="token" value="<?php
echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
Dan kemudian menggunakan operasi yang kongruen saat memverifikasi token:
$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
// Continue...
}
Token yang dihasilkan untuk satu formulir tidak dapat digunakan kembali dalam konteks lain tanpa diketahui $_SESSION['second_token']
. Anda harus menggunakan token terpisah sebagai kunci HMAC daripada yang baru saja Anda jatuhkan di halaman.
Bonus: Pendekatan Hibrid + Integrasi Ranting
Siapa pun yang menggunakan mesin template Twig dapat memanfaatkan strategi ganda yang disederhanakan dengan menambahkan filter ini ke lingkungan Twig mereka:
$twigEnv->addFunction(
new \Twig_SimpleFunction(
'form_token',
function($lock_to = null) {
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
if (empty($_SESSION['token2'])) {
$_SESSION['token2'] = random_bytes(32);
}
if (empty($lock_to)) {
return $_SESSION['token'];
}
return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
}
)
);
Dengan fungsi Twig ini, Anda dapat menggunakan kedua token tujuan umum seperti:
<input type="hidden" name="token" value="{{ form_token() }}" />
Atau varian yang dikunci:
<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
Twig hanya berhubungan dengan rendering template; Anda masih harus memvalidasi token dengan benar. Menurut pendapat saya, strategi Twig menawarkan fleksibilitas dan kesederhanaan yang lebih besar, sambil mempertahankan kemungkinan untuk keamanan maksimum.
Token CSRF Sekali Pakai
Jika Anda memiliki persyaratan keamanan bahwa setiap token CSRF diizinkan untuk digunakan tepat sekali, strategi paling sederhana membuatnya kembali setelah setiap validasi berhasil. Namun, hal itu akan membatalkan setiap token sebelumnya yang tidak cocok dengan orang-orang yang menjelajahi banyak tab sekaligus.
Paragon Initiative Enterprises memiliki perpustakaan Anti-CSRF untuk kasus sudut ini. Ia bekerja dengan token sekali pakai per bentuk, secara eksklusif. Ketika cukup token disimpan dalam data sesi (konfigurasi default: 65535), itu akan menggilir token tertua yang belum ditebus terlebih dahulu.
token_time
digunakan?