Question

I've set nginx userid module to produce uid cookies on requests to the server for anonymous tracking, While everything goes as expected with setting the cookies, I can't figure out how these cookies should be parsed (in Python) for further analysis.

According the nginx's docs (http://nginx.org/en/docs/http/ngx_http_userid_module.html#userid_service) the http_userid_module is fully compliant with apache's mod_uid and according to apache's mod_uid docs (http://www.lexa.ru/programs/mod-uid-eng.html) the cookie value actually contains valuable data such as the timestamp the cookie was issued at.

the base64 decoding part is easy :) Was wondering if anyone here can help with the rest of the actions needed in order to parse the data in these cookies?

Was it helpful?

Solution

import base64
import socket
import struct


def decode_cookie(cookie):
    """decode a u cookie into an uid

    :param cookie: a cookie value that will be decoded into a uid
    :return: string representing the uid

    This algorithm is for version 2 of http://wiki.nginx.org/HttpUseridModule.

    This nginx module follows the apache mod_uid module algorithm, which is
    documented here: http://www.lexa.ru/programs/mod-uid-eng.html.

    """
    # get the raw binary value
    binary_cookie = base64.b64decode(cookie)

    # unpack into 4 parts, each a network byte orderd 32 bit unsigned int
    unsigned_ints = struct.unpack('!4I', binary_cookie)

    # convert from network (big-endian) to host byte (probably little-endian) order
    host_byte_order_ints = [socket.ntohl(i) for i in unsigned_ints]

    # convert to upper case hex value
    uid = 'u=' + ''.join(['{0:08X}'.format(h) for h in host_byte_order_ints])

    return uid


def encode_uid(uid):
    """encode an uid into a u cookie

    :param uid: an uid that will be encoded into a cookie.
    :return: string representing the u cookie

    The algorithm is for version 2 of http://wiki.nginx.org/HttpUseridModule.

    This nginx module follows the apache mod_uid module algorithm, which is
    documented here: http://www.lexa.ru/programs/mod-uid-eng.html.

    """
    # get the hex value of the uid
    hex_value = uid.split('=')[1]

    # convert 128 bit string into 4 32 bit integers
    host_byte_order_ints = [int(hex_value[i:i+8], 16) for i in range(0, 32, 8)]

    # convert from host byte (probably little-endian) to network byte (big-endian) order
    unsigned_ints = [socket.htonl(i) for i in host_byte_order_ints]

    # convert to raw binary value
    binary_cookie = struct.pack('!4I', *unsigned_ints)

    # get the base64 version of the cookie
    cookie = base64.b64encode(binary_cookie)

    return cookie

OTHER TIPS

Backport of @d3w4rd's answer in PHP just in case someone is looking for it.

class NginxUniqid
{
    public static function decodeCookie($cookie)
    {
        return strtoupper(join('', array_map(function ($e) {
            return str_pad(
                base_convert($e, 10, 16),
                8, '0', STR_PAD_LEFT
            );
        }, unpack('I4', base64_decode($cookie)))));
    }

    public static function encodeUid($hex)
    {
        return base64_encode(pack(
            'I4',
            base_convert(substr($hex, 0, 8), 16, 10),
            base_convert(substr($hex, 8, 8), 16, 10),
            base_convert(substr($hex, 16, 8), 16, 10),
            base_convert(substr($hex, 24, 8), 16, 10)
        ));
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top