1 <?xml version="1.0" encoding="UTF-8"?>
3 <sect1 id="zend.session.global_session_management">
4 <title>Global Session Management</title>
7 The default behavior of sessions can be modified using the static methods of
8 <classname>Zend_Session</classname>. All management and manipulation of global session
9 management occurs using <classname>Zend_Session</classname>, including configuration of the
10 <ulink url="http://www.php.net/session#session.configuration">usual options provided by
11 ext/session</ulink>, using <methodname>Zend_Session::setOptions()</methodname>. For
12 example, failure to insure the use of a safe <code>save_path</code> or a unique cookie name
13 by ext/session using <methodname>Zend_Session::setOptions()</methodname> may result in
17 <sect2 id="zend.session.global_session_management.configuration_options">
18 <title>Configuration Options</title>
21 When the first session namespace is requested, <classname>Zend_Session</classname> will
22 automatically start the <acronym>PHP</acronym> session, unless already started with
24 linkend="zend.session.advanced_usage.starting_a_session"><methodname>Zend_Session::start()</methodname></link>.
25 The underlying <acronym>PHP</acronym> session will use defaults from
26 <classname>Zend_Session</classname>, unless modified first by
27 <methodname>Zend_Session::setOptions()</methodname>.
31 To set a session configuration option, include the basename (the part of the name after
32 "<code>session.</code>") as a key of an array passed to
33 <methodname>Zend_Session::setOptions()</methodname>. The corresponding value in the
34 array is used to set the session option value. If no options are set by the developer,
35 <classname>Zend_Session</classname> will utilize recommended default options first, then
36 the default php.ini settings. Community feedback about best practices for these options
37 should be sent to <ulink
38 url="mailto:fw-auth@lists.zend.com">fw-auth@lists.zend.com</ulink>.
41 <example id="zend.session.global_session_management.setoptions.example">
42 <title>Using Zend_Config to Configure Zend_Session</title>
45 To configure this component using <link
46 linkend="zend.config.adapters.ini"><classname>Zend_Config_Ini</classname></link>,
47 first add the configuration options to the <acronym>INI</acronym> file:
50 <programlisting language="ini"><![CDATA[
51 ; Accept defaults for production
66 ; hash_bits_per_character
68 ; name should be unique for each PHP application sharing the same
79 ; remember_me_seconds = <integer seconds>
82 ; Development inherits configuration from production, but overrides
84 [development : production]
85 ; Don't forget to create this directory and make it rwx (readable and
87 save_path = /home/myaccount/zend_sessions/myapp
89 ; When persisting session id cookies, request a TTL of 10 days
90 remember_me_seconds = 864000
94 Next, load the configuration file and pass its array representation to
95 <methodname>Zend_Session::setOptions()</methodname>:
98 <programlisting language="php"><![CDATA[
99 $config = new Zend_Config_Ini('myapp.ini', 'development');
101 Zend_Session::setOptions($config->toArray());
106 Most options shown above need no explanation beyond that found in the standard
107 <acronym>PHP</acronym> documentation, but those of particular interest are noted below.
109 <itemizedlist mark="opencircle">
112 boolean <code>strict</code> - disables automatic starting of
113 <classname>Zend_Session</classname> when using
114 <code>new Zend_Session_Namespace()</code>.
120 integer <code>remember_me_seconds</code> - how long should session id cookie
121 persist, after user agent has ended (e.g., browser application terminated).
127 string <code>save_path</code> - The correct value is system dependent, and
128 should be provided by the developer using an
129 <emphasis>absolute path</emphasis> to a directory readable and writable by
130 the <acronym>PHP</acronym> process. If a writable path is not supplied, then
131 <classname>Zend_Session</classname> will throw an exception when started
132 (i.e., when <methodname>start()</methodname> is called).
136 <title>Security Risk</title>
139 If the path is readable by other applications, then session hijacking
140 might be possible. if the path is writable by other applications, then
141 <ulink url="http://en.wikipedia.org/wiki/Session_poisoning">session
142 poisoning</ulink> might be possible. If this path is shared with
143 other users or other <acronym>PHP</acronym> applications, various
144 security issues might occur, including theft of session content,
145 hijacking of sessions, and collision of garbage collection (e.g.,
146 another user's application might cause <acronym>PHP</acronym> to delete
147 your application's session files).
151 For example, an attacker can visit the victim's website to obtain a
152 session cookie. Then, he edits the cookie path to his own domain on the
153 same server, before visiting his own website to execute
154 <methodname>var_dump($_SESSION)</methodname>. Armed with detailed
155 knowledge of the victim's use of data in their sessions, the attacker
156 can then modify the session state (poisoning the session), alter the
157 cookie path back to the victim's website, and then make requests from
158 the victim's website using the poisoned session. Even if two
159 applications on the same server do not have read/write access to the
160 other application's <code>save_path</code>, if the
161 <code>save_path</code> is guessable, and the attacker has control over
162 one of these two websites, the attacker could alter their website's
163 <code>save_path</code> to use the other's save_path, and thus accomplish
164 session poisoning, under some common configurations of
165 <acronym>PHP</acronym>. Thus, the value for <code>save_path</code>
166 should not be made public knowledge and should be altered to a secure
167 location unique to each application.
174 string <code>name</code> - The correct value is system dependent and should
175 be provided by the developer using a value <emphasis>unique</emphasis> to
180 <title>Security Risk</title>
183 If the <code>php.ini</code> setting for <code>session.name</code> is the
184 same (e.g., the default "PHPSESSID"), and there are two or more
185 <acronym>PHP</acronym> applications accessible through the same domain
186 name then they will share the same session data for visitors to both
187 websites. Additionally, possible corruption of session data may result.
194 boolean <code>use_only_cookies</code> - In order to avoid introducing
195 additional security risks, do not alter the default value of this option.
198 <title>Security Risk</title>
201 If this setting is not enabled, an attacker can easily fix victim's
202 session ids, using links on the attacker's website, such as
203 <code>http://www.example.com/index.php?PHPSESSID=fixed_session_id</code>.
204 The fixation works, if the victim does not already have a session id
205 cookie for example.com. Once a victim is using a known session id,
206 the attacker can then attempt to hijack the session by pretending to
207 be the victim, and emulating the victim's user agent.
216 <sect2 id="zend.session.global_session_management.headers_sent">
217 <title>Error: Headers Already Sent</title>
220 If you see the error message, "Cannot modify header information - headers already sent",
221 or, "You must call ... before any output has been sent to the browser; output started in
222 ...", then carefully examine the immediate cause (function or method) associated with
223 the message. Any actions that require sending <acronym>HTTP</acronym> headers, such as
224 sending a cookie, must be done before sending normal output (unbuffered output), except
225 when using <acronym>PHP</acronym>'s output buffering.
228 <itemizedlist mark="opencircle">
231 Using <ulink url="http://php.net/outcontrol">output buffering</ulink> often is
232 sufficient to prevent this issue, and may help improve performance. For example,
233 in <code>php.ini</code>, "<code>output_buffering = 65535</code>" enables output
234 buffering with a 64K buffer. Even though output buffering might be a good tactic
235 on production servers to increase performance, relying only on buffering to
236 resolve the "headers already sent" problem is not sufficient. The application
237 must not exceed the buffer size, or the problem will occur whenever the output
238 sent (prior to the <acronym>HTTP</acronym> headers) exceeds the buffer size.
244 If a <classname>Zend_Session</classname> method is involved in causing the error
245 message, examine the method carefully, and make sure its use really is needed in
246 the application. For example, the default usage of
247 <methodname>destroy()</methodname> also sends an <acronym>HTTP</acronym> header
248 to expire the client-side session cookie. If this is not needed, then use
249 <methodname>destroy(false)</methodname>, since the instructions to set cookies
250 are sent with <acronym>HTTP</acronym> headers.
256 Alternatively, try rearranging the application logic so that all actions
257 manipulating headers are performed prior to sending any output whatsoever.
263 Remove any closing "<code>?></code>" tags, if they occur at the end of a
264 <acronym>PHP</acronym> source file. They are not needed, and newlines and other
265 nearly invisible whitespace following the closing tag can trigger output to the
272 <sect2 id="zend.session.global_session_management.session_identifiers">
273 <title>Session Identifiers</title>
276 Introduction: Best practice in relation to using sessions with Zend Framework calls for
277 using a browser cookie (i.e. a normal cookie stored in your web browser), instead of
278 embedding a unique session identifier in <acronym>URL</acronym>s as a means to track
279 individual users. By default this component uses only cookies to maintain session
280 identifiers. The cookie's value is the unique identifier of your browser's session.
281 <acronym>PHP</acronym>'s ext/session uses this identifier to maintain a unique
282 one-to-one relationship between website visitors, and persistent session data storage
283 unique to each visitor. <classname>Zend_Session</classname>* wraps this storage
284 mechanism (<varname>$_SESSION</varname>) with an object-oriented interface.
285 Unfortunately, if an attacker gains access to the value of the cookie (the session id),
286 an attacker might be able to hijack a visitor's session. This problem is not unique to
287 <acronym>PHP</acronym>, or Zend Framework. The <methodname>regenerateId()</methodname>
288 method allows an application to change the session id (stored in the visitor's cookie)
289 to a new, random, unpredictable value. Note: Although not the same, to make this section
290 easier to read, we use the terms "user agent" and "web browser" interchangeably.
294 Why?: If an attacker obtains a valid session identifier, an attacker might be able to
295 impersonate a valid user (the victim), and then obtain access to confidential
296 information or otherwise manipulate the victim's data managed by your application.
297 Changing session ids helps protect against session hijacking. If the session id is
298 changed, and an attacker does not know the new value, the attacker can not use the new
299 session id in their attempts to hijack the visitor's session. Even if an attacker gains
300 access to an old session id, <methodname>regenerateId()</methodname> also moves the
301 session data from the old session id "handle" to the new one, so no data remains
302 accessible via the old session id.
306 When to use regenerateId(): Adding <methodname>Zend_Session::regenerateId()</methodname>
307 to your Zend Framework bootstrap yields one of the safest and most secure ways to
308 regenerate session id's in user agent cookies. If there is no conditional logic to
309 determine when to regenerate the session id, then there are no flaws in that logic.
310 Although regenerating on every request prevents several possible avenues of attack, not
311 everyone wants the associated small performance and bandwidth cost. Thus, applications
312 commonly try to dynamically determine situations of greater risk, and only regenerate
313 the session ids in those situations. Whenever a website visitor's session's privileges
314 are "escalated" (e.g. a visitor re-authenticates their identity before editing their
315 personal "profile"), or whenever a security "sensitive" session parameter change occurs,
316 consider using <methodname>regenerateId()</methodname> to create a new session id. If
317 you call the <methodname>rememberMe()</methodname> function, then don't use
318 <methodname>regenerateId()</methodname>, since the former calls the latter. If a user
319 has successfully logged into your website, use <methodname>rememberMe()</methodname>
320 instead of <methodname>regenerateId()</methodname>.
324 id="zend.session.global_session_management.session_identifiers.hijacking_and_fixation">
325 <title>Session Hijacking and Fixation</title>
328 Avoiding <ulink url="http://en.wikipedia.org/wiki/Cross_site_scripting">cross-site
329 script (XSS) vulnerabilities</ulink> helps preventing session hijacking.
330 According to <ulink url="http://secunia.com/">Secunia's</ulink> statistics XSS
331 problems occur frequently, regardless of the languages used to create web
332 applications. Rather than expecting to never have a XSS problem with an application,
333 plan for it by following best practices to help minimize damage, if it occurs. With
334 XSS, an attacker does not need direct access to a victim's network traffic. If the
335 victim already has a session cookie, Javascript XSS might allow an attacker to read
336 the cookie and steal the session. for victims with no session cookies, using XSS to
337 inject Javascript, an attacker could create a session id cookie on the victim's
338 browser with a known value, then set an identical cookie on the attacker's system,
339 in order to hijack the victim's session. If the victim visited an attacker's
340 website, then the attacker can also emulate most other identifiable characteristics
341 of the victim's user agent. If your website has an XSS vulnerability, the attacker
342 might be able to insert an <acronym>AJAX</acronym> Javascript that secretly "visits"
343 the attacker's website, so that the attacker knows the victim's browser
344 characteristics and becomes aware of a compromised session at the victim website.
345 However, the attacker can not arbitrarily alter the server-side state of
346 <acronym>PHP</acronym> sessions, provided the developer has correctly set the value
347 for the <code>save_path</code> option.
351 By itself, calling <methodname>Zend_Session::regenerateId()</methodname> when the
352 user's session is first used, does not prevent session fixation attacks, unless you
353 can distinguish between a session originated by an attacker emulating the victim. At
354 first, this might sound contradictory to the previous statement above, until we
355 consider an attacker who first initiates a real session on your website. The session
356 is "first used" by the attacker, who then knows the result of the initialization
357 (<methodname>regenerateId()</methodname>). The attacker then uses the new session id
358 in combination with an XSS vulnerability, or injects the session id via a link on
359 the attacker's website (works if <code>use_only_cookies = off</code>).
363 If you can distinguish between an attacker and victim using the same session id,
364 then session hijacking can be dealt with directly. However, such distinctions
365 usually involve some form of usability tradeoffs, because the methods of distinction
366 are often imprecise. For example, if a request is received from an IP in a different
367 country than the IP of the request when the session was created, then the new
368 request probably belongs to an attacker. Under the following conditions, there might
369 not be any way for a website application to distinguish between a victim and an
372 <itemizedlist mark='opencircle'>
375 attacker first initiates a session on your website to obtain a valid
382 attacker uses XSS vulnerability on your website to create a cookie on
383 the victim's browser with the same, valid session id (i.e. session
390 both the victim and attacker originate from the same proxy farm (e.g.
391 both are behind the same firewall at a large company, like AOL)
396 The sample code below makes it much harder for an attacker to know the current
397 victim's session id, unless the attacker has already performed the first two steps
402 id="zend.session.global_session_management.session_identifiers.hijacking_and_fixation.example">
403 <title>Session Fixation</title>
405 <programlisting language="php"><![CDATA[
406 $defaultNamespace = new Zend_Session_Namespace();
408 if (!isset($defaultNamespace->initialized)) {
409 Zend_Session::regenerateId();
410 $defaultNamespace->initialized = true;
417 <sect2 id="zend.session.global_session_management.rememberme">
418 <title>rememberMe(integer $seconds)</title>
421 Ordinarily, sessions end when the user agent terminates, such as when an end user exits
422 a web browser program. However, your application may provide the ability to extend user
423 sessions beyond the lifetime of the client program through the use of persistent
424 cookies. Use <methodname>Zend_Session::rememberMe()</methodname> before a session is
425 started to control the length of time before a persisted session cookie expires. If you
426 do not specify a number of seconds, then the session cookie lifetime defaults to
427 <code>remember_me_seconds</code>, which may be set using
428 <methodname>Zend_Session::setOptions()</methodname>. To help thwart session
429 fixation/hijacking, use this function when a user successfully authenticates with your
430 application (e.g., from a "login" form).
434 <sect2 id="zend.session.global_session_management.forgetme">
435 <title>forgetMe()</title>
438 This function complements <methodname>rememberMe()</methodname> by writing a session
439 cookie that has a lifetime ending when the user agent terminates.
443 <sect2 id="zend.session.global_session_management.sessionexists">
444 <title>sessionExists()</title>
447 Use this method to determine if a session already exists for the current user
448 agent/request. It may be used before starting a session, and independently of all other
449 <classname>Zend_Session</classname> and <classname>Zend_Session_Namespace</classname>
454 <sect2 id="zend.session.global_session_management.destroy">
455 <title>destroy(bool $remove_cookie = true, bool $readonly = true)</title>
458 <methodname>Zend_Session::destroy()</methodname> destroys all of the persistent data
459 associated with the current session. However, no variables in <acronym>PHP</acronym> are
460 affected, so your namespaced sessions (instances of
461 <classname>Zend_Session_Namespace</classname>) remain readable. To complete a "logout",
462 set the optional parameter to <constant>TRUE</constant> (the default) to also delete the
463 user agent's session id cookie. The optional <varname>$readonly</varname> parameter
464 removes the ability to create new <classname>Zend_Session_Namespace</classname>
465 instances and for <classname>Zend_Session</classname> methods to write to the session
470 If you see the error message, "Cannot modify header information - headers already sent",
471 then either avoid using <constant>TRUE</constant> as the value for the first argument
472 (requesting removal of the session cookie), or see <xref
473 linkend="zend.session.global_session_management.headers_sent" />. Thus,
474 <methodname>Zend_Session::destroy(true)</methodname> must either be called before
475 <acronym>PHP</acronym> has sent <acronym>HTTP</acronym> headers, or output buffering
476 must be enabled. Also, the total output sent must not exceed the set buffer size, in
477 order to prevent triggering sending the output before the call to
478 <methodname>destroy()</methodname>.
482 <title>Throws</title>
485 By default, <varname>$readonly</varname> is enabled and further actions involving
486 writing to the session data store will throw an exception.
491 <sect2 id="zend.session.global_session_management.stop">
492 <title>stop()</title>
495 This method does absolutely nothing more than toggle a flag in
496 <classname>Zend_Session</classname> to prevent further writing to the session data
497 store. We are specifically requesting feedback on this feature. Potential uses/abuses
498 might include temporarily disabling the use of
499 <classname>Zend_Session_Namespace</classname> instances or
500 <classname>Zend_Session</classname> methods to write to the session data store, while
501 execution is transferred to view- related code. Attempts to perform actions involving
502 writes via these instances or methods will throw an exception.
506 <sect2 id="zend.session.global_session_management.writeclose">
507 <title>writeClose($readonly = true)</title>
510 Shutdown the session, close writing and detach <varname>$_SESSION</varname> from the
511 back-end storage mechanism. This will complete the internal data transformation on this
512 request. The optional <varname>$readonly</varname> boolean parameter can remove write
513 access by throwing an exception upon any attempt to write to the session via
514 <classname>Zend_Session</classname> or <classname>Zend_Session_Namespace</classname>.
518 <title>Throws</title>
521 By default, <varname>$readonly</varname> is enabled and further actions involving
522 writing to the session data store will throw an exception. However, some legacy
523 application might expect <varname>$_SESSION</varname> to remain writable after
524 ending the session via <methodname>session_write_close()</methodname>. Although not
525 considered "best practice", the <varname>$readonly</varname> option is available for
531 <sect2 id="zend.session.global_session_management.expiresessioncookie">
532 <title>expireSessionCookie()</title>
535 This method sends an expired session id cookie, causing the client to delete the session
536 cookie. Sometimes this technique is used to perform a client-side logout.
540 <sect2 id="zend.session.global_session_management.savehandler">
541 <title>setSaveHandler(Zend_Session_SaveHandler_Interface $interface)</title>
544 Most developers will find the default save handler sufficient. This method provides an
545 object-oriented wrapper for <ulink
546 url="http://php.net/session_set_save_handler"><methodname>session_set_save_handler()</methodname></ulink>.
550 <sect2 id="zend.session.global_session_management.namespaceisset">
551 <title>namespaceIsset($namespace)</title>
554 Use this method to determine if a session namespace exists, or if a particular index
555 exists in a particular namespace.
559 <title>Throws</title>
562 An exception will be thrown if <classname>Zend_Session</classname> is not marked as
563 readable (e.g., before <classname>Zend_Session</classname> has been started).
568 <sect2 id="zend.session.global_session_management.namespaceunset">
569 <title>namespaceUnset($namespace)</title>
572 Use <methodname>Zend_Session::namespaceUnset($namespace)</methodname> to efficiently
573 remove an entire namespace and its contents. As with all arrays in
574 <acronym>PHP</acronym>, if a variable containing an array is unset, and the array
575 contains other objects, those objects will remain available, if they were also stored by
576 reference in other array/objects that remain accessible via other variables. So
577 <methodname>namespaceUnset()</methodname> does not perform a "deep" unsetting/deleting
578 of the contents of the entries in the namespace. For a more detailed explanation, please
579 see <ulink url="http://php.net/references">References Explained</ulink> in the
580 <acronym>PHP</acronym> manual.
584 <title>Throws</title>
587 An exception will be thrown if the namespace is not writable (e.g., after
588 <methodname>destroy()</methodname>).
593 <sect2 id="zend.session.global_session_management.namespaceget">
594 <title>namespaceGet($namespace)</title>
597 DEPRECATED: Use <methodname>getIterator()</methodname> in
598 <classname>Zend_Session_Namespace</classname>. This method returns an array of the
599 contents of <varname>$namespace</varname>. If you have logical reasons to keep this
600 method publicly accessible, please provide feedback to the <ulink
601 url="mailto:fw-auth@lists.zend.com">fw-auth@lists.zend.com</ulink> mail list.
602 Actually, all participation on any relevant topic is welcome :)
606 <title>Throws</title>
609 An exception will be thrown if <classname>Zend_Session</classname> is not marked as
610 readable (e.g., before <classname>Zend_Session</classname> has been started).
615 <sect2 id="zend.session.global_session_management.getiterator">
616 <title>getIterator()</title>
619 Use <methodname>getIterator()</methodname> to obtain an array containing the names of
624 <title>Throws</title>
627 An exception will be thrown if <classname>Zend_Session</classname> is not marked as
628 readable (e.g., before <classname>Zend_Session</classname> has been started).