VAITP Dataset

← Back to the dataset

CVE-2026-27205

Flask fails to set the Vary: Cookie header, risking sensitive info cache.

  • CVSS 2.3
  • CWE-524
  • Information Leakage
  • Remote

Flask is a web server gateway interface (WSGI) web application framework. In versions 3.1.2 and below, when the session object is accessed, Flask should set the Vary: Cookie header., resulting in a Use of Cache Containing Sensitive Information vulnerability. The logic instructs caches not to cache the response, as it may contain information specific to a logged in user. This is handled in most cases, but some forms of access such as the Python in operator were overlooked. The severity and risk depend on the application being hosted behind a caching proxy that doesn't ignore responses with cookies, not setting a Cache-Control header to mark pages as private or non-cacheable, and accessing the session in a way that only touches keys without reading values or mutating the session. The issue has been fixed in version 3.1.3.

CVSS base score
2.3
Published
2026-02-21
OWASP
A05 Security Misconfiguration
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Information Leakage
Subcategory
Information Disclosure
Accessibility scope
Remote
Impact
Information Disclosure
Affected component
Flask
Fixed by upgrading
Yes

Solution

Upgrade Flask to version 3.1.3 or later.

Vulnerable code sample

from flask import Flask, session, request

# This code is intended to be run with a Flask version <= 3.0.2
# It must be placed behind a misconfigured caching proxy to demonstrate the vulnerability.

app = Flask(__name__)
app.secret_key = 'a-demonstration-secret-key'

@app.route('/')
def index():
    """Provides a simple way to log in and out to manage the session."""
    if 'login' in request.args:
        session['user'] = 'alice'
        return 'Logged in as "alice". <a href="/profile">View your profile.</a>'
    
    if 'logout' in request.args:
        session.clear()
        return 'Logged out. <a href="/profile">View profile as a guest.</a>'

    return 'Welcome. <a href="/?login=true">Login as "alice"</a>'

@app.route('/profile')
def profile():
    """
    This is the vulnerable endpoint. It only checks for the presence of a
    session key using the 'in' operator without reading a value or modifying it.
    """
    page_content = "<h1>Public Profile</h1><p>No sensitive information here.</p>"

    # This 'in' operator check was the source of the vulnerability.
    # In affected versions, it did not flag the session as being accessed,
    # thus failing to add the 'Vary: Cookie' header to the response.
    if 'user' in session:
        # A caching proxy that receives this response from a logged-in user
        # might cache it and serve it to subsequent unauthenticated users.
        page_content = "<h1>Private Profile for Alice</h1><p>This is sensitive information that should not be cached.</p>"

    return page_content

if __name__ == '__main__':
    # Running this with the built-in server will not show the caching issue.
    # A separate caching proxy (like Varnish or a specific Nginx config) is required.
    app.run(port=5000)

Patched code sample

import typing as t


class SessionMixin(t.MutableMapping[str, t.Any]):
    """Expands a basic dictionary with session attributes."""

    def __init__(self, initial: t.Optional[t.Dict[str, t.Any]] = None) -> None:
        if initial:
            self.update(initial)

    def _get_interface(self) -> "SessionInterface":
        raise NotImplementedError

    #: Some implementations can detect changes to the session and set
    #: this when that happens. The default :class:`Session` will set
    #: this to ``True`` if ``__setitem__`` or ``__delitem__`` is
    #: called.
    modified = False

    #: The session was accessed at all. This is used by the default
    #: implementation to add a ``Vary: Cookie`` header.
    accessed = False

    def __getitem__(self, key: str) -> t.Any:
        self.accessed = True
        return self._get_interface().get_data(self)[key]

    def __setitem__(self, key: str, value: t.Any) -> None:
        self._get_interface().get_data(self)[key] = value
        self.modified = True

    def __delitem__(self, key: str) -> None:
        del self._get_interface().get_data(self)[key]
        self.modified = True

    # The fix for CVE-2024-27205 involved adding the `self.accessed = True`
    # line to the __contains__, __iter__, and __len__ methods. Previously,
    # these operations did not mark the session as accessed, preventing the
    # Vary: Cookie header from being set.

    def __contains__(self, item: str) -> bool:
        """Check if a key is in the session."""
        self.accessed = True
        return item in self._get_interface().get_data(self)

    def __iter__(self) -> t.Iterator[str]:
        self.accessed = True
        return iter(self._get_interface().get_data(self))

    def __len__(self) -> int:
        self.accessed = True
        return len(self._get_interface().get_data(self))

    def __repr__(self) -> str:
        return f"<{type(self).__name__} {self._get_interface().get_data(self)!r}>"

Cite this entry

@misc{vaitp:cve202627205,
  title        = {{Flask fails to set the Vary: Cookie header, risking sensitive info cache.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-27205},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-27205/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::