Skip to content

CORS middleware reflects Origin with Allow-Credentials, enabling cross-origin attacks (CWE-346) #6390

Description

@jaredbischof

Summary

The CORS middleware reflects the request's Origin header back as Access-Control-Allow-Origin when the configured allow_origin contains *, while simultaneously setting Access-Control-Allow-Credentials: true. This is a well-known CORS misconfiguration that allows any website to make authenticated cross-origin requests to the StackStorm API.

Affected File

st2common/st2common/middleware/cors.py (lines 65-69, 88, 91)

if origin:
    if "*" in origins:
        origin_allowed = origin  # reflects whatever the browser sends
    else:
        origin_allowed = origin if origin in origins else list(origins)[0]
# ...
headers["Access-Control-Allow-Origin"] = origin_allowed
# ...
headers["Access-Control-Allow-Credentials"] = "true"  # always set

Additionally, the middleware hardcodes localhost development origins that are always allowed:

# Default gulp development server WebUI URL
origins.add("http://127.0.0.1:3000")

# By default WebUI simple http server listens on 8080
origins.add("http://localhost:8080")
origins.add("http://127.0.0.1:8080")

Impact

When allow_origin contains *:
Any website can make authenticated, credential-bearing requests to the StackStorm API from a user's browser. If a StackStorm user visits a malicious page while logged in, the attacker can:

  • Read API responses (execution results, datastore values, pack configurations)
  • Trigger action executions
  • Modify rules and workflows
  • Exfiltrate API keys and tokens

This is because the combination of a reflected Access-Control-Allow-Origin and Access-Control-Allow-Credentials: true tells the browser to include cookies/auth headers and allow the response to be read by the requesting page.

Hardcoded localhost origins:
If an attacker can run a web server on localhost:3000 or localhost:8080 on a machine where a StackStorm user has an active session, they can exploit these hardcoded origins. These should not be present in production builds.

Recommended Fix

  1. Never reflect the Origin header when Access-Control-Allow-Credentials is true. If wildcard origin is configured, either:

    • Set Access-Control-Allow-Origin: * and remove Access-Control-Allow-Credentials (allows unauthenticated CORS only)
    • Or reject the configuration and log a warning
  2. Remove hardcoded localhost origins from the default allowed list. These should only be present in development configurations.

  3. Only set Access-Control-Allow-Credentials: true when the origin is explicitly whitelisted (not wildcarded).

if origin and origin in origins:
    headers["Access-Control-Allow-Origin"] = origin
    headers["Access-Control-Allow-Credentials"] = "true"
elif "*" in origins:
    headers["Access-Control-Allow-Origin"] = "*"
    # Do NOT set Allow-Credentials with wildcard origin

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions