Skip to content

Problèmes connus

Ce chapitre revient sur certains problèmes identifiés et pour lesquelles il n'existe pas de solution directement intégrée à Ewt. Des axes de solution sont proposés lorsque cela est possible.

Duplications de sessions

Lorsqu'on duplique un onglet sur lequel on est connecté à une application Ewt, cela ouvre une nouvelle fenêtre qui partage la même session et les mêmes cookies. Le moteur n'est alors pas capable de faire la distinction entre les requêtes provenant de l'un ou l'autre des deux onglets. Le fait que deux onglets distincts (ou plus) génèrent des requêtes pour une même session Ewt peut entraîner au mieux des blocages de policies ou, au pire, des écrasements de valeurs ou des comportements imprévisibles. Il n'est donc pas souhaitable que cette situation se produise.

En réalité il n'est pas possible d'empêcher un utilisateur de dupliquer un onglet et il n'est pas possible pour le moteur de lier une session à un onglet car il n'existe pas de standard commun aux différents navigateurs qui permette d'identifier un onglet. Chaque navigateur propose des plugins spécifiques, mais il n'existe pas de spécification standardisée à l'heure où ces lignes sont écrites.

Pour limiter le problème, il est donc recommandé de mettre en place une sécurité au niveau du navigateur lui-même. Cela peut se faire en JavaScript à l'aide de BroadcastChannel:

Le service BroadcastChannel permet d'envoyer et de recevoir des messages au sein des différents onglets, des différentes fenêtres, iframe ou workers qui portent sur une même session (la portée est en réalité un peu différente de cela, mais cela correspond à notre besoin).

Une solution est donc d'utiliser cette interface pour mettre en place une communication entre les onglets qui partagent une même session. L'idée est d'envoyer un message invalidate via le channel lors d'un submit afin d'indiquer à tous les autres onglets qu'ils doivent s'invalider. Chaque onglet décidera alors du comportement à adopter (afficher un bandeau d'avertissement à l'écran, fermer tous les dossiers ouverts, forcer un retour à la page d'accueil au moyen d'une requête GET, etc.)

Ci-dessous, nous donnons un exemple de solution qui va dans ce sens.

 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
33
34
35
36
37
38
39
40
const ewt = {
    // broadcast channel utilisé pour synchroniser les onglets pointant
    // sur une même session
    channel: undefined,

    init: function() {
        // initialisaiton du channel (on utilise le nom de session
        // comme nom de channel) et enregistrement de la méthode
        // qui gérera les messages
        this.channel = new BroadcastChannel(appSettings.session);
        this.channel.addEventListener('message', ewt.onMessage);
    },

    onMessage: function(ev) {
        if (ev && ev.data) {
            if (ev.data.message == 'invalidate') {
                console.log("Recieved `invalidate` message");
                window.open(appSettings.requestUrl, "_self");
            }
        }
    },

    // effectue un submit standard sur /web
    submit: function(action, params, context) {
        let command = { action: action, params: params };

        $('#ewt_command').val(JSON.stringify(command));
        $('#ewt_context').val(context);

        if (this.channel) {
            // envoi message d'invalidation aux autres onglets liés
            // à la session
            this.channel.postMessage({ message: 'invalidate' });
        }

        $('#form').submit();
    }
}

document.addEventListener('DOMContentLoaded', ewt.init);

Ici la méthode onMessage force un retour à la page d'accueil de l'application au moyen de window.open. Cette action offre deux avantages:

  1. Elle force l'utilisateur à quitter tout dossier ouvert qui pourrait être modifié depuis un autre onglet
  2. Elle force la création d'une session propre à l'onglet, ce qui permet d'éviter les conflits de sessions sur les requêtes à venir.

On pourrait améliorer ce comportement de différentes façons, pour commencer en annonçant à l'utilisateur que la page va se fermer. Ensuite on pourrait gérer le comportement en fonction du contexte : si l'onglet est ouvert sur un écran qui ne comporte pas de champ en écriture (par exemple si l'onglet n'affiche pas un écran de dossier), alors on pourrait ignorer le message invalidate.

Le token CSRF est une valeur émise par le moteur servant à protéger une application contre une attaque CSRF. Il s'agit d'un cookie possédant l'attribut SameSite=Strict. Si l'application est accessible via une connexion SSL, le cookie possède également l'attribut Secure.

Il se peut que le cookie ne soit pas correctement véhiculé dans les échanges entre le serveur et le navigateur. C'est notamment le cas lorsqu'on utilise un serveur Apache Httpd en front, avec ou sans HTTPS.

La solution "quick and dirty" consiste à désactiver la sécurité CSRF en modifiant la valeur de la propriété de configuration ignoreCSRF.

Si on souhaite conserver la sécurité CSRF, il va falloir adapter la configuration du module de proxy de Apache Httpd.

Cause 1 : le serveur tomcat ne voit pas la connexion comme sécurisée

Symptomes: Le problème peut s'exprimer de différentes manières.

  • l'application se plaint de ne pas recevoir le cookie csrf
  • le navigateur affiche le message suivant lors de l'authentification

    Les informations saisies vont être transmises en clair (sans 
    chiffrement). Elles peuvent donc éventuellement être interceptées 
    et lues logs de leur acheminement.  
    Voulez-vous vraiment transmettre ces informations ?
    
  • la trace réseau montre que certaines requêtes transitent via une URL en http

Comme le serveur Apache Httpd effectue une redirection vers un port HTTP, le serveur Tomcat ne voit pas qu'elle provient en réalité d'une connexion sécurisée. Il faut donc demander à Apache Httpd d'indiquer cela au niveau de l'entête HTTP.

Éditez le fichier de configuration ssl présent dans /etc/apache/sites-enabled.

Ajoutez les lignes suivantes dans votre configuration:

ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"

Variante possible

ProxyPreserveHost On
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
RequestHeader set "X-Forwarded-SSL" expr=%{HTTPS}

Au niveau de la config Tomcat, éditez le fichier server.xml et ajoutez la valve suivante à la fin du bloc /Server/Service/Engine/Host:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
       remoteIpHeader="x-forwarded-for"
       remoteIpProxiesHeader="x-forwarded-by"
       protocolHeader="x-forwarded-proto"
       portHeader="x-forwarded-port"/>

Redémarrez ensuite Tomcat et Httpd:

sudo systemctl restart apache2 tomcat

Il se peut que ce dernier signale une erreur au redémarrage, en mentionnant une erreur de syntaxe avec l'entrée RequestHeader. Si tel est le cas, lancez la commande suivante pour installer le module mod_header:

sudo a2enmod headers

Il se peut que le cookie soit perdu lors du traitement de la requête par Httpd, du fait qu'il porte sur un domaine et/ou un contexte qui diffère entre l'interne et l'externe.

Éditez le fichier de configuration ssl présent dans /etc/apache/sites-enabled.

Ajoutez les lignes suivantes:

ProxyPassReverseCookiePath /internalContext /externalContext
ProxyPassReverseCookieDomain localhost example.com

Sauvegardez les modifications et redémarrez le serveur Apache Httpd.