Sécurisation d’une application Web

Une application Web Java EE peut être sécurisée de manière déclarative. Cela signifie qu’il est possible de déclarer que certaines adresses de cette application sont soumises à des restrictions d’accès. Dans ce cas, le serveur doit pouvoir garantir la confidentialité des données grâce à une connexion sécurisée https et/ou l’utilisateur doit, au préalable, s’authentifier auprès du serveur.

Le mécanisme d’authentification relève de la configuration du serveur. Dans le processus de développement, l’équipe de développement configure l’application pour sécuriser l’accès à certaines parties de l’application et les administrateurs du serveur mettent en place la configuration nécessaire pour prendre en charge la sécurisation.

La déclaration des contraintes de sécurité est standardisée par Java EE. La configuration des mécanismes de sécurité est propre à chaque serveur Web Java.

Déclarer des contraintes de sécurité

Pour l’équipe de développement, il faut déclarer les contraintes de sécurité dans le fichier web.xml.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Zone privée</web-resource-name>
      <url-pattern>/private/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Zone avec accès restreint</realm-name>
  </login-config>

  <security-role>
    <role-name>admin</role-name>
  </security-role>

</web-app>

Pour l’exemple précédent, on déclare une zone privée correspondant à toutes les adresses de la forme /private/* (ligne 10). Cette zone n’est accessible que pour les utilisateurs qui ont le rôle admin (ligne 13). Les données doivent être transmises de manière confidentielle, c’est-à-dire que les échanges doivent se faire en https (ligne 16). On déclare ensuite la configuration pour réaliser une étape de login. On déclare qu’il faut utiliser une authentification HTTP en mode BASIC (ligne 21). Enfin, on déclare que l’application utilise le rôle admin (ligne 26).

À partir de cette configuration, le serveur doit pouvoir satisfaire l’ensemble de ces contraintes et garantir que les accès sont correctement sécurisés.

Configuration de l’authentification dans Tomcat

Tomcat utilise la notion de royaumes (Realms) pour définir des zone sécurisées. Cela permet de configurer différentes stratégies pour valider l’authentification d’un utilisateur. Ainsi Tomcat peut valider une authentification en recherchant le couple login/mot de passe dans une base de données, en interrogeant un serveur LDAP ou tout simplement en consultant un fichier de configuration. Il est également possible de fournir son propre mécanisme d’authentification en fournissant une bibliothèque Java conforme au standard JAAS (Java Authentication and Authorization Service).

Par defaut, un serveur Tomcat est configuré pour consulter un fichier de configuration appelé tomcat-users.xml. Ce fichier contient la déclaration des rôles et des utilisateurs.

Exemple de fichier tomcat-users.xml
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users version="1.0"
              xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd">
  <role rolename="admin"/>
  <user username="user" password="azerty" roles="admin"/>
</tomcat-users>

Configuration https dans Tomcat

Pour activer le connecteur https dans Tomcat, il faut disposer d’un certificat serveur. Java fournit un outil appelé keytool qui est dédié à la gestion des certificats et à leur stockage dans un fichier sécurisé appelé un keystore.

Pour disposer d’un certificat, il faut effectuer une demande de validation auprès d’un organisme certificateur. En effet, un certificat numérique doit toujours être signé numériquement par un tiers de confiance. Cependant, il est également possible de créer des certificats dits auto-signés, c’est-à-dire signés par l’émetteur. Attention, ce type de certificat ne doit pas être utilisé dans un environnement de production. Un navigateur Web se connectant en https sur un serveur présentant un certificat auto-signé, produit un message d’avertissement à l’utilisateur car cela constitue une faille de sécurité.

Pour des raisons de simplicité, nous allons utiliser pour ce chapitre une certificat auto-signé.

Vous pouvez récupérer un fichier keystore contenant un certificat auto-signé :

Ce fichier contient un certificat auto-signé nommé tomcat pour le domaine localhost. Le mot de passe qui protège le keystore est azerty.

Note

Il est possible de générer votre propre certificat auto-signé grâce à l’outil keytool. Il s’agit d’un outil en ligne de commandes qui se trouve dans le sous-répertoire bin du répertoire d’installation du JDK. Pour créer un certificat auto-signé, il faut utiliser la commande :

keytool -genkey -keystore [NOM FICHIER KEYSTORE] -alias tomcat -keyalg RSA -keysize 4096 -validity 365

Il faut maintenant configurer Tomcat en éditant le fichier de configuration principal du serveur : server.xml. Ce fichier se trouve dans le * sous-répertoire conf du répertoire d’installation de Tomcat.

Important

Dans Eclipse, la configuration de Tomcat se trouve dans le projet nommé Servers.

Recherchez la déclaration du connecteur http dans le fichier qui ressemble à ceci :

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

Ajoutez à la suite la déclaration du connecteur https :

<Connector port="8443"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150"
           SSLEnabled="true"
           scheme="https"
           secure="true"
           clientAuth="false"
           sslProtocol="TLS"
           keystoreFile="selfsigned.keystore"
           keyAlias="tomcat"
           keystorePass="azerty" />

Avertissement

Faites attention à ce que l’attribut keystoreFile indique bien le chemin complet d’accès au fichier keystore contenant le certificat.

Exercice

Sécurisation d’une application Web

Sécurisez une application Web de manière à ce que l’application demande une authentification HTTP et soit accessible en https.

Astuce

Une fois connecté, les informations sur l’utilisateur (dont son login) sont accessibles grâce à la méthode HttpServletRequest.getUserPrincipal. Il est également possible d’y accéder dans une JSP puisque l’objet implicite pageContext permet d’accéder à l’objet HttpServletRequest :

${pageContext.request.userPrincipal.name}

Login à partir d’un formulaire Web

Si vous ne désirez pas utiliser l’authentification HTTP, il est possible de créer son propre formulaire de login et de préciser que la configuration de login doit se faire à partir d’un formulaire.

Configuration pour l’utilisation d’un formulaire
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Zone privée</web-resource-name>
      <url-pattern>/private/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

  <login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/login.html</form-login-page>
        <form-error-page>/login-failed.html</form-error-page>
    </form-login-config>
  </login-config>

  <security-role>
    <role-name>admin</role-name>
  </security-role>

</web-app>

Les balises <form-login-page> et <form-error-page> indiquent les ressources Web à utiliser pour afficher le formulaire de login et la page d’erreur de connexion. Il peut s’agir de n’importe quelle ressource : page HTML, JSP, Servlet.

La page de login doit contenir un formulaire qui soumet des données vers j_security_check. Les paramètres soumis doivent être j_username et j_password.

Le fichier login.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form method="POST" action="j_security_check" accept-charset="latin-1">
  <label>login : </label>
  <input type="text" name="j_username"><br>
  <label>mot de passe : </label>
  <input type="password" name="j_password"><br>
  <button type="submit">Connexion</button>
</form>
</body>
</html>
Le fichier login-failed.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Échec login</title>
</head>
<body>

<div>Le login ou le mot de passe est invalide !</div>

<form method="POST" action="j_security_check">
  <label>login : </label>
  <input type="text" name="j_username"><br>
  <label>mot de passe : </label>
  <input type="text" name="j_password"><br>
  <button type="submit">Connexion</button>
</form>
</body>
</html>