← All posts

Tutorial Security

How to Secure PHP Sessions on a LAMP Production Server

Lock down PHP session handling with secure directives, HTTPS-only cookies, and restricted file permissions.

Difficulty Intermediate Time 25 min
Padlock securing server rack symbolizing PHP session security on a LAMP production environment

Session hijacking and fixation attacks target weak PHP session configurations. In this tutorial, we will harden PHP session handling on a production LAMP server by locking down session storage, enforcing secure cookie flags, and configuring strict session directives. By the end, your sessions will transmit only over HTTPS, regenerate IDs on privilege changes, and store data with restricted file permissions.

Prerequisites

  • A LAMP stack (Linux, Apache, MySQL, PHP 7.4 or later) in production
  • Root or sudo access to the server
  • HTTPS already configured with a valid SSL certificate
  • SSH access and basic familiarity with editing php.ini

Step 1 — Locate and Back Up Your php.ini

First, identify which php.ini file your web server is using. Run:

php -i | grep "Loaded Configuration File"

You will see output like /etc/php/8.1/apache2/php.ini. Copy that path. Create a backup before making changes:

sudo cp /etc/php/8.1/apache2/php.ini /etc/php/8.1/apache2/php.ini.backup

Now open the file in your editor:

sudo nano /etc/php/8.1/apache2/php.ini

Step 2 — Restrict Session File Permissions and Path

By default, PHP writes session files to a shared temporary directory. Lock this down. Search for the session.save_path directive in php.ini and set a dedicated directory:

session.save_path = "/var/lib/php/sessions"

Ensure that directory exists and is owned by the web server user (commonly www-data on Debian and Ubuntu):

sudo mkdir -p /var/lib/php/sessions sudo chown www-data:www-data /var/lib/php/sessions sudo chmod 700 /var/lib/php/sessions

The 700 permission means only the web server can read or write session files. No other user can peek at session data.

Step 3 — Enforce HTTPS-Only and HttpOnly Cookies

Session cookies must never transmit over plain HTTP, and JavaScript must not access them. In php.ini, find or add these directives:

session.cookie_secure = 1 session.cookie_httponly = 1 session.cookie_samesite = "Strict"

The session.cookie_secure directive forces the cookie to be sent only over HTTPS. The session.cookie_httponly directive blocks client-side scripts from reading the session cookie, mitigating XSS-based session theft. The session.cookie_samesite directive prevents the browser from sending the cookie in cross-site requests, defending against CSRF attacks.

If your application legitimately requires cross-site requests, you may set Lax instead of Strict, but Strict is the safer default.

Step 4 — Use Strict Session ID Generation and Hashing

Modern PHP defaults to strong entropy, but explicitly verify and lock down the hash and bits:

session.sid_length = 48 session.sid_bits_per_character = 6 session.hash_function = sha256

These settings generate a 48-character session ID with high entropy. If your PHP version is 7.1 or later, session.hash_function is deprecated in favor of the internal CSPRNG, so you can omit or comment it out. PHP 7.1 and later uses a cryptographically secure random number generator by default.

Step 5 — Disable Transparent Session IDs and Use Cookies Only

Prevent session IDs from appearing in URLs, which is a major security risk. Set these directives:

session.use_only_cookies = 1 session.use_trans_sid = 0

The session.use_only_cookies directive ensures session IDs are only stored in cookies, never in query strings. The session.use_trans_sid directive disables transparent session ID propagation via URL rewriting.

Step 6 — Set a Realistic Session Lifetime

Balance security and user experience by limiting how long sessions persist. In php.ini, configure these values:

session.gc_maxlifetime = 1800 session.cookie_lifetime = 0

The session.gc_maxlifetime directive marks sessions older than 30 minutes for garbage collection. The session.cookie_lifetime directive makes the cookie a session cookie that expires when the browser closes. For applications requiring remember-me functionality, implement a separate secure token mechanism rather than extending the session cookie lifetime.

Step 7 — Regenerate Session IDs on Privilege Changes

Session fixation attacks rely on reusing the same session ID before and after login. In your application code, call session_regenerate_id with the parameter true immediately after successful authentication. This deletes the old session file and generates a new ID, preventing fixation. Regenerate again on logout and whenever privilege escalates, such as switching to admin mode.

Step 8 — Restart Apache and Test

Save and close php.ini, then restart Apache to apply the new session configuration:

sudo systemctl restart apache2

Verify the settings took effect by checking your application and inspecting the session cookie in your browser developer tools. Open the Network or Application tab and look for the session cookie. It should have the Secure, HttpOnly, and SameSite flags set. You can also create a temporary phpinfo script to verify the directives, but delete it immediately after checking to avoid exposing server information.

Wrap-up

You have hardened PHP session handling by isolating session files with strict permissions, enforcing HTTPS-only and HttpOnly cookies, disabling URL-based session IDs, and implementing session regeneration on authentication. These controls close the most common session attack vectors on LAMP stacks. Regularly audit your session configuration and keep PHP updated to inherit the latest security defaults. For applications handling sensitive data, consider adding IP binding or user-agent checks in your session validation logic as an additional layer.