Fix unpacking of the sessionId
authorTim Düsterhus <duesterhus@woltlab.com>
Wed, 18 Aug 2021 07:44:07 +0000 (09:44 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Wed, 18 Aug 2021 07:55:20 +0000 (09:55 +0200)
As documented by PHP's reference documentation:

> The "a" code now retains trailing NULL bytes.
> The "A" code now strips all trailing ASCII whitespace (spaces, tabs,
> newlines, carriage returns, and NULL bytes).

Previously, with the 'A' code, sessionIds ending in ASCII whitespace would be
incorrectly unpacked, missing their trailing bytes. This ultimately resulted in
the session not being found and the user being logged out.

Five of the 256 possible characters exhibited this bug, making this fail in
roughly 2% of the cases.

However this likely was not noticable by the typical user. Once they have a
non-affected sessionId, this Id is not going to change. What the user might've
noticed is a login not working, despite showing a success message, because they
sessionId change after a successful login handed out an affected sessionId. But
then the user would likely try again, succeeding this time and writing off the
incident as a fluke.

Test script to reproduce the issue:

    <?php

    for ($i = 0; $i <= 255; $i++) {
        $string = "foo".chr($i);

        $packed = \pack(
            'CA4',
            1,
            $string
        );
        $unpacked1 = \unpack('Cversion/A4string', $packed);
        $unpacked2 = \unpack('Cversion/a4string', $packed);

        if ($unpacked1['string'] !== $string) {
            echo "$i: unpacked1\n";
        }
        if ($unpacked2['string'] !== $string) {
            echo "$i: unpacked2\n";
        }
    }

wcfsetup/install/files/lib/system/session/SessionHandler.class.php

index 28d8c818f4dae482118c03f550d6f7825332b76e..0cdeb4e337069d5ff2b9ea606b5e5e8ed0f985d1 100644 (file)
@@ -241,7 +241,7 @@ final class SessionHandler extends SingletonFactory
                     $length
                 ));
             }
-            $data = \unpack('Cversion/A20sessionId/Ctimestep', $value);
+            $data = \unpack('Cversion/a20sessionId/Ctimestep', $value);
             $data['sessionId'] = Hex::encode($data['sessionId']);
 
             return $data;