The session module cannot guarantee that the information you store in a session is only viewed by the user who created the session. You need to take additional measures to protect the confidentiality of the session, depending on the value associated with it.
Assess the importance of the data carried by your sessions and deploy additional protections -- this usually comes at a price, reduced convenience for the user. For example, if you want to protect users from simple social engineering tactics, you need to enable session.use_only_cookies. In that case, cookies must be enabled unconditionally on the user side, or sessions will not work.
Note: Even HTTPS cannot protect confidential data in contents sometimes. e.g. CRIME, Beast attack. There are many networks that use HTTPS MITM proxy for audit purpose. Attackers may setup such proxy also.
PHP's session manager is adaptive by default currently. Adaptive session manger has additional risks.
Since PHP 5.5.2, session.use_strict_mode is available. When it is enabled and session save handler supports it, uninitialized session ID is rejected and new session ID is created. This protects attack that forces users to use known session ID. Attacker may paste links or send mail that contains session ID. e.g. http://example.com/page.php?PHPSESSID=123456789 If session.use_trans_sid is enabled, victim will start session using attacker provided session ID. session.use_strict_mode mitigates the risk.
User defined save handler can also support strict session mode by implementing session ID validation function/method. All user defined save handlers must implement session ID validation function/method.
Session ID cookie could be set with domain, path, httponly, secure attributes. There is precedence defined by browsers. By using the precedence, attacker can set session ID that could be used permanently. Use of session.use_only_cookies will not solve this issue. session.use_strict_mode mitigates this risk. With session.use_strict_mode=On, uninitialized session ID will not be accepted. Session module creates new session ID always when session ID is not initialized by session module.
Access to obsolete session data does not mean attack always. Unstable network and/or immediate active session deletion will cause legitimate users to use obsolete sessions.
Since PHP 7.1.0, session_create_id() is added. This function could be used to prefix session ID by user ID to access active sessions for a user efficiently. Enabling session.use_strict_mode is very important with this setup. Otherwise, malicious users can set malicious session ID for other users.
Note: Users prior to PHP 7.1.0 should use CSPRNG, e.g. /dev/urandom, or random_bytes() and hash functions to generate new session ID. session_create_id() has collision detection and generates session ID according to session INI settings. Use of session_create_id() is preferred.
Session ID regeneration reduces risk of stolen session ID, thus session_regenerate_id() must be called periodically. e.g. Regenerate session ID for every 15 minutes for security sensitive content. Even when session ID is stolen, either legitimate user or attacker session will be expired. i.e. User or attacker access will generate obsolete session access error.
Session ID must be regenerated when user is authenticated. session_regenerate_id() must be called prior to set authentication information to $_SESSION. (Since PHP 7.0.0, session_regenerate_id() saves current session data automatically in order to save time-stamp/etc to current session.) Make sure only new session contains authenticated flag.
Developer must NOT rely on session ID expiration by session.gc_maxlifetime. Attackers may access victim's session ID periodically to prevent expiration and keep exploiting victim's session ID including authenticated sessions.
Instead, you must implement time-stamp based session data management by yourself.
Although session manager can manage time-stamp transparently, but this feature is not implemented. Old session data must be kept until GC. At the same time, developers must make sure obsolete session data is removed. However, developers must NOT remove active session data immediately. i.e. Never call session_regenerate_id(true); and session_destroy() for active session. This may sound contradictory, but this is the mandatory requirement.
session_regenerate_id() does not delete old session by default. Old authenticated session may be available for use. Developers must prevent old session to be used by anyone, must prohibit access to obsolete session data by themselves using time-stamp.
Immediate active session removal has unwanted side effects. Session could be vanished when there are concurrent connections to web application and/or network is unstable.
Possibly malicious access cannot be detected with immediate active session removal also.
Instead of deleting old session immediately, you must set short term expiration time (time-stamp) in $_SESSION, and prohibit access to the session data by yourself.
You must not prohibit access to old session data immediately after session_regenerate_id(). It must be prohibited a little later. e.g. A few seconds later for stable wired network. A few minutes later for unstable network such as mobile or WiFi.
If user accesses to obsolete session(expired session), deny access to it. It is recommended to remove authenticated status from all of the users' session because it is likely an attack.
Proper use of session.use_only_cookies and session_regenerate_id() could cause personal DoS by undeletable cookies set by attackers. When this is the case, you may ask users to remove cookies and warn users that there could be possible security issues. Attackers may set malicious cookies via vulnerable web application, vulnerable/malicious browser plugins, physically compromised device, etc.
Do not misunderstand the DoS risk. use_strict_mode=On is mandatory for general session ID security! All sites are advised to enable use_strict_mode.
Obsolete session data must be inaccessible and deleted. Current session module does not handle this well.
Obsolete session data is better to be removed as soon as possible. However, active sessions MUST NOT be removed immediately. To satisfy these requirements, you MUST implement time-stamp based session data management by yourself.
Set and manage expiration time-stamp in $_SESSION. Prohibit access to obsolete session data. When obsolete session data access is detected, it is advised to remove all authenticated status from the user's sessions and force them to re-authenticated. Obsolete session data access could be an attack. To do this, you must keep track active sessions per user.
Note: Access to obsolete session could happen by unstable network and/or concurrent access to web site also. Server tried to set new session ID via cookie, but Set-Cookie packet may not be reached to client due to lost connection. One connection may issue new session ID by session_regenerate_id(), but another conncurrent connection may not get the new session ID yet. Therefore, you must prohibit access to obsolete session a while later. i.e. Time-stamp based session management is mandatory.
In short, do not destroy session data by session_regenerate_id() nor session_destroy(), but use time-stamp to control access to session data. Let session_gc() to remove obsolete data from session data storage.
Session data is locked to avoid races by default. Locking is mandatory to keep session data consistent across requests.
However, locking can be abused by attacker to perform DoS attacks. To mitigate risks of DoS by session lock, minimize lock. Use read only sessions when session data update is not required. Use 'read_and_close' option with session_start(). session_start(['read_and_close'=>1]); Close session as soon as you finish updating $_SESSION by using session_commit().
Current session module does not detect $_SESSION modification while session is inactive. It is your responsibility not to modify $_SESSION when session is inactive.
Developers should keep track active sessions per user and notify the user how many active sessions, from which IP (and area), how long is active, etc. PHP does not keep track these. You are supposed to do so.
There are number of ways for the implementation. You may setup a database that keeps track required data and store information to it. Since session data is GCed, you have to take care of GCed data to maintain the active session database consistency.
One of the simplest implementation is "User ID prefixed session ID" and store required information to $_SESSION. Many databases have good performance for selecting string prefix. You can use session_regenerate_id() and session_create_id() for this.
Never use confidential data as prefix. If user ID is confidential, consider to use hash_hmac().
Enabling session.use_strict_mode is mandatory for this setup. Make sure it is enabled, otherwise active session database can be compromised.
Time-stamp based session management is mandatory to detect obsolete session access. When access to obsolete session is detected, you should remove authentication flags from all of active sessions for the user. This prevents attackers to keep exploiting stolen session.
Developers must NOT use long life session ID for auto login because it increases risk of stolen session. Auto login should be implemented by developer.
If user is not authenticated, check the one time auto login key is valid or not. If key is valid, authenticate user and set new secure one time hash key. Auto login key must be able to be used only once. i.e. Never reuse auto login key, generate new auto login key always.
Auto login key is long life authentication key, this key should be protected as much as possible. Use path/httponly/secure cookie attributes to protect. i.e. Never transmit auto login key unless it is required.
Developer must implement feature that disables auto login and removes unneeded auto login key cookie.
Session and authentication does not protect against CSRF attack. Developers must implement CSRF protection by themselves.
output_add_rewrite_var() can be used for CSRF protection. Refer to the manual page for details.
Note: PHP prior to 7.2.0 uses the same output buffer and INI setting as trans sid. Therefore, use of output_add_rewrite_var() with PHP prior to 7.2.0 is not recommended.
Most web application frameworks support CSRF protection. Refer to your web application framwork manual for detials.