Indonesian (Bahasa Indonesia) translation by Ari Gustiawan (you can also view the original English article)
Keamanan adalah topik panas. Memastikan bahwa website Anda aman sangat penting untuk aplikasi web. Bahkan, aku menghabiskan 70% dari waktu saya mengamankan aplikasi saya. Salah satu hal yang paling penting kita harus aman adalah form. Hari ini, kita akan meninjau metode untuk mencegah XSS (Cross-site scripting) dan Cross-site request forgery di form.
Mengapa?
POST data dapat dikirim dari satu situs web lain. Mengapa hal ini buruk? Skenario sederhana...
User, login ke situs web Anda, mengunjungi situs web lain selama sesi nya. Website ini akan dapat mengirim POST data ke website Anda--misalnya, dengan AJAX. Karena user login di situs Anda, situs web lain juga akan dapat mengirim post data untuk mengamankan yang hanya dapat diakses setelah login.
Kita juga harus melindungi halaman kami terhadap serangan menggunakan cURL
Bagaimana kita memperbaikinya?
Dengan form keys! Kami akan menambahkan hash khusus (form key) untuk setiap form untuk memastikan bahwa data hanya dapat diproses ketika itu telah dikirim dari website Anda. Setelah submit form, script PHP kami akan memvalidasi form key yang di submit juga form key yang kita set di session.
Apa yang harus kita lakukan:
- Tambahkan form key untuk setiap form.
- Menyimpan form key dalam session.
- Memvalidasi form key setelah submit form.
Langkah 1: Simpple Form
Pertama kita perlu form sederhana untuk keperluan demonstrasi. Salah satu form yang paling penting kita harus amankan adalah login form. Login form adalah rentan terhadap brute force attacks . Buat sebuah file baru dan Simpan sebagai index.php di web Anda root. Tambahkan kode berikut dalam body:
1 |
|
2 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3 |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
4 |
<head>
|
5 |
<meta http-equiv="content-type" content="text/html;charset=UTF-8" /> |
6 |
<title>Securing forms with form keys</title> |
7 |
</head>
|
8 |
<body>
|
9 |
<form action="" method="post"> |
10 |
<dl>
|
11 |
<dt><label for="username">Username:</label></dt> |
12 |
<dd><input type="text" name="username" id="username" /></dd> |
13 |
<dt><label for="username">Password:</label></dt> |
14 |
<dd><input type="password" name="password" id="password" /></dd> |
15 |
<dt></dt>
|
16 |
<dd><input type="submit" value="Login" /></dd> |
17 |
</dl>
|
18 |
</form>
|
19 |
</body>
|
20 |
</html>
|
Sekarang kita memiliki halaman XHTML yang sederhana dengan login form. Jika Anda ingin menggunakan form ley website Anda, Anda dapat mengganti script diatas dengan halaman login Anda sendiri. Sekarang, mari kita lanjutkan untuk tindakan nyata.
Langkah 2: Membuat kelas
Kita akan membuat PHP class untuk form key. Karena setiap halaman dapat mengandung hanya satu form key, kita bisa membuat singleton kelas kami untuk memastikan bahwa kelas kami digunakan dengan benar. Karena menciptakan singletons OOP topik yang lebih advance, kami akan melewatkan bagian ini. Buat sebuah file baru yang disebut formkey.class.php dan menempatkannya di web Anda root. Sekarang kita harus berpikir tentang fungsi-fungsi yang kita butuhkan. Pertama, kita perlu fungsi untuk menghasilkan sebuah kunci bentuk sehingga kita dapat menempatkan dalam form. Dalam file PHP Anda menempatkan kode berikut:
1 |
|
2 |
<?php
|
3 |
|
4 |
//You can of course choose any name for your class or integrate it in something like a functions or base class
|
5 |
class formKey |
6 |
{
|
7 |
//Here we store the generated form key
|
8 |
private $formKey; |
9 |
|
10 |
//Here we store the old form key (more info at step 4)
|
11 |
private $old_formKey; |
12 |
|
13 |
//Function to generate the form key
|
14 |
private function generateKey() |
15 |
{
|
16 |
|
17 |
}
|
18 |
}
|
19 |
?>
|
Di atas, Anda melihat kelas dengan tiga bagian: dua variabel dan fungsi. Kami membuat fungsi private karena fungsi ini hanya boleh digunakan oleh outputfunctions kami, yang kita akan buat nanti. Dalam dua variabel, kami akan menyimpan form key. Ini juga private karena mereka hanya dapat digunakan oleh fungsi di dalam kelas kami.
Sekarang, kita harus memikirkan cara untuk menghasilkan form key kami. Karena form key kami harus unik (jika tidak, kami tidak memiliki efek apapun), kita menggunakan kombinasi dari pengguna alamat IP untuk mengikat key pengguna, mt_rand() untuk membuatnya unik dan fungsi uniqid() untuk membuatnya lebih unik. Kami juga mengenkripsi informasi ini dengan md5() untuk membuat hash unik yang kita kemudian dapat menyisipkan ke halaman kami. Karena kita menggunakan md5(), pengguna tidak dapat melihat apa yang kita digunakan untuk menghasilkan kunci. Seluruh fungsi:
1 |
|
2 |
//Function to generate the form key
|
3 |
private function generateKey() |
4 |
{
|
5 |
//Get the IP-address of the user
|
6 |
$ip = $_SERVER['REMOTE_ADDR']; |
7 |
|
8 |
//We use mt_rand() instead of rand() because it is better for generating random numbers.
|
9 |
//We use 'true' to get a longer string.
|
10 |
//See http://www.php.net/mt_rand for a precise description of the function and more examples.
|
11 |
$uniqid = uniqid(mt_rand(), true); |
12 |
|
13 |
//Return the hash
|
14 |
return md5($ip . $uniqid); |
15 |
}
|
Masukkan kode di atas ke dalam file formkey.class.php Anda. Menggantikan fungsi dengan fungsi baru.
Langkah 3: Memasukkan Form key ke Form
Untuk langkah ini, kami membuat sebuah fungsi baru yang output bidang HTML tersembunyi dengan form key kami. Fungsi terdiri dari tiga langkah:
- Menghasilkan kunci formulir dengan fungsi generateKey() kami.
- Menyimpan form key dalam variabel $formKey kami dan dalam session.
- Bidang output HTML.
Kami nama kami outputKey() fungsi dan membuat publik, karena kita harus menggunakannya di luar kelas kami. Fungsi kita akan memanggil fungsi private generateKey() untuk menghasilkan form key baru dan menyimpannya secara lokal dalam session. Akhirnya, kita membuat kode XHTML. Sekarang tambahkan kode berikut di dalam kelas PHP kami:
1 |
|
2 |
//Function to output the form key
|
3 |
public function outputKey() |
4 |
{
|
5 |
//Generate the key and store it inside the class
|
6 |
$this->formKey = $this->generateKey(); |
7 |
//Store the form key in the session
|
8 |
$_SESSION['form_key'] = $this->formKey; |
9 |
|
10 |
//Output the form key
|
11 |
echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />"; |
12 |
}
|
Sekarang, kita akan menambahkan form key ke form login untuk mengamankan itu. Kita harus mencakup kelas di file index.php kami. Kita juga harus mulai session karena kelas kami menggunakan session untuk menyimpan key yang digenerate. Untuk ini, kita menambahkan kode berikut di atas doctype dan tag head:
1 |
|
2 |
<?php
|
3 |
//Start the session
|
4 |
session_start(); |
5 |
//Require the class
|
6 |
require('formkey.class.php'); |
7 |
//Start the class
|
8 |
$formKey = new formKey(); |
9 |
?>
|
Kode di atas cukup jelas. Kita mulai sesi (karena kami menyimpan form key) dan memuat file kelas PHP. Setelah itu, kita mulai kelas dengan new formKey(), ini akan membuat kelas kami dan menyimpannya dalam $formKey. Sekarang kita hanya perlu mengedit form agar berisi form key:
1 |
|
2 |
<form action="" method="post"> |
3 |
<dl>
|
4 |
<?php $formKey->outputKey(); ?>
|
5 |
<dt><label for="username">Username:</label></dt> |
6 |
<dd><input type="text" name="username" id="username" /></dd> |
7 |
<dt><label for="username">Password:</label></dt> |
8 |
<dd>input type="password" name="password" id="password" /></dd> |
9 |
<dl>
|
10 |
</form>
|
Dan itu semua! Karena kami membuat outputKey() fungsi, kita hanya harus memasukkannya ke dalam form. Kita dapat menggunakan form key dalam setiap form dengan hanya menambahkan <?php $formKey-> outputKey();? > Sekarang hanya review sumber halaman web Anda dan Anda dapat melihat bahwa ada sebuah form key yang disisipkan ke dalam form. Satu-satunya langkah yang tersisa adalah untuk memvalidasi request.
Langkah 4: memvalidasi
Kami tidak akan memvalidasi form secara keseluruhan; hanya form key. Validasi form adalah dasar PHP dan tutorial dapat ditemukan di seluruh web. Mari kita memvalidasi form key. Karena fungsi "generateKey" kami akan menimpa nilai session, kami menambahkan konstruktor kelas PHP kami. Constructor yang akan dipanggil ketika kelas kami menciptakan (atau dibangun). Konstruktor akan menyimpan kunci sebelumnya di dalam kelas sebelum kita membuat yang baru; Jadi kita akan selalu memiliki form key sebelumnya untuk validasi form. Jika kita tidak melakukan ini, kita tidak akan mampu memvalidasi form key. Tambahkan fungsi PHP berikut untuk kelas Anda:
1 |
|
2 |
//The constructor stores the form key (if one exists) in our class variable.
|
3 |
function __construct() |
4 |
{
|
5 |
//We need the previous key so we store it
|
6 |
if(isset($_SESSION['form_key'])) |
7 |
{
|
8 |
$this->old_formKey = $_SESSION['form_key']; |
9 |
}
|
10 |
}
|
Constructor yang harus selalu bernama __construct(). Ketika constructor disebut kami memeriksa jika session di set, dan jika demikian, kami menyimpan itu secara lokal dalam variabel old_formKey kami.
Sekarang kami mampu memvalidasi form key kami. Kami membuat fungsi dasar di dalam kelas kami yang memvalidasi form key. Fungsi ini juga harus public karena kami akan menggunakannya di luar kelas kami. Fungsi akan memvalidasi nilai POST form key terhadap nilai tersimpan di form key. Tambahkan fungsi ini PHP class:
1 |
|
2 |
//Function that validated the form key POST data
|
3 |
public function validate() |
4 |
{
|
5 |
//We use the old formKey and not the new generated version
|
6 |
if($_POST['form_key'] == $this->old_formKey) |
7 |
{
|
8 |
//The key is valid, return true.
|
9 |
return true; |
10 |
}
|
11 |
else
|
12 |
{
|
13 |
//The key is invalid, return false.
|
14 |
return false; |
15 |
}
|
16 |
}
|
Dalam index.php, memvalidasi form key dengan menggunakan fungsi yang kami hanya dibuat di kelas kami. Tentu saja, kami hanya memvalidasi setelah permintaan POST. Tambahkan kode berikut setelah $formKey = new formKey();
1 |
|
2 |
$error = 'No error'; |
3 |
|
4 |
//Is request?
|
5 |
if($_SERVER['REQUEST_METHOD'] == 'post') |
6 |
{
|
7 |
//Validate the form key
|
8 |
if(!isset($_POST['form_key']) || !$formKey->validate()) |
9 |
{
|
10 |
//Form key is invalid, show an error
|
11 |
$error = 'Form key error!'; |
12 |
}
|
13 |
else
|
14 |
{
|
15 |
//Do the rest of your validation here
|
16 |
$error = 'No form key error!'; |
17 |
}
|
18 |
}
|
Kami membuat sebuah variabel $error yang menyimpan pesan error kita. Jika permintaan POST telah dikirim kami memvalidasikan formkey dengan $formKey->validate(). Jika ini mengembalikan false, form key tidak valid dan kami menampilkan pesan error. Perhatikan bahwa kita hanya memvalidasi form key--Anda diharapkan untuk memvalidasi seluruh form sendiri.
Dalam HTML, Anda dapat menempatkan kode berikut untuk menampilkan pesan error:
1 |
|
2 |
<div><?php if($error) { echo($error); } ?></div> |
Ini akan echo variabel $error jika sudah di set.



