Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this index string clear the $_SESSION array? [duplicate]

Tags:

php

Some weird behavior I've found - I'm intrigued - can anyone shed any light on this..?

Overview

Spent some time this morning finding the cause of a bug which was resulting in the $_SESSION array to empty. Eventually got to the bottom of it and found that when defining an index for one of the $_SESSION variables, an & was being used for concatination instead of a .. It only broke when two specific strings were &ed, other strings resulted in nonsense keys but $_SESSION didn't empty.

This is running on PHP5.5.9-1ubuntu4.20 and locally on PHP5.6.15.

Try it yourself!

Using the example code below,

  1. Run set-session.php
  2. Run check-session.php - all good
  3. Run break-session.php - still good so far...
  4. Run check-session.php again - $_SESSION is now empty!

Example code

set-session.php

session_start();

$_SESSION = [
   'colour' => 'blue',
   'shape'  => 'round',
   'size'   => 'medium'
];

check-session.php

session_start();

echo '<pre>';
print_r($_SESSION);

break-session.php

session_start();

$killer_string = 'Admin_CH_1_' & '101_';
$_SESSION[$killer_string] = null;

echo '<pre>';
print_r($_SESSION);

My guess

I'm guessing that the result of the bitwise operation results in a string, in this case $killer_string, that causes PHP to get angry when it tries to store the $_SESSION array on the server. Strange that $killer_string is no longer a killer when used as a key for a sub array in $_SESSION though.

Ideas?

I know that the code doesn't actually make sense so there's no fault with PHP. However, I am curious as to what is actually going on behind the scenes and what's causing this...

Cheers!

like image 712
Steve Avatar asked Oct 05 '16 18:10

Steve


1 Answers

Here's a simplified test case (see it in action):

<?php
header('Content-Type: text/plain');
ob_start();

session_start();
$_SESSION = [
    'colour'               => 'blue',
    'shape'                => 'round',
    'size'                 => 'medium',
    //'Admin_CH_1_' & '101_' => 'Gone',
    chr(0x01) . chr(0x20) . chr(0x21) . chr(0x49) => 'Gone',
];
var_dump($_SESSION);
session_write_close();

session_start();
var_dump($_SESSION);
ob_end_flush();

If you inspect the session file you can see it has zero bytes.

My best guess so far (until someone smarter shares a link to the PHP github repo with the specific internals) is that you are inadvertently pushing the limits of the session serialization code. Such code assumes that keys are non-binary strings. There's certain validation (pure numeric keys trigger a Skipping numeric key notice) but it doesn't cover all possible malformed inputs. At some point it just crashes.

This gets supported by the fact that changing the serialisation method fixes the issue:

ini_set('session.serialize_handler', 'php_serialize');

In the session.serialize_handler documentation we can read:

Older serialize handlers cannot store numeric index nor string index contains special characters (| and !) in $_SESSION. Use php_serialize to avoid numeric index or special character errors at script shutdown. Defaults to php.

like image 135
Álvaro González Avatar answered Oct 22 '22 15:10

Álvaro González