Signing Cookies with Vinyl Cache

The Problem: A simple example

Many web applications accept externally set cookies, enabling all kinds of manipulation, from changing the behavior of a website to security exploits:

A simple example could be a lang cookie, set to either en or de to render html in either English or German: Understanding the (simple) structure of the cookie, the user interface language can be changed at will, maybe even to unsupported languages which the backend does not implement correctly. If the value of the lang cookie is added to the cache key in Vinyl Cache, permutating its value can be used to force cache misses.

In this simple example, the solution to check allowed values of lang in VCL is trivial, but one can easily image cookies where VCL does not or can not know allowed values.

A slightly more nuanced example is an ordinary session cookie, which is usually just a hex or base64 encoded nonce: VCL supporting backends offering some kind of user login will normally switch to pass mode for most URLs if such a cookie is present. In this case, even if an adversary does not have a valid session, they can easily force a pass and potentially brute force session IDs, if they are not sufficiently long and random.

More elaborate scenarios are left to the reader as a research exercise.

The Solution: Signing Cookies

So what if we could make sure that only the web application can set valid cookies?

We can by signing them and validating the signature.

We provide here a VCL include which implements the following:

  • For all Set-Cookie response headers, an expiry timestamp and a signature are added to the cookie value. The expiry timestamp is derived from the max-age attribute. For session cookies (which do not have the attribute) a customizable default of 30 days is used.

  • All received cookies are validated by

    • checking the expiry timestamp

    • verifying that the signature is correct

  • Only valid cookies are accepted and have the added information (timestamp, signature) stripped off. This way, the application can remain unmodified, because it sees exactly the cookies it set.

To allow signing key rotations, the VCL include supports a transition key via the cs_signer_transition object.

Summary

With this technique, the application only receives cookies it has set itself, which minimizes the attack surface on cookies.

See it in action

This technique is used on https://code.vinyl-cache.org:

$ curl -sI https://code.vinyl-cache.org/user/login?redirect_to=%2f | grep -i set-cookie
set-cookie: gitea_incredible=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax
set-cookie: redirect_to=%2F?1@1783182062?1@az_OrG5w2y-L6X72-MAj4Nvw5XS3wgeTKcUtXN8_tuM; Path=/; HttpOnly; Secure; SameSite=Lax

Here, the redirect_to cookie has ?1@<timestamp>?1@<signature> added.

How to use

The VCL include requires the following custom VMODs:

Also, the blob and std bundled VMODs are used.

All mentioned VMODs need to be installed. re2 and objvar need to be recent as of 2026-02-15 to get the taskvar.strands and re2.hdr_iter classes.

The VCL include vcl/cookiesign.inc.vcl needs to be installed in a directory found via vcl_path.

In the main VCL, the following needs to be added:

  • include "cookiesign.inc.vcl";

  • call cs_recv; must be added early in vcl_recv

  • call cs_backend_response; must be added in vcl_backend_response for the case that Set-Cookie has not been removed (usually for uncacheable responses). When in doubt, just add call cs_backend_response; at the top.

References

This work is used as an example in the presentation WAF: Wrong Approach Firewall at GPN24.

Acknowledgements

Development of the VCL code and this documentation has been funded through an in investment of the Sovereign Tech Agency.