[php] PHP accepteerd gemanipuleerde Sessie-IDs via cookie

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • DPLuS
  • Registratie: April 2000
  • Niet online
Momenteel maak ik gebruik van het Zend Framework inclusief een custom session_save_handler() zodat mijn sessies netjes in een database worden opgeslagen.

Alleen zit ik nu met de volgende vraag:
Op het moment dat er een Session ID gezet wordt door PHP, wordt er aan de kant van de browser een cookie aangemaakt met het Sessie ID.
Stel dat ik dat sessie ID verander, dan accepteert PHP dit ID als zijnde het nieuwe Sessie ID tijdens een session_start().

Hoe kan ik er nu voor zorgen dat PHP alleen maar sessie ID's accepteert die serverside gegenereerd zijn (a la Microsoft IIS)?

Het probleem zat hem namelijk in de write-functie van mijn session_save_handler.
Hier schrijf ik het sessie_id tezamen met de sessie-data weg in de sessie-tabel van de database.
In die tabel heb ik een veld: sessie_id gedefinieerd van VARCHAR(32).
Dat komt dus overeen met de standaard lengte van een PHP5 server side gegeneerde session_id().

Maar als ik het cookie nu manipuleer, en ik maak van die sessie-id gewoon een heel lange string, dan krijg ik in mijn output een vage foutmelding te zien, naar aanleiding van de write actie in mijn session_save_handler class.

Dus ik wil eigenlijk weten wat hier nu best practise is.
Moet ik bijvoorbeeld tijdens de write-actie checken of de session_id wel 32 karakters lang is in onderstaande functie?
Of is er nog een andere betere manier?

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// deze write functie komt uit mijn custom save_handler klasse:

    /**
     * Write Session - commit data to resource
     *
     * @param string $id The session ID
     * @param mixed $data The session data
     * @return bool
     */
    public function write($id, $data)
    {
        // Hier dan checken of $id wel 32 karakters is?????

        $dbSelect = $this->_zendDb->select();
        $dbSelect->from($this->_tableName, 'COUNT(session_id)');
        $dbSelect->where($this->_zendDb->quoteIdentifier('session_id') . ' = ?', $id);

        $result = (int) $this->_zendDb->fetchOne($dbSelect);

        if ($result) {
            $tblData = array(
                'last_update' => new Zend_Db_Expr('NOW()'),
                'expiry'      =>
                    new Zend_Db_Expr($this->_zendDb->quoteInto('DATE_ADD(NOW(), INTERVAL ? SECOND)', $this->_lifeTime)),
                'hits'        =>
                    new Zend_Db_Expr($this->_zendDb->quoteIdentifier('hits') . ' + 1'),
                'value'       => $data
            );
            $where = $dbSelect->getPart(Zend_Db_Select::WHERE);
            $affectedRows = $this->_zendDb->update($this->_tableName, $tblData, $where);
        } else {
            $tblData = array(
                'session_id'  => $id,
                'created'     => new Zend_Db_Expr('NOW()'),
                'last_update' => new Zend_Db_Expr('NOW()'),
                'expiry'      =>
                    new Zend_Db_Expr($this->_zendDb->quoteInto('DATE_ADD(NOW(), INTERVAL ? SECOND)', $this->_lifeTime)),
                'value'       => $data
            );
            $affectedRows = $this->_zendDb->insert($this->_tableName, $tblData);
        }

        return $affectedRows;
    }


B.t.w., dit probleem geldt waarschijnlijk gewoon voor de manier waarop PHP met sessies omgaat,
in dit (http://www.acros.si/papers/session_fixation.pdf) document noemen ze het namelijk een "permissive session management":
We can classify session management mechanisms on web servers in two classes:
a) “Permissive”: those that accept arbitrary session IDs, and create a new session
with proposed session ID if one doesn’t exist yet (e.g., Macromedia JRun
server, PHP).
b) “Strict”: those that only accept known session IDs, which have been locally
generated at some point in the past (e.g., Microsoft Internet Information
Server).

[ Voor 10% gewijzigd door DPLuS op 18-06-2007 18:36 ]


Acties:
  • 0 Henk 'm!

  • assembler
  • Registratie: Mei 2004
  • Niet online
Je zegt in je post dat je sessions in een database worden bijgehouden, dan kan je toch gewoon controleren of de session-id daar wel in voorkomt?
Zo niet, dan is het dus een fake session-id

Acties:
  • 0 Henk 'm!

  • DPLuS
  • Registratie: April 2000
  • Niet online
Het gaat erom dat als men bijvoorbeeld de eerste keer al connect (en een sessie initieert) al een gemanipuleerde sessie-id kan aanleveren.

Probeer het maar eens met Firefox en de web-developer extensie, dan kun je vantevoren gewoon een cookie aanmaken met de waardes:

- Name: PHPSESSID
- Value: Een bogus SessieID
- Host: jouw-development-omgeving.lan
- Vinkje session cookie

Als je dan je sessie start (Zend_Session::start()), dan pikt 'ie de sessie op aan de hand van het sessie-id dat uit $_COOKIE komt, in dit geval: "Een bogus SessieID".

De vraag is dus, waar kan ik dit het beste afvangen?
In de write-actie van mijn Session_Save_Handler klasse, of vantevoren de waarde van $_COOKIE['PHPSESSID'] controleren (string lengte en regex [0-9] en [a-f] ??

Wat ik eigenlijk niet wil is dat PHP zomaar een sessie-id in mijn database gooit die een gebruiker gewoon willekeurig bedacht heeft en in een cookie gemanipuleerd heeft.

Maar aangezien dit niet 100% te verbannen is, zal ik toch die check moeten inbouwen.

Acties:
  • 0 Henk 'm!

Verwijderd

DPLuS schreef op maandag 18 juni 2007 @ 20:14:

Maar aangezien dit niet 100% te verbannen is, zal ik toch die check moeten inbouwen.
Dat is uit te sluiten door die check in te bouwen. En dat moet je doen vóórdat je de sessie start. Zeker als je je eigen sessionhandler schrijft is dit echt een fluitje van een cent.

Acties:
  • 0 Henk 'm!

  • DPLuS
  • Registratie: April 2000
  • Niet online
Grappig dat je dat zegt, ik vermoedde al zoiets, vraag is alleen wat is hier nu de best practice om dit te implementeren?

Jij zegt voor de session_start, ik zou een voorstander zijn om dat tijdens de write af te vangen...

Ik heb een aantal session handlers bekeken (zoals db_esession: http://freshmeat.net/projects/db_esession/) maar daar komt zo'n initiële check dus ook niet in voor.
M.a.w., als je dat niet controleert kun je allerlei bogus session ID's in je database verwachten.

Just my 2 cents

Acties:
  • 0 Henk 'm!

  • Paul C
  • Registratie: Juni 2002
  • Laatst online: 14:02
Kun je niet gewoon een session_destroy(); doen voordat je de session_start() aanroept?

Je moet namelijk een jou eigen session_key enforcen, want deze moet namelijk niet alleen aan jou regex voldoen, maar ook uniek en (pseudo)random zijn.

Een check lijkt me namelijk niet genoeg. Het is niet zo moeilijk om een valide session_id te verzinnen en als je die kunt injecteren voor een sessie, dan heb je in principe een lek. A kan namelijk bij B een valide bekende session_key injecteren, waarna B hiermee een sessie opbouwt bij jou site, omdat de key gewoon valid is. Nu is de session_key echter wel bekend bij A waardoor deze dus zou kunnen inbreken op de sessie van B.
Pagina: 1