Bagaimana mencegah XSS dengan HTML / PHP?

256

Bagaimana cara mencegah XSS (skrip lintas situs) hanya menggunakan HTML dan PHP?

Saya telah melihat banyak posting lain tentang topik ini tetapi saya belum menemukan artikel yang jelas dan singkat menyatakan bagaimana sebenarnya mencegah XSS.

TimTim
sumber
3
Hanya catatan bahwa ini tidak akan menyelesaikan kasus di mana Anda mungkin ingin menggunakan input pengguna sebagai atribut HTML. Misalnya, URL sumber suatu gambar. Bukan kasus yang umum, tetapi yang mudah untuk dilupakan.
Michael Mior
@MichaelMior di sini adalah solusi untuk mencegah XSS dalam hrefatau srcatribut HTML: stackoverflow.com/questions/19047119/…
baptx
Ada artikel bagus di sini yang menjelaskan XSS dan cara mencegahnya dalam berbagai bahasa (termasuk PHP).
XCore

Jawaban:

296

Pada dasarnya Anda perlu menggunakan fungsi htmlspecialchars()kapan pun Anda ingin menampilkan sesuatu ke browser yang berasal dari input pengguna.

Cara yang benar untuk menggunakan fungsi ini adalah sesuatu seperti ini:

echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');

Universitas Kode Google juga memiliki video yang sangat mendidik ini tentang Keamanan Web:

Alix Axel
sumber
7
@ TimTim: Untuk sebagian besar kasus, ya. Namun, ketika Anda perlu mengizinkan hal-hal input HTML menjadi sedikit rumit dan jika ini masalahnya saya sarankan Anda menggunakan sesuatu seperti htmlpurifier.org
Alix Axel
@Alix Axel, jadi apakah jawaban Anda untuk menggunakan htmlspecialchars atau menggunakan htmlpurifier.org ?
TimTim
3
Jika Anda perlu menerima input HTML gunakan HTML Purifier, jika tidak digunakan htmlspecialchars().
Alix Axel
9
htmlspecialchars atau htmlentities? Lihat di sini stackoverflow.com/questions/46483/…
kiranvj
4
Sebagian besar waktu itu benar, tetapi tidak sesederhana itu. Anda harus mempertimbangkan memasukkan string yang tidak tepercaya ke dalam HTML, Js, Css, dan mempertimbangkan memasukkan HTML yang tidak tepercaya ke dalam HTML. Lihat ini: owasp.org/index.php/…
orang perunggu
41

Salah satu referensi OWASP favorit saya adalah penjelasan Cross-Site Scripting karena walaupun ada sejumlah besar vektor serangan XSS, beberapa aturan berikut dapat mempertahankan sebagian besar dari mereka!

Ini adalah Lembar Curang Keamanan PHP

Wahyu Kristianto
sumber
7
Saya juga .. Ini XSS Filter Evasion Cheat Sheet owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
1
Tidak persis XSS, tapi saya pikir XSS dan CSRF umumnya campur aduk dan keduanya sangat berbahaya: owasp.org/index.php/…
Simon
2
Halaman ini sudah tidak ada lagi
Mazzy
1
@Mazzy cache terakhir web.archive.org/web/20180817180409/owasp.org/index.php/…
Wahyu Kristianto
15

Salah satu langkah paling penting adalah membersihkan setiap input pengguna sebelum diproses dan / atau dirender kembali ke browser. PHP memiliki beberapa " filter fungsi " " yang dapat digunakan.

Bentuk serangan XSS biasanya adalah untuk menyisipkan tautan ke beberapa javascript luar-situs yang berisi niat jahat bagi pengguna. Baca lebih lanjut tentang itu sini .

Anda juga ingin menguji situs Anda - Saya dapat merekomendasikan add-on Firefox XSS Me .

James Kolpack
sumber
Apa yang saya perlukan untuk memastikan saya membersihkan input dengan tepat. Apakah ada satu karakter / string tertentu yang harus saya perhatikan?
TimTim
27
@TimTim - no. Semua input pengguna harus selalu dianggap bermusuhan secara inheren.
zombat
Selain itu, data internal (karyawan, sysadmin, dll.) Bisa tidak aman. Anda harus mengidentifikasi dan memantau data (dengan tanggal log dan pengguna) yang ditampilkan dengan interpretasi.
Samuel Dauzon
9