Jika Anda mulai server Anda dan pergi ke index.php, Anda akan melihat form dan pesan 'No error'. Ketika Anda mengirimkan form Anda akan melihat pesan 'No form key error' karena itu adalah permintaan POST yang valid. Sekarang cobalah untuk reload halaman dan menerima ketika peramban permintaan bahwa POST data dikirim kembali. Anda akan melihat bahwa script kami trigger pesan error: 'Form key error!' Form sekarang dilindungi terhadap masukan dari situs web lain dan kesalahan dengan halaman ulang! error juga ditampilkan setelah refresh karena form key baru dibuat setelah kami submit form. Ini baik karena, sekarang, pengguna tidak sengaja tidak dapat memposting form dua kali.
Kode lengkap
Di sini adalah seluruh kode PHP dan HTML:
index.php
1 |
|
2 |
<?php
|
3 |
//Start the session
|
4 |
session_start(); |
5 |
//Require the class
|
6 |
require('formkey.class.php'); |
7 |
//Start the class
|
8 |
$formKey = new formKey(); |
9 |
|
10 |
$error = 'No error'; |
11 |
|
12 |
//Is request?
|
13 |
if($_SERVER['REQUEST_METHOD'] == 'post') |
14 |
{
|
15 |
//Validate the form key
|
16 |
if(!isset($_POST['form_key']) || !$formKey->validate()) |
17 |
{
|
18 |
//Form key is invalid, show an error
|
19 |
$error = 'Form key error!'; |
20 |
}
|
21 |
else
|
22 |
{
|
23 |
//Do the rest of your validation here
|
24 |
$error = 'No form key error!'; |
25 |
}
|
26 |
}
|
27 |
?>
|
28 |
|
29 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
30 |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
31 |
<head>
|
32 |
<meta http-equiv="content-type" content="text/html;charset=UTF-8" /> |
33 |
<title>Securing forms with form keys</title> |
34 |
</head>
|
35 |
<body>
|
36 |
<div><?php if($error) { echo($error); } ?> |
37 |
<form action="" method="post"> |
38 |
<dl>
|
39 |
<?php $formKey->outputKey(); ?> |
40 |
|
41 |
<dt><label for="username">Username:</label></dt> |
42 |
<dd><input type="text" name="username" id="username" /></dd> |
43 |
<dt><label for="username">Password:</label></dt> |
44 |
<dd><input type="password" name="password" id="password" /></dd> |
45 |
<dt></dt>
|
46 |
<dd><input type="submit" value="Submit" /></dd> |
47 |
<dl>
|
48 |
</form>
|
49 |
</body>
|
50 |
</html>
|
fomrkey.class.php
1 |
|
2 |
<?php
|
3 |
|
4 |
//You can of course choose any name for your class or integrate it in something like a functions or base class
|
5 |
class formKey |
6 |
{
|
7 |
//Here we store the generated form key
|
8 |
private $formKey; |
9 |
|
10 |
//Here we store the old form key (more info at step 4)
|
11 |
private $old_formKey; |
12 |
|
13 |
//The constructor stores the form key (if one excists) in our class variable
|
14 |
function __construct() |
15 |
{
|
16 |
//We need the previous key so we store it
|
17 |
if(isset($_SESSION['form_key'])) |
18 |
{
|
19 |
$this->old_formKey = $_SESSION['form_key']; |
20 |
}
|
21 |
}
|
22 |
|
23 |
//Function to generate the form key
|
24 |
private function generateKey() |
25 |
{
|
26 |
//Get the IP-address of the user
|
27 |
$ip = $_SERVER['REMOTE_ADDR']; |
28 |
|
29 |
//We use mt_rand() instead of rand() because it is better for generating random numbers.
|
30 |
//We use 'true' to get a longer string.
|
31 |
//See http://www.php.net/mt_rand for a precise description of the function and more examples.
|
32 |
$uniqid = uniqid(mt_rand(), true); |
33 |
|
34 |
//Return the hash
|
35 |
return md5($ip . $uniqid); |
36 |
}
|
37 |
|
38 |
|
39 |
//Function to output the form key
|
40 |
public function outputKey() |
41 |
{
|
42 |
//Generate the key and store it inside the class
|
43 |
$this->formKey = $this->generateKey(); |
44 |
//Store the form key in the session
|
45 |
$_SESSION['form_key'] = $this->formKey; |
46 |
|
47 |
//Output the form key
|
48 |
echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />"; |
49 |
}
|
50 |
|
51 |
|
52 |
//Function that validated the form key POST data
|
53 |
public function validate() |
54 |
{
|
55 |
//We use the old formKey and not the new generated version
|
56 |
if($_POST['form_key'] == $this->old_formKey) |
57 |
{
|
58 |
//The key is valid, return true.
|
59 |
return true; |
60 |
}
|
61 |
else
|
62 |
{
|
63 |
//The key is invalid, return false.
|
64 |
return false; |
65 |
}
|
66 |
}
|
67 |
}
|
68 |
?>
|
Kesimpulan
Menambahkan kode ini ke setiap form yang penting di website Anda akan meningkatkan keamanan form Anda secara dramatis. Bahkan berhenti menyegarkan masalah, seperti yang kita lihat di langkah 4. Karena form key hanya berlaku untuk satu permintaan, double posting ini tidak mungkin.
Ini adalah tutorial pertama saya, saya harap Anda seperti itu dan menggunakannya untuk meningkatkan keamanan Anda! Tolong beritahu saya tahu pikiran Anda, melalui komentar. Memiliki metode yang lebih baik? Marilah kita tahu.
Bacaan lebih lanjut
- WordPress juga menggunakan form key (menamakannya Nonces): Wordpress Nonces
- Tujuh kebiasaan untuk menulis aplikasi PHP yang aman
- Ikuti kami di Twitter, atau berlangganan NETTUTS RSS Feed untuk harian web pengembangan tuts dan artikel.