vendredi 15 juin 2012

D'ou vient ce ";jsessionid=..." dans l'URL de la page

Dans de nombreux sites web développés en Java, on voit apparaître parfois un peu n'importe ou un paramètre "jsessionid". Ex :
http://www.legifrance.gouv.fr/rechSarde.do;jsessionid=5D1D8F2EA5F4E3B6B7058B65817EE539.tpdjo09v_2?reprise=true&page=1&lettre=null

Parfois ils apparaissent dans une URL provenant d'une redirection (en général quand on arrive sur le site) mais on les trouve aussi dans les URL des liens qui se trouvent dans les pages. Souvent ces derniers disparaissent quand on rafraîchit la page.

Beaucoup de développeurs ne savent pas vraiment pourquoi ils sont la, quel est leur impact et ne s'en soucient guère. Et pourtant il faut savoir que cette fonctionnalité typique des serveurs d'applications java jadis très utile est aujourd'hui une belle source de problèmes.

Comment ça marche ?

La spécification servlet prévoit que pour pouvoir maintenir coté serveur et état lié à l'utilisateur (la session) le serveur doit implémenter au minimum deux techniques :
  • un cookie de session dont le nom est "JSESSIONID"
  • la réécriture des URL avec l'ajout d'un paramètre "jsessionid" ajouté en principe à la fin de l'URL sous la forme ";jsessionid=".
Mais dans quel cas le serveur va t'il utiliser l'une ou l'autre des techniques ?

Tout d'abord la création de la session :
Tout le monde sait qu'elle a lieu quand dans le code on appelle la méthode request.getSession(). Ce que certains ne savent pas c'est qu'elle a aussi lieu quand on passe dans une jsp dans laquelle on a pas spécifié dans la directive page qu'il n'y avait pas besoin de session. Ex : <%@page session="false" %>
Si on n'y fait pas attention, il y a donc souvent des sessions créées inutilement.

Une fois la session créée, le serveur va faire tout son possible pour maintenir cette session il va donc :
  1. envoyer le cookie JSESSIONID
  2. ajouter le paramètre jsessionid dans les URL à condition que celles-ci passent par la méthode response.encodeURL(...) ou response.sendRedirect(...)
La réécriture d'URL est prévue à l'origine pour les navigateurs qui n'acceptent pas les cookies. Initialement à la création de la session, le serveur ne sait pas si le navigateur acceptera son cookie, il va donc bien activer les deux mécanismes simultanément pour être certain de ne pas perdre la session (en théorie).

Ensuite dans les requêtes suivantes, le serveur s'il reçoit le cookie saura qu'il n'est plus nécessaire de réécrire les URL. Le fonctionnement est donc simple coté serveur :
  • s'il y a un cookie de session dans la requête et le paramètre dans l'URL, on peut désactiver la réécriture
  • s'il y a un cookie de session mais par le paramètre dans l'URL, même chose
  • s'il y a uniquement le paramètre dans l'URL mais pas le cookie, il faudra continuer à réécrire toutes les URL tout au long de la navigation pour cet utilisateur !

Quel impact ?

Le mécanisme de réécriture d'URL va donc s'activer à l'arrivée d'un utilisateur sur le site mais aussi si par malheur un utilisateur refuse les cookies... ce qui est le cas pour les crawlers des moteurs de recherche comme Google, résultat : de nombreuses pages sont indexées par les moteurs de recherche avec un jsessionid. Elles peuvent aussi être mises en cache par des proxy.
On peut facilement vérifier ceci en saisissant des Google "inurl:;jsessionid". On obtient 216 000 000 de résultats !

Mais que fait le serveur quand un utilisateur arrive avec l'une de ces URL par accident (à cause d'un cache ou d'un moteur de recherche) ?
Si la session correspondante a expiré, une nouvelle session va être créée avec un nouvel identifiant. Mais si par contre la session est encore valide, on peut se retrouver avec plusieurs internautes qui vont partager la même session.

Autre cas de figure : dans le cas ou l'application est déployée sur une cluster, généralement avec affinité de session, le cookie comporte généralement deux partie dont la première est l'identifiant de session suivi par l'identifiant du noeud cible. Ex : ;jsessionid=5D1D8F2EA5F4E3B6B7058B65817EE539.tpdjo09v_2
Ceci a pour but qu'un même utilisateur soit toujours dirigé vers le même serveur pour conserver sa session. Mais si encore une fois de nombreux utilisateurs arrivent via cette même URL, ils seront tous dirigés vers le même noeud du cluster, entraînant un déséquilibre de charge.

Dans le cas d'un proxy qui fait aussi du cache, afin d'éviter ce genre de problèmes il faudrait pour bien faire refuser de mettre en cache les réponses dès lors qu'elles contiennent un en-tête "Set-cookie" puisqu'elle peuvent correspondre au moment de la création de session mais aussi repérer dans les requête les jsessionid pour ne pas non plus mettre en cache les résultats qui correspondront potentiellement au cas de l'utilisateur qui n'accepte pas les cookies.
On pourrait également imaginer filtrer les jsessionid directement dans les pages mais ce serait complexe et coûteux.

Il faut noter que dans le cas hypothétique ou un utilisateur s'amuserait réellement à naviguer sans cookie, il suffit d'un seul lien que les développeurs auraient oublié de réécrire (par exemple dans une application développée avec le framework Struts un lien en dur plutôt que généré par un tag Struts) pour que l'utilisateur perde sa session !

Conclusion

Le mécanisme de suivi de session par réécriture d'URL partait d'une bonne intention. Conçu à une époque ou certains navigateurs ne supportaient pas les cookies et ou certains utilisateurs les désactivaient volontairement.
De nos jours il semble impensable de naviguer sans cookie et d'ailleurs même sur les sites ou la réécriture fonctionne, il n'y en a pratiquement aucun pour lequel la réécriture soit systématique.
C'est donc devenu une mécanisme pratiquement inutile mais source de nombreux problèmes et qu'aucune option ne permet de désactiver.

En pratique certains serveurs proposent quand même de les désactiver même si ce n'est pas standardisé. Une autre solution est d'utiliser un Servlet Filter.
Pour les éditeurs de proxys, de caches ou de crawlers ça reste un gros problème.

1 commentaire:

Unknown a dit…

Depuis la version 3.0 de la Java™ Servlet Specification, il est désormais possible de désactiver le session tracking sur les url avec les conteneur tomcat 7 et glassfish 3

http://fralef.me/tomcat-disable-jsessionid-in-url.html