author Ahmad Muhardian

Cara Mengatasi Serangan XSS pada CodeIgniter


Cara Mengatasi Serangan XSS di Codeigniter

Yakin aplikasimu sudah aman dari serangan XSS?

Kalau saya tidak bisa 100% yakin, karena tidak ada sistem yang 100% aman!

Hehe, minjam kata-kata hekel ๐Ÿ˜„

Pada kesempatan ini, kita akan belajar tentang cara melindungi aplikasi webโ€“terutama yang dibuat dengan Codeigniterโ€“dari serangan XSS.

Selain itu, kita juga akan belajar melakukan serangan XSS agar tahu cara menghindarinya.

Baiklah..

Mari kita mulai!

Apa itu XSS?

XSS merupakan singkatan dari cross site scripting.

Alasan singkatan yang digunakan XSS bukan CSS, karena CSS sudah digunakan untuk cascade style sheet.

Jadi X untuk singkatan kata Cross.

XSS merupakan salah satu bentuk serangan pada aplikasi web yang dilakukan dengan menginjeksi kode javascript dari sisi client. 1

Dampak yang bisa diakbiatkan XSS:

  • Keamanan aplikasi web dapat di-bypass;
  • Data user dapat dicuri;
  • Halaman yang terkena XSS bisa dijadikan media Phising;
  • dan lain-lain.

Cara Mengatasi XSS di Codeigniter

“Untuk menangkap pencuri, kita harus berpikir seperti pencuri”

Mari kita pelajari bagaimana cara melakukan serangan XSS agar bisa menghindarinya.

1. Reflected XSS

Ini adalah jenis serangan XSS yang sering dilakukan attacker.

Biasanya terjadi pada input dari parameter query string yang kita tampilkan.

Contoh:

Misalkan kita punya fitur pencarian seperti ini:

<?php

class Search extends CI_Controller
{
	public function index()
	{
		$data["keyword"] = $this->input->get("keyword");

		// do search and render result to view
		$this->load->view('search', $data);
	}
}

Kemudian pada view, kita menampilkan $keyword yang diinputkan seperti ini:

<p>Hasil pencarian dengan kata kunci <?= $keyword ?></p>
<p>Hasil: ...</p>

Sekilas tidak ada masalah dengan kode ini.

Tapi coba inputkan keyword dengan kode Javascript seperti ini:

index.php/search/?keyword=<script>alert('hacked!')</script>

Maka hasilnya:

contoh hasil xss reflected

Bagaimana cara mengatasi serangan ini?

โœ… Solusinya:

Gunakan fungsi htmlentities() pada view untuk menampilkan variabel $keyword.

<p>Hasil pencarian dengan kata kunci <?= htmlentities($keyword) ?></p>
<p>Hasil: ...</p>

Maka sekarang halaman ini tidak akan bisa diserang XSS.

hasil fungsi htmlentities

Tapi jangan senang dulu..

Masih ada jenis serangan XSS yang lainnya:

2. Stored XSS

Stored XSS adalah serangan XSS yang dilakukan dengan memasukan injeksi XSS pada input yang disimpan ke database.

Sebenarnya tidak harus database, yang penting input itu disimpan di suatu tempat.

Nantinya, saat ditampilkan di halaman view.. kode injeksi XSS-nya akan dieksekusi.

Contohnya:

Misalkan kita membuat fitur untuk aplikasi lowongan pekerjaan. Input yang kita butuhkan untuk menambahkan lowongan baru sebagai berikut.

  • name nama atau judul lowongan kerja;
  • job_image_url alamat URL poster;
  • job_url alamat pendaftaran lowongan kerja.

Bentuk kode di Controller-nya seperti ini:

๐Ÿ“œ controllers/Jobvacancy.php

<?php