Dalam urutan pilihan:

  1. Jika Anda menggunakan mesin templating (mis. Twig, Smarty, Blade), periksa apakah ia menawarkan pelolosan konteks-sensitif. Saya tahu dari pengalaman yang dilakukan Ranting.{{ var|e('html_attr') }}
  2. Jika Anda ingin mengizinkan HTML, gunakan HTML Purifier . Bahkan jika Anda berpikir Anda hanya menerima Markdown atau ReStructuredText, Anda masih ingin memurnikan HTML output bahasa markup ini.
  3. Jika tidak, gunakan htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)dan pastikan sisa dokumen Anda menggunakan karakter yang sama dengan $charset. Dalam kebanyakan kasus, 'UTF-8'adalah rangkaian karakter yang diinginkan.

Juga, pastikan Anda melarikan diri pada output, bukan pada input .

Scott Arciszewski
sumber
7

Posting silang ini sebagai referensi gabungan dari beta Dokumentasi SO yang sedang offline.

Masalah

Skrip lintas situs adalah eksekusi kode jarak jauh yang tidak disengaja oleh klien web. Aplikasi web apa pun dapat membuka dirinya ke XSS jika mengambil input dari pengguna dan mengeluarkannya langsung di halaman web. Jika input menyertakan HTML atau JavaScript, kode jarak jauh dapat dijalankan ketika konten ini diberikan oleh klien web.

Misalnya, jika pihak ke-3 berisi file JavaScript:

// http://example.com/runme.js
document.write("I'm running");

Dan aplikasi PHP langsung mengeluarkan string yang dilewatkan ke dalamnya:

<?php
echo '<div>' . $_GET['input'] . '</div>';

Jika parameter GET yang tidak dicentang mengandung <script src="http://example.com/runme.js"></script>maka output dari skrip PHP adalah:

<div><script src="http://example.com/runme.js"></script></div>

JavaScript pihak ke-3 akan berjalan dan pengguna akan melihat "Saya sedang menjalankan" di halaman web.

Larutan

Sebagai aturan umum, jangan pernah percaya input yang datang dari klien. Setiap nilai GET, POST, dan cookie dapat berupa apa saja, dan karenanya harus divalidasi. Saat mengeluarkan salah satu dari nilai-nilai ini, lepas dari nilai-nilai ini sehingga tidak akan dievaluasi secara tak terduga.

Perlu diingat bahwa bahkan dalam aplikasi yang paling sederhana pun data dapat dipindahkan dan akan sulit untuk melacak semua sumber. Karena itu merupakan praktik terbaik untuk selalu keluar dari hasil.

PHP menyediakan beberapa cara untuk menghindari output tergantung pada konteksnya.

Fungsi Filter

Fungsi Filter PHP memungkinkan input data ke skrip php untuk disanitasi atau divalidasi dalam banyak cara . Mereka berguna saat menyimpan atau mengeluarkan input klien.

Pengkodean HTML

htmlspecialcharsakan mengonversi "karakter khusus HTML" apa pun ke penyandian HTML mereka, yang berarti mereka tidak akan diproses sebagai HTML standar. Untuk memperbaiki contoh kami sebelumnya menggunakan metode ini:

<?php
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// or
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';

Akan menghasilkan:

<div>&lt;script src=&quot;http://example.com/runme.js&quot;&gt;&lt;/script&gt;</div>

Segala sesuatu di dalam <div>tag tidak akan diartikan sebagai tag JavaScript oleh browser, tetapi sebagai simpul teks sederhana. Pengguna akan melihat:

<script src="http://example.com/runme.js"></script>

Pengkodean URL

Saat mengeluarkan URL yang dihasilkan secara dinamis, PHP menyediakan urlencodefungsi untuk menghasilkan URL yang valid dengan aman. Jadi, misalnya, jika pengguna dapat memasukkan data yang menjadi bagian dari parameter GET lain:

<?php
$input = urlencode($_GET['input']);
// or
$input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
echo '<a href="http://example.com/page?input="' . $input . '">Link</a>';

