Snipped: Cookies mit mehr als 4096 Zeichen

Ich möchte bei einer via Express gehosteten API Cookies mit mehr als 4096 Zeichen (inklusive Name, Gültigkeit und Parameter) setzen. Das kann funktioniert, ist aber gegen RFC 2109; jedes einzelne Cookie darf nur 4096 Zeichen lang sein.
Mein Workaround: zwei Funktionen, eine mit der ein Cookie „gepaged“ auf andere Cookies verteilt werden kann, eines mit der das dann gelesen wird.

Dieser Code ist von uuid abhängig; also npm install --save uuid.

import { v4 as uuidv4 } from 'uuid';

/**
 * Setzt einen paged Cookie. Dieser nutzt mehrere cookies, die alle auf 2048 Zeichen
 * gesplittet sind. Das umgeht damit das Verbot von Cookies mit mehr als 4096 Zeichen
 * nach RFC 2109 (6.3), RFC 2965 (5.3), und RFC 6265
 * @param {object} resp 
 * @param {string} cookieName 
 * @param {string} cookieValue 
 * @param {number} maxAge in Millisekunden
 * @param {boolean?} httpOnly
 */
const setPagedCookie = (resp, cookieName, cookieValue, maxAge, httpOnly) => {
    if (typeof httpOnly == "undefined")
        httpOnly = true;
    
    // Hier kann übrigens die 2048 durch eine andere Zahl ersetzt werden, falls man
    // den Wert nicht auf 2048 kürzen möchte.
    // Man muss nur bedenken, dass da die Parameter hinzu kommen (siehe weiter unten)
    var tokens = cookieValue.match(/.{1,2048}/g);
    for (var i = 0; i < tokens.length; i++) {
        tokens[i] = {
            id: uuidv4(),
            data: tokens[i],
        };
    }
    for (var i = 0; i < tokens.length; i++) {
        resp.cookie(tokens[i].id, tokens[i].data, { maxAge, httpOnly });
    }
    for (var i = 0; i < tokens.length; i++) {
        tokens[i] = tokens[i].id;
    }
    resp.cookie(cookieName, tokens.join(','), { maxAge, httpOnly });
};


/**
 * Liest einen via setPagedCookie gesetzten Cookie aus
 * @param {object} req
 * @param {string} cookieName 
 * @returns {string|null}
 */
const getPagedCookie = (req, cookieName) => {
    if (typeof req.cookies[cookieName] == "undefined")
        return null;
    var ret = "";
    var cookieNames = req.cookies[cookieName].split(",");
    for (var i = 0; i < cookieNames.length; i++) {
        if (typeof req.cookies[cookieNames[i]] == "undefined")
            return null;
        ret += req.cookies[cookieNames[i]];
    }
    return ret;
};

export {
    setPagedCookie,
    getPagedCookie,
};

Beispielhafte Verwendung:

app.get(`/cookie/set`, (_req, resp) => {
    // Hier übergeben wir als ersten Parameter {resp}
    setPagedCookie(resp, 'jwt_cookie_name', variableWithVeryLongJwt, 5*60*1000);
});

Das setzt dann pro 2048 Zeichen ein Cookie mit einer UUID als Namen, und in das Cookie jwt eine Liste der UUIDs in der korrekten Reihenfolge wie sie gelesen werden sollen.

Um das Cookie dann wieder auszulesen:

app.get(`/cookie/get`, (req, resp) => {
    // Hier übergeben wir als ersten Parameter {req}
    const variableWithVeryLongJwt = getPagedCookie(req, 'jwt_cookie_name');
});

In der Variable variableWithVeryLongJwt ist exakt der Text, der oben via setPagedCookie gesetzt wurde.

Die Größe der einzelnen Cookies kann an einer Stelle direkt angepasst werden (ist oben kommentiert). Auf bereits gesetzte Cookies hat das keinen Einfluss.
Man muss nur bedenken, dass die maximale Länge für alle Parameter 4096 Zeichen nicht überschreitet. Ein typisches Set-Cookie sieht z.B. wie folgt aus:

Set-Cookie: [UUID]=[WERT]; Max-Age=2400; Path=/; Expires=Sat, 27 Jul 2024 13:45:56 GMT; HttpOnly

Hier gehen also schon mal 84 Zeichen nur für die Kopfzeile, das Gleichheitszeichen und alles nach dem ersten Semikolon weg. Die UUID nimmt auch noch mal 36 Zeichen weg. Gefahrenlos sollten also bis zu 3.584 Zeichen an Cookie möglich sein, aber wozu so knapp planen.

Kategorien:Javascript