class Jobvacancy extends CI_Controller
{
	public function addjob()
	{
		if ($this->input->method() === "post") {
			$data["job"] = $this->input->post();

			// di sini biasanya dilakukan penyimpanan ke database

			return $this->load->view("list_job", $data);
		}

		$this->load->view('add_job_form');
	}
}

..dan berikut ini kode untuk view-nya.

๐Ÿ“œ views/list_job.php

<h1><?= $job["name"] ?></h1>
<img src='<?= $job["job_image_url"] ?>' />
<br>
<a href='<?= $job["job_url"] ?>'>Link Pendaftaran</a>

๐Ÿ“œ views/add_job_form.php

<form action="" method="post">
	<label for="name">Vacancy Job name</label>
	<input type="text" name="name" >
	<br>
	<label for="job_image_url">Image URL</label>
	<input type="text" name="job_image_url">
	<br>
	<label for="job_url">Job URL</label>
	<input type="text" name="job_url">
	<br>
	<input type="submit" value="Simpan">
</form>

Ini hasilnya saat kita inputkan data yang benar.

hasil add job

Sekarang coba berikan input untuk name atau judul lokernya dengan kode javascript.

<script>alert('hacked')</script>

Untuk URL kita kosongkan aja dulu..

input xss-injection

Maka hasilnya:

hasil xss job vacancy

Serangan ini sama persis seperti XSS Reflected, bedanya injeksi pada seraingan ini akan disimpan di database.

Pada contoh di atas memang kita tidak menggunakan database. Tapi dalam prakteknya, pasti kita akan menyimpan ke database untuk input data ini.

Lalu solusi yang bisa kita lakukan apa?

โœ… Solusi:

Solusinya sama seperti XSS Reflected, yakni kita harus menampilkan output dengan fungsi htmlentities().

Maka pada view ๐Ÿ“œ list_job.php dapat kita ubah seperti ini:

<h1><?= htmlentities($job["name"]) ?></h1>
<img src='<?= htmlentities($job["job_image_url"]) ?>' />
<br>
<a href='<?= htmlentities($job["job_url"]) ?>'>Link Pendaftaran</a>

Hasilnya:

hasil proteksi xss

Mantap!

Tapi jangan senang dulu..

Karena ini masih belum aman.

Lah kenapa?

Kita patut waspadai output pada atribut src pada image dan href pada link.

Bisa saja mereka memberikan injeksi seperti ini:

x' onerror='alert("Hacked")'

Injeksi ini akan membuat atribut baru bernama onerror yang berfungsi untuk menjalankan kode javascript.

Atau kita bisa juga menggunakan onclick seperti ini:

#' click='alert("Hacked")'

Mari kita coba:

injeksi atribut

Hasilnya:

hasil injeksi xss atribut

Lihat kan.. belum 100% aman hehe.

Lalu giaman cara mengatasi ini?

โœ… Solusi:

Sebenarnya kita bisa gunakan double quote pada atribut HTML seperti ini:

<img src="<?= htmlentities($job["job_image_url"]) ?>" />
<a href="<?= htmlentities($job["job_url"]) ?>">Link Pendaftaran</a>

Hasilnya atribut akan seperti ini:

solusi double quote

Apakah sudah aman?

Belum! ๐Ÿ˜„

Percobaan ini saya lakukan di browser versi terbaru. Kemungkin di browser versi lama, akan tetap kena.

Terus solusinya bagaimana?

Kita bisa gunakan konstanta ENT_QUOTES pada parameter htmlentities() untuk melakukan enkode pada tanda petik. Baik yang ganda maupun yang tunggal.

Sehingga kode viewnya akan menjadi seperti ini:

<h1><?= htmlentities($job["name"]) ?></h1>
<img src="<?= htmlentities($job["job_image_url"], ENT_QUOTES) ?>" />
<br>
<a href="<?= htmlentities($job["job_url"], ENT_QUOTES) ?>">Link Pendaftaran</a>

Hasilnya bisa kita lihat di View Source.

hasil entquotes