Setiap input jahat akan dikonversi ke parameter URL yang disandikan.

Menggunakan perpustakaan eksternal khusus atau daftar AntiSamy OWASP

Terkadang Anda ingin mengirim HTML atau input kode jenis lain. Anda perlu mempertahankan daftar kata yang diotorisasi (daftar putih) dan tidak resmi (daftar hitam).

Anda dapat mengunduh daftar standar yang tersedia di situs web OWASP AntiSamy . Setiap daftar cocok untuk jenis interaksi tertentu (ebay api, tinyMCE, dll ...). Dan itu open source.

Ada perpustakaan yang ada untuk menyaring HTML dan mencegah serangan XSS untuk kasus umum dan melakukan setidaknya serta daftar AntiSamy dengan penggunaan yang sangat mudah. Misalnya Anda memiliki Pemurni HTML

Matt S
sumber
5

Banyak kerangka kerja membantu menangani XSS dengan berbagai cara. Saat menggulirkan Anda sendiri atau jika ada kekhawatiran XSS, kami dapat memanfaatkan filter_input_array (tersedia dalam PHP 5> = 5.2.0, PHP 7.) Saya biasanya akan menambahkan cuplikan ini ke SessionController saya, karena semua panggilan masuk ke sana sebelum pengontrol lain berinteraksi dengan data. Dengan cara ini, semua input pengguna dibersihkan di 1 lokasi pusat. Jika ini dilakukan pada awal proyek atau sebelum database Anda diracuni, Anda seharusnya tidak memiliki masalah pada saat output ... berhenti sampah masuk, sampah keluar.

/* Prevent XSS input */
$_GET   = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST  = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
/* I prefer not to use $_REQUEST...but for those who do: */
$_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;

Di atas akan menghapus SEMUA tag HTML & skrip. Jika Anda membutuhkan solusi yang memungkinkan tag aman, berdasarkan daftar putih, lihat Pemurni HTML .


Jika database Anda sudah diracuni atau Anda ingin berurusan dengan XSS pada saat output, OWASP merekomendasikan membuat fungsi pembungkus kustom untuk echo, dan menggunakannya di mana saja Anda menghasilkan nilai yang disediakan pengguna:

//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
   return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
}
function xecho($data)
{
   echo xssafe($data);
}
webaholik
sumber
2

Anda juga dapat mengatur beberapa header respons HTTP terkait XSS melalui header(...)

X-XSS-Protection "1; mode = block"

untuk memastikan, mode perlindungan browser XSS diaktifkan.

Kebijakan-Keamanan-Konten "default-src 'self'; ..."

untuk mengaktifkan keamanan konten sisi browser. Lihat yang satu ini untuk perincian Kebijakan Keamanan Konten (CSP): http://content-security-policy.com/ Terutama menyiapkan CSP untuk memblokir skrip inline dan sumber skrip eksternal sangat membantu terhadap XSS.

untuk kumpulan umum header tanggapan HTTP yang bermanfaat mengenai keamanan webapp Anda, lihat OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers

chris
sumber
1
<?php
function xss_clean($data)
{
// Fix &entity\n;
$data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');

// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);

// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);

// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);

// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);

do
{
    // Remove really unwanted tags
    $old_data = $data;
    $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);

// we are done...
return $data;
}
Tuan Rumah
sumber
5
Anda seharusnya tidak menggunakannya preg_replaceseperti yang digunakan evalpada input Anda. owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Injection
CrabLab
0

Gunakan htmlspecialcharspada PHP. Pada HTML cobalah untuk menghindari menggunakan:

element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);

mana varyang dikendalikan oleh pengguna .

Juga jelas mencoba menghindari eval(var), jika Anda harus menggunakan salah satu dari mereka kemudian mencoba JS melarikan diri dari mereka, HTML melarikan diri dari mereka dan Anda mungkin harus melakukan lebih tetapi untuk dasar-dasar ini sudah cukup.

Pablo
sumber
0

Cara terbaik untuk melindungi input Anda menggunakan htmlentitiesfungsinya. Contoh:

htmlentities($target, ENT_QUOTES, 'UTF-8');

Anda dapat memperoleh informasi lebih lanjut di sini .

Marco Concas
sumber