Frontend Security 101: Saving You from XSS Attack (still)
Masih membahas seputar XSS dan langkah-langkah pencegahannya
Setelah di postingan sebelumnya kita sudah membahas XSS dan salah satu cara kita menanggulangi serangan XSS yaitu dengan menggunakan Content Security Policy (CSP), pada kesempatan kali ini saya mau melengkapi pembahasan saya terkait XSS dan mungkin hal-hal yang bisa kita implementasi ke aplikasi frontend kita sehingga memiliki risiko lebih kecil dari serangan XSS. Bukan menghindari ya, soalnya kan tetep aja selama aplikasi kita masih rely ke browser, risiko serangan itu tetap ada.
Sedikit flashback, supaya enggak ngah ngoh apa itu XSS, tapi saya sih yakin kamu yang baca ini pasti sudah pernah dengar XSS itu apa, yaa ibaratnya cukup familiar lah ya dengan istilah XSS ini.
Cross-Site Scripting (XSS) adalah salah satu bentuk serangan injeksi di mana penyerang bisa menyisipkan skrip berbahaya ke halaman web yang terlihat aman*. Serangan ini terjadi ketika aplikasi tidak berhasil menyaring atau sanitize input yang dikirimkan pengguna. Ketika pengguna lain mengunjungi halaman tersebut, skrip berbahaya itu bisa dieksekusi, dan dampaknya? Bisa macam-macam, mulai dari pencurian data, kontrol penuh atas sesi pengguna, hingga manipulasi halaman web. - ChatGPT*
Nah itu tadi definisinya dari ChatGPT, jadi XSS ini memang serangan yang memungkinkan user tidak bertanggung jawab melakukan script injection ke halaman web kita, sehingga merugikan user-user lain.
Terus, selain menggunakan CSP, beberapa cara berikut bisa kita implementasi untuk mengurangi risiko XSS attacks:
Sanitize the input, escape the output
Terutama untuk halaman web yang tidak statis alias dinamis, yang butuh ada interaksi dengan pengguna, hal ini adalah hal paling mendasar untuk dilakukan. Karena salah satu pintu masuk serangan XSS adalah dari user input, makanya kita harus, wajib, kudu, melakukan sanitize terhadap input yang diberikan user. Untungnya by default React JS melakukan sanitizing terhadap input dari pengguna, tapi… tidak berhenti sampai disitu dong. Kita tetap perlu mengimplementasi beberapa lapisan yang lain.
Never use dangerouslySetInnerHTML
unless you know what you do
Salah satu aturan yang kami sepakati dulu di Stockbit, adalah jangan pernah menggunakan dangerouslySetInnerHTML
, secara namanya saja sudah menjelaskan ya kalo ini dangerous alias berbahaya. Jika terpaksa menggunakan prop tersebut untuk render content dari backend atau input user, kita perlu menambahkan DOMPurify
supaya kita meng-sanitize data input tersebut.
import DOMPurify from 'dompurify';
const SafeComponent = ({ userInput }) => {
return (
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
);
};
Atau kita juga bisa pakai html-react-parser
yang digabung dengan sanitize-html
untuk render content string yang berisi html data.
import sanitizeHTML from 'sanitize-html';
import parse from 'html-react-parser';
const SafeComponent = ({ userInput }) => {
return (parse(sanitizeHTML(userInput)));
}
Untuk case html parser ini sebenernya sama aja kaya dangerouslySetInnerHTML
tapi kadang kita mau custom styling atau replace tag dll yang agak kompleks, secara security, yaaa…. 11/12 lah sama sama vulnerable.
Selain berhubungan dengan sanitize input, kita juga perlu memastikan data-data yang kita outputkan telah kita escape sehingga karakter-karakter khusus seperti <
, >
, dan &
tidak diencode melainkan ditampilkan as is.
When dealing with cookies, make sure it uses Secure and HTTPOnly
XSS juga bisa menyerang melalui cookie, jadi management cookie yang baik juga menjadi hal yang sangat penting untuk diperhatikan. Kalau aplikasi kita tidak menggunakan SSO, kita bisa enggak menggunakan cookie, sebagai alternatif kita bisa memanfaatkan localStorage
untuk menyimpan data-data credentials, tapi biasanya ketika kita menggunakan library auth seperti next-auth
dan semacamnya sih dia disimpan di cookie ya, yaa itu nggak papa deh kalo pakai middleware gitu mah.
Kembali ke cookie, untuk setting cookie agar Secure dan HTTPOnly yaa sangat sederhana
import cookie from "js-cookie";
cookie.set('COOKIE_NAME', 'value, {
secure: true,
httpOnly: true
})
js-cookie
adalah salah satu library yang sering saya pakai terkait cookie-cookie an karena dia works di semua browser, jadi bisa dipertimbangkan ya.
Selain dari frontend sebenernya kalau cookie ini juga dari backend perlu setup ya, sehingga datanya tidak bisa diambil oleh user. Misalnya di express js implementasinya
res.cookie('sessionId', 'your-session-id', {
httpOnly: true, // Cookie tidak bisa diakses via JavaScript
secure: true // Hanya dikirim melalui HTTPS
});
Kegunaan dari httpOnly adalah untuk memastikan bahwa cookie tidak bisa diakses melalui Javascript dan hanya menerima komunikasi dari site yang secure atau HTTPS, jadi tidak bisa dipaksa hit dari localhost juga. CMIIW
Satu lagi ya tentu si CSP itu tadi, selain itu…. saya belum baca lagi, kalau ada suggestion, please do make sure hit the comment section (eh ada nggak sih? ini platform baru jadi enggak yakin ada comment section)
Anyway, that’s it for today, semoga bisa membantu teman-teman semua. Have a great day!