Masalah injeksi tanda petik sudah beres.

Apakah sudah aman?

Belum! ๐Ÿคฃ

Serangan injeksi berikutnya, saat user menginputkan:

javascript:alert('hacked')

Biasanya jika nilai ini diberikan pada link, maka browser akan menjalankan kode javascript yang diberikan.

Coba gunakan input tersebut.

injeksi url

Hasilnya:

hasil injeksi url

Kita sudah menggunakan htmlentities(), tapi yang ini bisa lolos.

Solusinya giaman donk?

โœ… Solusi:

Solusi yang bisa kita lakukan untuk mengatasi serangan ini adalah dengan melakukan validasi data. Baik di sisi client maupun server.

Pelajari juga:

Mari kita coba lakukan validasi di sisi server terlebih dahulu.

Ubah Controllernya menjadi seperti ini:

<?php

class Jobvacancy extends CI_Controller
{
	public function addjob()
	{
		$this->load->library('form_validation');
		if ($this->input->method() === "post") {
			$data["job"] = $this->input->post();

			$rules = [
				[
					'field' => 'name',
					'label' => 'Name',
					'rules' => ''
				],
				[
					'field' => 'job_image_url',
					'label' => 'Image URL',
					'rules' => 'valid_url'
				],
				[
					'field' => 'job_url',
					'label' => 'Job URL',
					'rules' => 'valid_url'
				],
			];

			$this->form_validation->set_rules($rules);
			if ($this->form_validation->run() == FALSE) {
				return $this->load->view('add_job_form');
			}

			// di sini biasanya dilakukan penyimpanan ke database

			return $this->load->view("list_job", $data);
		}

		$this->load->view('add_job_form');
	}
}

Kemudian ubah view untuk form-nya menjadi seperti ini:

๐Ÿ“œ views/add_job_form.php

<form action="" method="post">
	<label for="name">Vacancy Job name</label>
	<input type="text" name="name" value="<?= htmlentities(set_value('name'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('name') ?></div>
	<br>
	<label for="job_image_url">Image URL</label>
	<input type="text" name="job_image_url" value="<?= htmlentities(set_value('job_image_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_image_url') ?></div>
	<br>
	<label for="job_url">Job URL</label>
	<input type="text" name="job_url" value="<?= htmlentities(set_value('job_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_url') ?></div>
	<br>
	<input type="submit" value="Simpan">
</form>

Hasilnya:

validasi input url

Mantap!

Dengan begini aman sudah.

Tinggal lakukan validasi di sisi client.

Validasi di sisi client bisa kita lakukan dengan atribut HTML. Kita bisa ubah nilai pada atribut type menjadi url dan juga menambahkan pattern untuk validasi dengan regex.

Silahkan pelajari juga:

Maka sekarang kita ubah kode view untuk form-nya menjadi seperti ini:

<form action="" method="post">
	<label for="name">Vacancy Job name</label>
	<input type="text" name="name" value="<?= htmlentities(set_value('name'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('name') ?></div>
	<br>
	<label for="job_image_url">Image URL</label>
	<input type="url" pattern="[(http(s)?):\/\/(www\.)[email protected]:%._\+~#=]{2,256}\.[a-z]{2,6}\b([[email protected]:%_\+.~#?&//=]*)" name="job_image_url" value="<?= htmlentities(set_value('job_image_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_image_url') ?></div>
	<br>
	<label for="job_url">Job URL</label>
	<input type="url" pattern="[(http(s)?):\/\/(www\.)[email protected]:%._\+~#=]{2,256}\.[a-z]{2,6}\b([[email protected]:%_\+.~#?&//=]*)" name="job_url" value="<?= htmlentities(set_value('job_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_url') ?></div>
	<br>
	<input type="submit" value="Simpan">
</form>

Hasilnya:

validasi sisi client

Kini saya rasa sudah aman..

Tapi jangan berpikir 100% akan aman, hehe.

