This report combines identified vulnerabilities from multiple sources into a single list, removing duplicates and providing detailed descriptions, impacts, mitigations, and test cases for each vulnerability.
Description:
The Channels framework offers an OriginValidator
for WebSocket connections to prevent Cross-Site WebSocket Hijacking (CSWSH) attacks. However, this validator is not enabled by default. If developers fail to explicitly implement origin validation middleware for their WebSocket consumers, connections from any origin, including malicious ones, will be accepted. An attacker can craft a WebSocket connection request from a malicious domain, setting an arbitrary Origin
header. Without default validation, the application will accept this connection, potentially exposing sensitive data or allowing unauthorized actions through the WebSocket.
Impact:
- Establishment of WebSocket connections from hostile domains.
- Facilitation of Cross-Site WebSocket Hijacking (CSWSH) attacks.
- Potential for session impersonation and unauthorized actions.
- Exposure of sensitive data transmitted via WebSocket if session cookies are used for authentication.
Vulnerability Rank: High
Currently Implemented Mitigations:
- The Channels project provides
OriginValidator
andAllowedHostsOriginValidator
inchannels/security/websocket.py
. - Tests demonstrate that these validators function correctly when applied, rejecting connections from disallowed origins.
Missing Mitigations:
- Origin validation is not enforced by default for WebSocket consumers. Developers must manually apply the
OriginValidator
middleware. - There is a lack of clear documentation or secure defaults that would automatically enable origin validation, increasing the risk of developers overlooking this crucial security measure.
Preconditions:
- The Channels application exposes one or more WebSocket endpoints.
- The deployed application does not utilize origin validation middleware like
OriginValidator
orAllowedHostsOriginValidator
. - Allowed origins are either not configured or are misconfigured to permit untrusted origins.
Source Code Analysis:
- Located in
channels/security/websocket.py
, theOriginValidator.__call__
method is designed to validate theOrigin
header of incoming WebSocket connections. - The method parses the
Origin
header and uses helper methodsvalid_origin
andvalidate_origin
to check against a list of allowed origins. - However, Channels does not enforce the use of this validator by default.
- If a developer does not explicitly wrap their WebSocket consumer with
OriginValidator
, the connection request bypasses origin checks and is passed directly to the application. - The absence of default origin validation means that applications are vulnerable unless developers proactively implement this security measure.
Security Test Case:
- Deploy a Channels application with a WebSocket consumer, ensuring that
OriginValidator
middleware is not applied. - Use a WebSocket testing tool (e.g.,
WebsocketCommunicator
orwebsocat
) to initiate a connection to the WebSocket endpoint. - In the connection request, include an
Origin
header with a malicious domain, such ashttp://malicious.com
. - Verify that the WebSocket connection is successfully established, demonstrating the missing origin validation.
- Next, modify the application to wrap the WebSocket consumer with
AllowedHostsOriginValidator
, configured to allow only trusted domains (e.g.,http://trusted.com
). - Repeat the connection attempt with the same malicious
Origin
header (http://malicious.com
). - Confirm that the connection is now rejected, demonstrating the effectiveness of the
OriginValidator
when correctly implemented.
Description:
The session middleware in Channels, specifically within channels/sessions.py
, uses CookieMiddleware.set_cookie
to set session cookies in HTTP responses. By default, critical cookie parameters like secure
and samesite
are not set to secure values. For instance, secure
defaults to False
, and samesite
defaults to "lax"
. In production environments using HTTPS, if developers do not override these defaults by setting Django settings like SESSION_COOKIE_SECURE = True
, session cookies might be transmitted over unencrypted HTTP or lack strong "SameSite" restrictions. This can enable attackers with network access to intercept session cookies and hijack user sessions.
Impact:
- Session hijacking due to interception of session cookies transmitted without the
Secure
flag over non-TLS channels. - Increased susceptibility to Cross-Site Request Forgery (CSRF) and related attacks if cookie attributes are not restrictively configured.
- Unauthorized access to user accounts and sensitive application functionalities through the reuse of hijacked session cookies.
Vulnerability Rank: High
Currently Implemented Mitigations:
- The
CookieMiddleware.set_cookie
method inchannels/sessions.py
allows setting cookie parameters such assecure
,httponly
, andsamesite
. - Developers can configure these parameters using Django settings like
SESSION_COOKIE_SECURE
,SESSION_COOKIE_SAMESITE
, etc.
Missing Mitigations:
- Channels does not enforce secure cookie attributes (like
secure=True
) by default in production environments. - Relying solely on developer configuration creates a risk if defaults are not explicitly overridden, leading to insecure cookie settings in deployed applications.
- There are no built-in warnings or automatic fallbacks to alert developers about insecure cookie settings in publicly accessible instances.
Preconditions:
- The Channels application is deployed with default Django session settings, which often have
SESSION_COOKIE_SECURE = False
in development. - The application is running in an environment exposed to public traffic, potentially including non-TLS channels or with weak cookie security configurations.
- An attacker is positioned to observe or intercept HTTP traffic, such as through a man-in-the-middle attack on an open Wi-Fi network.
Source Code Analysis:
- In
channels/sessions.py
, theCookieMiddleware.set_cookie
method constructs cookies using Python'sSimpleCookie
routines. - Default settings within this method include
secure=False
(unless overridden) andsamesite="lax"
. - The code does not automatically enforce stricter security defaults like
Secure
andHttpOnly
in production, leaving it to developers to configure these through Django settings. - This "opt-in" approach to secure cookie settings can result in insecure cookie transmission if developers fail to modify the default settings before deploying a Channels-based application to production.
Security Test Case:
- Deploy a Channels application using session middleware with default settings, without modifying Django’s default
SESSION_COOKIE_SECURE
and related settings. - Initiate an HTTP request (using
curl
or a browser) that results in a session cookie being set in the response. - Capture the response headers and examine the
Set-Cookie
header. Verify that theSecure
flag is missing andSameSite
is set to"lax"
. - In a production-like deployment or by simulating an HTTP environment, use a network proxy (e.g., Wireshark or mitmproxy) to confirm that the session cookie is transmitted in cleartext over HTTP.
- Simulate an attacker intercepting the session cookie and then reusing it in a subsequent request to demonstrate successful session hijacking.
- As a corrective test, update Django settings to enforce stricter parameters (e.g., set
SESSION_COOKIE_SECURE = True
). Verify that theSet-Cookie
header now includes secure attributes and that intercepted cookies are no longer transmitted over insecure channels when using HTTPS.
Description:
The OriginValidator
middleware in channels.security.websocket
aims to prevent Cross-Site WebSocket Hijacking (CSWSH) by validating the Origin
header of WebSocket connections against allowed origins. However, the validation logic in OriginValidator.match_allowed_origin
can be bypassed. An attacker can craft an Origin
header that includes a valid allowed origin as a subdomain of a malicious domain (e.g., allowed-domain.com.attacker-domain.com
), tricking the validator. The is_same_domain
function, used for origin matching, incorrectly validates such origins, leading to a bypass of the intended origin check and allowing malicious WebSocket connections.
Impact:
- Critical
- Successful exploitation allows an attacker to circumvent origin validation and establish WebSocket connections from malicious websites, leading to Cross-Site WebSocket Hijacking (CSWSH).
- This can enable attackers to perform unauthorized actions on behalf of authenticated users if the application uses session-based authentication and WebSockets for sensitive operations.
Vulnerability Rank: Critical
Currently Implemented Mitigations:
- The project utilizes
OriginValidator
middleware to check theOrigin
header, located in/code/channels/security/websocket.py
.
Missing Mitigations:
- The
is_same_domain
function used inOriginValidator.match_allowed_origin
is too permissive and vulnerable to subdomain bypasses. - The missing mitigation is to implement stricter origin validation logic that prevents subdomain bypasses. This could involve directly comparing hostnames after parsing and ensuring no subdomain matching occurs unless explicitly intended through wildcard configurations.
Preconditions:
- The Channels application uses
OriginValidator
middleware with a list of allowed origins. - WebSocket connections are used for sensitive operations within the application.
- The allowed origins list contains domain names without wildcards or with wildcard patterns that are not strictly defined to prevent subdomain matching vulnerabilities.
Source Code Analysis:
- Code location:
/code/channels/security/websocket.py
def match_allowed_origin(self, parsed_origin, pattern):
"""
...
"""
if parsed_origin is None:
return False
# Get ResultParse object
parsed_pattern = urlparse(pattern.lower())
if parsed_origin.hostname is None:
return False
if not parsed_pattern.scheme:
pattern_hostname = urlparse("//" + pattern).hostname or pattern
return is_same_domain(parsed_origin.hostname, pattern_hostname) # Vulnerable line
# ...
- The vulnerability lies in the line
return is_same_domain(parsed_origin.hostname, pattern_hostname)
. is_same_domain
fromdjango.http.request
performs a check that incorrectly considers subdomains of the allowed host as valid.- For example, with
allowed_origins
includingallowed-domain.com
,is_same_domain
incorrectly returnsTrue
for bothallowed-domain.com
andsubdomain.allowed-domain.com
. - This flawed logic is exploited when an attacker uses
allowed-domain.com.attacker-domain.com
as the origin.is_same_domain
comparesallowed-domain.com.attacker-domain.com
withallowed-domain.com
and erroneously returnsTrue
becauseallowed-domain.com
is considered a "domain" ofallowed-domain.com.attacker-domain.com
based onis_same_domain
's suffix matching logic.
# django/http/request.py
def is_same_domain(server_name, hostname):
"""
Return ``True`` if the two hostnames are equal.
If either hostname is a wildcard, return ``True`` if the non-wildcard
hostname matches the wildcard.
"""
server_name = server_name.lower()
hostname = hostname.lower()
if hostname.startswith('.'):
return server_name.endswith(hostname)
return server_name == hostname
- Django's
is_same_domain
implementation checks for suffix matches ifhostname
starts with.
. While wildcard patterns are not directly used in the provided vulnerability description, theis_same_domain
function still treatsallowed-domain.com
as a potential "domain" and incorrectly allows subdomains of attacker-controlled domains to pass the origin check if they embed the allowed domain as a subdomain.
Security Test Case:
- Set up a Channels application that employs
OriginValidator
middleware withallowed_origins = ["allowed-domain.com"]
. - Prepare a malicious website hosted on
attacker-domain.com
. - On the malicious website, create a JavaScript WebSocket client to connect to the Channels application's WebSocket endpoint.
- In the WebSocket client's connection request, set the
Origin
header tohttp://allowed-domain.com.attacker-domain.com
. - Observe the WebSocket connection attempt from the malicious website.
- Expected Result: The WebSocket connection is accepted by the Channels application, indicating a successful bypass of origin validation.
- Correct Behavior: The WebSocket connection should be rejected because the origin
http://allowed-domain.com.attacker-domain.com
is not in the allowed origins list and originates from a different domain (attacker-domain.com
).