| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- <!-- EN-Revision: 12759 -->
- <sect1 id="zend.session.global_session_management">
- <title>Gestion générale de la session</title>
- <para>Le comportement des sessions peut être modifié en utilisant les méthodes statiques de la classe Zend_Session.
- Il s'agit du comportement global des sessions dans toute l'application, incluant la configuration des <ulink
- url="http://www.php.net/session#session.configuration">options usuelles offertes par ext/session</ulink>, ceci en
- utilisant <classname>Zend_Session::setOptions()</classname>. Ainsi, des problèmes de sécurité peuvent apparaître si vous
- utilisez mal le support de stockage des sessions <code>save_path</code> ou encore si vous négligez le cookie utilisé
- par ext/session.</para>
- <sect2 id="zend.session.global_session_management.configuration_options">
- <title>Options de configuration</title>
- <para>Lors de la création du premier namespace de session, Zend_Session va automatiquement démarrer la session
- PHP, sauf si celle-ci a été démarrée avec <link
- linkend="zend.session.advanced_usage.starting_a_session"><classname>Zend_Session::start()</classname></link> auparavant.
- La session PHP résultante utilisera les options de configuration par défaut de Zend_Session, sauf si ceux-ci ont
- été modifiés à l'aide de <classname>Zend_Session::setOptions()</classname>.</para>
- <para>Pour assigner une option de configuration, passez son nom (la partie qui suit "<code>session.</code>" dans
- les options de configuration de ext/session) comme clé au tableau passé à
- <classname>Zend_Session::setOptions()</classname>. La valeur correspondante dans le tableau sera alors utilisée comme
- valeur de l'option. Si vous omettez une option, alors celles par défaut recommandées par Zend_Session seront
- utilisées, sinon si elles n'existent pas, les valeurs par défaut de php.ini. Les retours et les idées quant aux
- "options recommandées" sont appréciées et peuvent être envoyées à <ulink
- url="mailto:fw-auth@lists.zend.com">fw-auth@lists.zend.com</ulink>.</para>
- <example id="zend.session.global_session_management.setoptions.example">
- <title>Utiliser Zend_Config pour configurer Zend_Session</title>
- <para>Pour configurer le composant en utilisant un objet <link
- linkend="zend.config.adapters.ini"><classname>Zend_Config_Ini</classname></link>, ajoutez ces paramètres au fichier
- INI en question:</para>
- <programlisting role="ini"><![CDATA[
- ; Paramètres de production
- [production]
- ; bug_compat_42
- ; bug_compat_warn
- ; cache_expire
- ; cache_limiter
- ; cookie_domain
- ; cookie_lifetime
- ; cookie_path
- ; cookie_secure
- ; entropy_file
- ; entropy_length
- ; gc_divisor
- ; gc_maxlifetime
- ; gc_probability
- ; hash_bits_per_character
- ; hash_function
- ; name doit être unique pour chaque application partageant le même nom de domaine
- name = UNIQUE_NAME
- ; referer_check
- ; save_handler
- ; save_path
- ; serialize_handler
- ; use_cookies
- ; use_only_cookies
- ; use_trans_sid
- ; remember_me_seconds = <integer seconds>
- ; strict = on|off
- ; Development hérite de production, mais redéfinit certaines valeurs
- [development : production]
- ; N'oubliez pas de créer ce dossier et d'attribuer à PHP les droits 'rwx'.
- save_path = /home/myaccount/zend_sessions/myapp
- use_only_cookies = on
- ; Le cookie de session durera 10 jours
- remember_me_seconds = 864000
- ]]></programlisting>
- <para>Ensuite, chargez ce fichier de configuration, et passez sa représentation tableau à
- <classname>Zend_Session::setOptions()</classname>:</para>
- <programlisting role="php"><![CDATA[
- $config = new Zend_Config_Ini('myapp.ini', 'development');
- require_once 'Zend/Session.php';
- Zend_Session::setOptions($config->toArray());
- ]]></programlisting>
- </example>
- <para>La plupart des options ne nécessitent pas d'explications étant donné qu'elles font parti des options de
- ext/session, documentées dans le manuel officiel de PHP, cependant les options particulières méritent une
- description: <itemizedlist mark="opencircle">
- <listitem>
- <para>bool <code>strict</code> : désactive le démarrage automatique de <classname>Zend_Session</classname>
- lorsque <code>new Zend_Session_Namespace()</code> est utilisé.</para>
- </listitem>
- <listitem>
- <para>int <code>remember_me_seconds</code> : temps de vie du cookie de session, une fois le
- navigateur client fermé.</para>
- </listitem>
- <listitem>
- <para>string <code>save_path</code> : Cette valeur est dépendante du système sur lequel PHP est
- lancé. Un <emphasis role="strong">chemin absolu</emphasis> vers un dossier lisible et écrivable à
- PHP devrait être utilisé (dans le cas d'utilisation d'un dossier pour le support des sessions). Si
- le chemin n'est pas pleinement accessible à PHP, <classname>Zend_Session</classname> lancera une exception à
- son démarrage (lorsque <code>start()</code> est appelée.</para>
- <note>
- <title>Attention aux failles de sécurité</title>
- <para>Si le chemin des sessions est accessible en lecture à d'autres applications, alors le vol
- de session peut être possible. Si le dossier est accessible en écriture à d'autres applications,
- alors <ulink url="http://en.wikipedia.org/wiki/Session_poisoning">l'empoisonnement de
- sessions</ulink> peut être possible. SI le chemin est partagé avec d'autres utilisateurs ou
- d'autres applications PHP, plusieurs problèmes de sécurité peuvent apparaître, incluant le vol
- de session, et les collisions de ramasse-miette (garbage collection) (Un process d'une autre
- application PHP déclenche une collecte sur vos fichiers de session).</para>
- <para>Par exemple, un pirate peut visiter le site d'une victime pour obtenir un cookie de
- session. Il modifie ensuite le chemin du cookie afin que celui-ci soit envoyé à sa propre
- application (en partage sur le même serveur que le votre), et il exécute
- <code>var_dump($_SESSION)</code>. Il obtient alors des informations sur les variables de session
- que vous stockez, et il peut les modifier pour retourner sur votre site. L'empoisonnement a eu
- lieu. Même si deux applications sur le même serveur ne partagent pas le même dossier
- <code>save_path</code>, si celui-ci est devinable, l'attaquant peut alors l'utiliser sur sa
- propre application et dans certaines configurations de PHP, accéder à la session de
- l'application victime. La valeur du <code>save_path</code> ne doit pas être rendue publique ou
- devinable, le dossier doit se trouver dans un endroit isolé et sécurisé.</para>
- </note>
- </listitem>
- <listitem>
- <para>string <code>name</code> - La valeur doit être choisie de manière <emphasis
- role="strong">unique</emphasis> pour chaque application.</para>
- <note>
- <title>Risque de sécurité</title>
- <para>Si la valeur <code>php.ini</code> de <code>session.name</code> n'est pas unique (celle par
- défaut "PHPSESSID"), et qu'il existe plusieurs applications accessible via le même domaine,
- alors elle partagerons leurs données pour les visiteurs. Aussi, des problème de corruption
- peuvent apparaître.</para>
- </note>
- </listitem>
- <listitem>
- <para>bool <code>use_only_cookies</code> - Afin d'éviter d'autres failles de sécurité (concernant le
- trans-sid), ne changez pas cette option. <note>
- <title>Risque de sécurité</title>
- <para>Si cette option n'est pas activée, un attaquant peut facilement fixer un id de session
- d'une victime en lui envoyant des liens tels que
- <code>http://www.example.com/index.php?PHPSESSID=fixed_session_id</code>. La fixation
- fonctionne si la victime n'a pas déjà un identifiant de session sur le site example.com.
- Lorsque la victime utilise un identifiant de session qu'un attaquant connaît, il peut alors
- se faire passer pour elle.</para>
- </note></para>
- </listitem>
- </itemizedlist></para>
- </sect2>
- <sect2 id="zend.session.global_session_management.headers_sent">
- <title>L'erreur: "Headers Already Sent"</title>
- <para>Si vous voyez l'erreur, "Cannot modify header information - headers already sent", ou, "You must call ...
- before any output has been sent to the browser; output started in ...", analysez tout de suite d'où vient la
- fuite grâce au message d'erreur. Toute action entraînant un envoi d'en-têtes HTTP, comme envoyer un cookie, doit
- être effectuée avant d'envoyer du contenu standard (non bufferisé), sauf si le buffer de sortie de PHP est
- activé.</para>
- <itemizedlist mark="opencircle">
- <listitem>
- <para>Utiliser <ulink url="http://php.net/outcontrol">le buffer de sortie</ulink> résout souvent le
- problème, et peut améliorer les performances. Par exemple, une valeur <code>php.ini</code>,
- "<code>output_buffering = 65535</code>" active un buffer de 64K. Même si le buffer de sortie peut
- améliorer les performances lorsqu'il est bien configuré, se reposer sur lui concernant les erreurs
- "headers already sent" n'est pas suffisant. En effet, sa taille peut être dépassé entraînant son vidage,
- et le problème revient.</para>
- </listitem>
- <listitem>
- <para>Aussi, il convient d'organiser l'application de manière à ce que les envois d'en-tête se passent
- avant l'envoi de contenu.</para>
- </listitem>
- <listitem>
- <para>Si Zend_Session produit ce message, cherchez la cause grâce au message d'erreur indiquant d'où
- provient "la fuite". Aussi, des opérations comme <code>destroy()</code> envoient des en-têtes concernant
- la destruction du cookie de session. Si vous ne voulez pas ces informations envoyées, utilisez alors
- <code>destroy(false)</code>.</para>
- </listitem>
- <listitem>
- <para>Supprimez tous les balises de fermeture "<code>?></code>", si elles terminent du code PHP.
- Elles sont facultatives et les nouvelles lignes blanches éventuelles en fin de fichier ne seront pas
- envoyées, car parsées par PHP.</para>
- </listitem>
- </itemizedlist>
- </sect2>
- <sect2 id="zend.session.global_session_management.session_identifiers">
- <title>Identifiants de session</title>
- <para>Les bonnes pratiques d'utilisation des sessions avec Zend Framework passent par un cookie, plutôt que se reporter à
- l'URL concernant l'identifiant de session. Par défaut, le composant Zend_Session est bloqué sur l'utilisation
- unique du cookie comme moyen de propagation de l'identifiant de session. La session PHP va alors utiliser cet
- identifiant de manière à identifier de manière unique chaque client (navigateur) qui s'y connecte, et maintenir
- un état entre leurs transactions, donnant l'impression de conservation de données. Zend_Session_* utilise alors
- le tableau (<code>$_SESSION</code>) et vous y donne accès d'une manière objet élégante. Attention, si un
- attaquant arrive à accéder au cookie de session d'une victime, il pourra alors tromper le serveur, et se faire
- passer pour la victime. Ce comportement n'est pas unique à PHP, ni à Zend Framework, mais au Web en général, et
- au protocole HTTP. La méthode <code>regenerateId()</code> permet de changer l'identifiant de session stocké dans
- le cookie du client, par un autre, en théorie imprévisible. Notez que par la suite, nous confondons les termes
- 'client' et 'navigateur', même si ceci n'est pas tout à fait juste.</para>
- <para>Changer l'identifiant de session permet d'aider contre le vol de données. Si un attaquant possède
- l'identifiant d'une victime, le changer ne changera rien pour la victime, mais provoquera une invalidation de la
- session de l'attaquant, qui ne connaît alors pas la nouvelle valeur de l'identifiant de session. Non seulement
- <code>regenerateId()</code> change l'identifiant de session, mais en plus il migre les données de l'ancien
- identifiant vers le nouveau, invalidant totalement l'ancien.</para>
- <para>Quand régénérer cet identifiant ? En théorie, mettre <classname>Zend_Session::regenerateId()</classname> en
- bootstrap est la manière la plus adaptée pour sécuriser une session. Cependant, ceci a un coût non négligeable,
- car il faut alors à chaque fois régénérer un identifiant, et renvoyer un nouveau cookie au client. Il est alors
- nécessaire de déterminer les situations 'à risque', et régénérer alors l'identifiant de session dans de telles
- situations. Ces situations peuvent être par exemple l'authentification d'un client, ou encore son élévation de
- privilèges. Si vous appelez <code>rememberMe()</code>, n'appelez alors pas <code>regenerateId()</code>, car elle
- sera appelée de manière automatique.</para>
- <sect3 id="zend.session.global_session_management.session_identifiers.hijacking_and_fixation">
- <title>Vol de session et fixation</title>
- <para>Éviter <ulink url="http://en.wikipedia.org/wiki/Cross_site_scripting">les failles cross-site script
- (XSS) </ulink> aide à éviter le vol de session. Selon <ulink url="http://secunia.com/">Secunia</ulink>, les
- problèmes XSS sont fréquents, quelque soit le langage utilisé pour créer l'application Web. Plutôt que de se
- considérer invulnérable, considérez votre application de manière à minimiser l'impact d'une éventuelle
- faille XSS. Avec XSS, l'attaquant n'a pas besoin d'accéder au trafic de la victime, sur le réseau. Si la
- victime possède déjà un cookie de session, javascript peut permettre à l'attaquant de voler celui-ci, et
- donc la session. Dans le cas de victimes sans cookie, l'attaquant peut utiliser XSS pour créer un cookie
- avec un session id connu, et l'envoyer à la victime, fixant ainsi la session. L'attaquant peut dès lors
- visualiser toute la session de la victime au fur et à mesure que celle-ci surfe, sans se rendre compte de
- rien. Cependant, l'attaquant ne peut modifier l'état de la session du coté PHP ( la fermer par exemple ),
- sauf si l'application possède d'autres vulnérabilités (CSRF), ou si le <code>save_path</code> est
- modifiable.</para>
- <para>En elle-même, la fonction <classname>Zend_Session::regenerateId()</classname> utilisée à la première utilisation
- de la session, ne protège pas contre la fixation. Ceci peut paraître contradictoire, mais un attaquant peut
- très bien initialiser une session de lui-même, qui sera alors rafraîchie (régénérée), et dont il connaîtra
- alors l'identifiant. Il n'aura plus qu'à fixer cet identifiant dans un javascript pour qu'une victime
- l'utilise, et la faille est à nouveau présente. Aussi, fixer la session par l'URL est extrêmement simple,
- mais n'est possible que lorsque <code>use_only_cookies = off</code>.</para>
- <para>Le vol de session ne peut se remarqué que si vous arrivez à faire la différence entre l'attaquant et
- la victime. Ce n'est pas chose simple, et les techniques utilisées ne sont jamais fiables à 100%. L'IP peut
- être utilisée, même si celle-ci n'est pas totalement fiable. Les en-têtes du navigateur Web, eux, le sont
- déjà plus (lorsque 2 requêtes successives avec le même identifiant de session arrivent au serveur, si l'une
- prétend être issue de FireFox et l'autre d'Opéra, alors très probablement qu'il s'agit de 2 personnes
- différentes, mais ayant le même identifiant de session. Typiquement : l'attaquant et sa victime.) Il est
- très difficile de différencier l'attaquant et la victime, c'est d'ailleurs impossible dans la suite de cas
- suivants : <itemizedlist mark="opencircle">
- <listitem>
- <para>l'attaquant initialise une session pour obtenir un identifiant valide.</para>
- </listitem>
- <listitem>
- <para>l'attaquant utilise une faille XSS pour envoyer un cookie de session à une victime,
- possédant son propre identifiant de session (fixation).</para>
- </listitem>
- <listitem>
- <para>l'attaquant et la victime utilisent le même navigateur, sont derrière le même
- proxy.</para>
- </listitem>
- </itemizedlist> Le code suivant permet d'empêcher l'attaquant de connaître l'identifiant de session de
- la victime (sauf s'il arrive à le fixer):</para>
- <example id="zend.session.global_session_management.session_identifiers.hijacking_and_fixation.example">
- <title>Vol et fixation, protections</title>
- <programlisting role="php"><![CDATA[
- $defaultNamespace = new Zend_Session_Namespace();
- if (!isset($defaultNamespace->initialized)) {
- Zend_Session::regenerateId();
- $defaultNamespace->initialized = true;
- }
- ]]></programlisting>
- </example>
- </sect3>
- </sect2>
- <sect2 id="zend.session.global_session_management.rememberme">
- <title><code>rememberMe(integer $seconds)</code></title>
- <para>Par défaut, la session se termine lorsque le client ferme son navigateur. Il peut cependant être
- nécessaire de faire en sorte que même après la fermeture, le cookie de session persiste un certain temps dans le
- navigateur. Utilisez <classname>Zend_Session::rememberMe()</classname> avant tout démarrage de la session, afin de
- spécifier à celle-ci qu'elle devra utiliser un cookie persistant du coté du client. Ce cookie persistera alors
- $seconds secondes. Si vous ne précisez pas de temps, <code>remember_me_seconds</code>, sera utilisé. Cette
- valeur se paramètre d'ailleurs au moyen de <classname>Zend_Session::setOptions()</classname>.</para>
- </sect2>
- <sect2 id="zend.session.global_session_management.forgetme">
- <title><code>forgetMe()</code></title>
- <para>Cette fonction est analogue à <code>rememberMe()</code> sauf qu'elle demande au cookie de session du
- navigateur client d'être détruit à la fermeture de celui-ci (et non éventuellement après X temps).</para>
- </sect2>
- <sect2 id="zend.session.global_session_management.sessionexists">
- <title><code>sessionExists()</code></title>
- <para>Utilisez cette méthode afin de savoir si une session existe pour le client (la requête) actuel. Ceci doit
- être utilisé avant le démarrage de la session.</para>
- </sect2>
- <sect2 id="zend.session.global_session_management.destroy">
- <title><code>destroy(bool $remove_cookie = true, bool $readonly = true)</code></title>
- <para><classname>Zend_Session::destroy()</classname> détruit la session et toutes les données la concernant. Cependant,
- aucune variable dans PHP n'est affectée, donc vos namespaces de session (instances de
- <classname>Zend_Session_Namespace</classname>) restent lisibles. Pour compléter la "déconnexion", laissez le premier
- paramètre à <code>true</code> (par défaut), demandant l'expiration du cookie de session du client.
- <code>$readonly</code> permet d'empêcher la future création de namespaces (new
- <classname>Zend_Session_Namespace</classname>) ou des opérations d'écriture via <classname>Zend_Session</classname>.</para>
- <para>Si vous voyez le message d'erreur "Cannot modify header information - headers already sent", alors tentez
- de ne pas utiliser <code>true</code> comme valeur du premier argument (ceci demande l'expiration du cookie de
- session, ou voyez <xref linkend="zend.session.global_session_management.headers_sent" />. Ainsi,
- <classname>Zend_Session::destroy(true)</classname> doit être appelé avant tout envoi d'en-tête HTTP par PHP, ou alors la
- bufferisation de sortie doit être activée (sans que celui-ci ne déborde).</para>
- <note>
- <title>Exception</title>
- <para>Par défaut, <code>$readonly</code> est activé et toute opération future d'écriture dans la session
- lèvera une exception.</para>
- </note>
- </sect2>
- <sect2 id="zend.session.global_session_management.stop">
- <title><code>stop()</code></title>
- <para>Cette méthode ne fait rien d'autre que de verrouiller la session en écriture. Tout appel futur d'écriture
- via des instances de <classname>Zend_Session_Namespace</classname> ou <classname>Zend_Session</classname> lèvera une
- exception.</para>
- </sect2>
- <sect2 id="zend.session.global_session_management.writeclose">
- <title><code>writeClose($readonly = true)</code></title>
- <para>Ferme la session coté serveur, soit enregistre les variables de session dans le support, et détache
- <code>$_SESSION</code> de son support de stockage. Le paramètre optionnel <code>$readonly</code> empêche alors
- toute future écriture via <classname>Zend_Session</classname> ou <classname>Zend_Session_Namespace</classname>. Ces écritures
- lèveront une exception.</para>
- <note>
- <title>Exception</title>
- <para>Par défaut, <code>$readonly</code> est activé, et donc tout appel d'écriture futur dans la session
- générera une exception. Certaines applications peuvent nécessiter de conserver un accès en écriture dans
- <code>$_SESSION</code>, même si ce tableau a été déconnecté de son support de stockage avec
- <code>session_write_close()</code>. Ainsi, Zend Framework propose cette option en passant à
- <code>false</code> la valeur de <code>$readonly</code>, mais ce n'est pas une pratique conseillée.</para>
- </note>
- </sect2>
- <sect2 id="zend.session.global_session_management.expiresessioncookie">
- <title><code>expireSessionCookie()</code></title>
- <para>Cette méthode envoie un cookie d'identifiant de session périmé au client. Quelque fois cette technique est
- utilisée pour déconnecter le client de sa session.</para>
- </sect2>
- <sect2 id="zend.session.global_session_management.savehandler">
- <title><code>setSaveHandler(Zend_Session_SaveHandler_Interface $interface)</code></title>
- <para>Cette méthode propose une correspondance orientée objet de <ulink
- url="http://php.net/session_set_save_handler"><code>session_set_save_handler()</code></ulink>.</para>
- </sect2>
- <sect2 id="zend.session.global_session_management.namespaceisset">
- <title><code>namespaceIsset($namespace)</code></title>
- <para>Cette méthode permet de déterminer si un namespace existe dans la session.</para>
- <note>
- <title>Exception</title>
- <para>Une exception sera levée si la session n'est pas lisible (n'a pas été démarrée).</para>
- </note>
- </sect2>
- <sect2 id="zend.session.global_session_management.namespaceunset">
- <title><code>namespaceUnset($namespace)</code></title>
- <para>Utilisez <classname>Zend_Session::namespaceUnset($namespace)</classname> pour détruire un namespace entier de la
- session. Comme pour les tableaux PHP, si le tableau est détruit, les objets à l'intérieur ne le sont pas s'il
- reste des références vers eux dans d'autres tableaux ou objets toujours accessibles. Ainsi
- <code>namespaceUnset()</code> ne détruit pas "en profondeur" la variable de session associée au namespace. Voyez
- <ulink url="http://php.net/references">les références en PHP</ulink> pour plus d'infos.</para>
- <note>
- <title>Exception</title>
- <para>Une exception sera envoyée si le namespace n'est pas écrivable (après un appel à
- <code>destroy()</code>).</para>
- </note>
- </sect2>
- <sect2 id="zend.session.global_session_management.namespaceget">
- <title><code>namespaceGet($namespace)</code></title>
- <para>Déprécié: Utilisez <code>getIterator()</code> dans <classname>Zend_Session_Namespace</classname>. Cette méthode
- retourne un tableau du contenu du namespace $namespace. Si vous avez une raison de conserver cette méthode,
- faites nous part de vos remarques à <ulink
- url="mailto:fw-auth@lists.zend.com">fw-auth@lists.zend.com</ulink>.</para>
- <note>
- <title>Exception</title>
- <para>Une exception sera levée si la session n'est pas lisible (n'a pas été démarrée).</para>
- </note>
- </sect2>
- <sect2 id="zend.session.global_session_management.getiterator">
- <title><code>getIterator()</code></title>
- <para><code>getIterator()</code> retourne un <code>ArrayObject</code> contenant tous les noms des namespaces de
- session.</para>
- <note>
- <title>Exception</title>
- <para>Une exception sera levée si la session n'est pas lisible (n'a pas été démarrée).</para>
- </note>
- </sect2>
- </sect1>
|