Karena masih ada bentuk serangan XSS yang lainnya:

3. DOM Based XSS

DOM Based XSS adalah jenis serangan XSS yang injeksinya dijalankan di dalam DOM (Document Object Model).

Apa itu DOM?

Silahkan pelajari di:

Untuk mendemokan serangan ini, mari kita ubah view pada form add_job.php menjadi seperti ini:

<form action="" method="post">
	<label for="name">Vacancy Job name</label>
	<input type="text" name="name" value="<?= htmlentities(set_value('name'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('name') ?></div>
	<br>
	<label for="job_image_url">Image URL</label>
	<input onchange="previewImage(this)" type="url" pattern="[(http(s)?):\/\/(www\.)[email protected]:%._\+~#=]{2,256}\.[a-z]{2,6}\b([[email protected]:%_\+.~#?&//=]*)" name="job_image_url" value="<?= htmlentities(set_value('job_image_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_image_url') ?></div>
	<div id="image-preview"></div>
	<br>
	<label for="job_url">Job URL</label>
	<input type="url" pattern="[(http(s)?):\/\/(www\.)[email protected]:%._\+~#=]{2,256}\.[a-z]{2,6}\b([[email protected]:%_\+.~#?&//=]*)" name="job_url" value="<?= htmlentities(set_value('job_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_url') ?></div>
	<br>
	<input type="submit" value="Simpan">
</form>


<script>
function previewImage(event) {
	const previewContainer = document.querySelector('#image-preview');
	const imageURL = event.value
	previewContainer.innerHTML = `
		<img src="${imageURL}" height="200" width="200" />
	`;
}
</script>

Kita menambahkan fitur untuk preview image berdasarkan URL image yang diinputkan.

Jika URL yang diinputkan bernar, maka akan tampil seperti ini:

fitur preview image

Berdasarkan kode javascript yang kita buat. Kita menampilkan image dengan manipulasi DOM.

Ini tentunya akan membuat kita bisa melakukan DOM based XSS.

Mau bukti?

Silahkan inputkan injeksi XSS berikut di dalam URL image.

x" onerror="alert('hacked')"

Maka hasilnya:

hasil dom xss

Benar kan! ๐Ÿ˜†

Padahal kita sudah melakukan validasi di sisi client dan server.

Lalu gimana solusinya?

โœ… Solusi:

Solusi untuk menghindari DOM based XSS sebenarnya hampir sama seperti Reflected XSS.

Bedanya, DOM based XSS dilakukan di client atau di dalam Javascript.. bukan di dalam PHP atau kode server.

Namun, fungsi htmlentities() di javascript belum ada. Fungsi ini bisa kita buat secara manual, atau bisa juga memanfaatkan library seperti underscore.js dengan fungsi _.escape().

Ada juga library DOMPurify yang khusus untuk mengatasi XSS.

OWASP sendiri juga menawarkan library ESAPI untuk escape HTML di javascript.

Kamu bisa baca:

Solusi lain bisa dengan men-trigger validator HTML saat input field diinputkan.

Contohnya seperti ini:

<form id="form-add" action="" method="post">
	<label for="name">Vacancy Job name</label>
	<input type="text" name="name" value="<?= htmlentities(set_value('name'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('name') ?></div>
	<br>
	<label for="job_image_url">Image URL</label>
	<input onchange="previewImage(this)" type="url" pattern="[(http(s)?):\/\/(www\.)[email protected]:%._\+~#=]{2,256}\.[a-z]{2,6}\b([[email protected]:%_\+.~#?&//=]*)" name="job_image_url" value="<?= htmlentities(set_value('job_image_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_image_url') ?></div>
	<div id="image-preview"></div>
	<br>
	<label for="job_url">Job URL</label>
	<input type="url" pattern="[(http(s)?):\/\/(www\.)[email protected]:%._\+~#=]{2,256}\.[a-z]{2,6}\b([[email protected]:%_\+.~#?&//=]*)" name="job_url" value="<?= htmlentities(set_value('job_url'), ENT_QUOTES) ?>">
	<div style="color: tomato;"><?= form_error('job_url') ?></div>
	<br>
	<input type="submit" value="Simpan">
</form>

<script type="text/javascript">
function previewImage(event) {
	const previewContainer = document.querySelector('#image-preview');
	const form = document.querySelector('#form-add');
	if(form.checkValidity()){
		const imageURL = event.value;
		previewContainer.innerHTML = `
			<img src="${imageURL}" height="200" width="200" />
		`;
	}
}
</script>

Maka preview image tidak akan ditampilkan selama nilai URL yang diberikan tidak valid.

mitigasi dom xss

4. Self-XSS

Teknik XSS ini sulit diatasi dari kode aplikasi. Karena dilakukan sendiri oleh user.

Misalnya gini:

Seorang Attacker melakukan sosial enginering ke pada pengguna market place.

Attacker: โ€œHi, saya punya kode rahasia buat dapetin diskon 99%โ€

Korban: โ€œBenarkah? gimana caranya?โ€

Attacker: โ€œBuka aplikasinya dari Google Chrome di Laptop, masuk ke halaman checkout, Klik kanan, lalu pilih inspect element, Lalu masuk ke Console. Setelah itu, paste Kode ini **XSS SCRIPT**, setelah itu tekan Enter. โ€

Korban: โ€œWah boleh nih dicoba.โ€

Beberapa hari kemudian..

Korban: โ€œKok saldo saya hilang ๐Ÿ˜ญ!!!โ€

Nah kira-kira begitulah skenario terjadinya Self XSS.

Injeksi XSS-nya dijalankan sendiri oleh user yang merupakan korban Social Engineering.

Lalu bagaimana cara mengatasinya?

โœ… Solusi:

Seperti yang saya bilang, Self XSS sulit diatasi dari kode aplikasi. Salah satu cara yang bisa dilakukan adalah memberikan edikasi kepada user agar tidak menjalankan kode apapun di dalam Console Javascript browser.

Kita bisa tiru cara facebook memberikan edukasi dengan menampilkan pesan ini di console Javascript.

facebook self xss

5. Mutated XSS (mXSS)

Teknik serangan XSS yang ini sangat jarang digunakan dan sulit untuk dideteksi. Karena baru ditemukan di tahun 2019.

Mutated XSS bisanya terjadi karena disebabkan adanya perbedaan cara browser menginterpretasi HTML.

Celah XSS ini ditemukan oleh Masato Kinugawa pada bulan Februri 2019.

Penjelasan cara kerjanya, bisa kamu tonton di video ini:

Sangat membingungkan bukan.

Celah ini ditemukan pada closure-library milik google dan tentunya sudah diperbaiki.

Akhir Kata..

Nah begitulah beberapa cara mitigasi atau menghindari serangan XSS attack di Codeigniter.

Intinya:

Saat menampilkan output, jangan lupa di encode atau escape teks yang mau ditampilkan.

Lalu jangan lupa lakukan validasi input juga.

Demo dari contoh di atas bisa kamu download di Github.

[๐ŸŽ Download Source]

Jika kamu punya tips keamanan lainnya, mari diskusi di kolom komentar.


  1. Wikipedia. 2021. Cross-site scripting. Diakses 13 Agustus 2021. ↩︎

Baca Juga ini

Helper CodeIgniter untuk Membuat Tanggal dalam Bahasa Indonesia

Helper CodeIgniter untuk Membuat Tanggal dalam Bahasa Indonesia

Saat kita membuat sbuah tanggal otomatis dengan fungsi Date(), kita akan mendapatkan hasil dalam format bahasa inggris. Contoh, Date(’d F Y’) akan menghasilkan output 22 December 2016 (sesui tanggal sekarang). Format penanggalan ini mungkin tidak begitu penting bagi sistem yang tidak digunakan di tempat formal. Namun, bagaimana kalau untuk membuat surat atau laporan formal. Pastinya, kita dituntut untuk menggunakan bahasa indonesia yang baik dan benar. Hal yang perlu kita lakukan untuk merubah tanggal ke dalam bahasa indonesia adalah merubah nama-nama bulan dari fungsi Date().

Cara Menggunakan Composer pada CodeIgniter

Cara Menggunakan Composer pada CodeIgniter

Composer sangat dibutuhkan apabila kita bekerja dengan banyak library. Composer akan membantu kita menginstal, men-download, meng-update, dan mencarikan depedency dari library yang digunakan. Pada CodeIgniter, composer sudah mulai didukung pada versi 3. Namun, autoload-nya belum diaktifkan secara default. Pada kesempatan ini, saya akan membahas cara konfigurasi CodeIgniter agar dapat mengunakan Composer. Mari kita mulai… Konfigurasi CodeIgniterBuka file konfigurasi CodeIgniter pada application/config/config.php. Kemudian isi nilai autoload untuk Composer seperti beikut ini:

Tutorial Codeigniter #09: Membuat Fitur Login untuk Admin

Tutorial Codeigniter #09: Membuat Fitur Login untuk Admin

Pelajari cara membuat fitur login untuk Admin di Codeigniter dengan Library session bawaan Codeigniter.

6 Fungsi Enkripsi di PHP untuk Mengamankan Data

6 Fungsi Enkripsi di PHP untuk Mengamankan Data

Data adalah harta karun yang paling berharga dalam sebuah sistem. Apabila itu dicuri… …bencana besar bisa terjadi ๐Ÿ˜„. Enkripsi adalah teknik untuk mengamankan data-data tersebut agar isinya tidak diketahui orang lain. Enkripsi biasanya dilakukan terhadap data-data sensitif seperti password. Enkripsi akan menjamin data-data tetap aman meskipun berada di tangan orang lain, karena mereka tidak tahu isi aslinya. Pada kesempatan ini, saya akan membahas beberapa fungsi enkripsi yang sudah disediakan oleh PHP, diantaranya: password_hash(), crypt(), md5(), hash(), sha1(), dan base64_encode().

Aplikasimu Lambat? Coba Cek dengan Profiler di Codeigniter

Aplikasimu Lambat? Coba Cek dengan Profiler di Codeigniter

Pernah dengar profiler? Kalau belum, artikel ini tepat untukmu. Codeigniter memang memudahkan kita dalam membuat aplikasi web. Meskipun kita sudah tahu… MVC adalah desain pola yang dipakai Codeigntier, tapi masih aja ada orang yang menulis kode brantakan. Akibatnya: Aplikasi berjalan lambat dan terasa berat. Nah si Profiler ini akan memberitahu kita, kelemahan dari aplikasi. Seperti: Berapa lama Waktu yang dibutuhkan untuk eksekusi controller Query apa saja yang dieksekusi Kecepatan ekekusi query Data yang terkirim dan sebagainya.

Cara Menggunakan Composer untuk Manajemen Proyek PHP

Cara Menggunakan Composer untuk Manajemen Proyek PHP

Bayangkan kita sedang mengerjakan proyek web dengan PHP. Kemudian kita membutuhkan beberapa library untuk melengkapi proyek ini. Biasanya, kita men-download sendiri library-nya dari internet. Tapi… Kadang library yang satu dengan yang lain saling membutuhkan agar bisa digunakan. Hal ini disebut depedency (keterkaitan/ketergantungan). Contohnya: Library X membutuhkan library Y agar bisa digunakan, kemudian library Y membutuhkan library Q,R,S. “Bagaimana kalau ada banyak sekali library yang digunakan?” Pastinya akan repot mencari sendiri depedency library-nya.