Skip to content

Security: ApiServerConfig allows wildcard CORS origin #430

@sfloess

Description

@sfloess

Description

ApiServerConfig.Builder.addAllowedOrigin() accepts wildcard "*" with only a warning comment, creating a critical security vulnerability in production deployments.

Location

platform-api/src/main/java/org/flossware/platform/api/ApiServerConfig.java:315-325

public Builder addAllowedOrigin(String origin) {
  if (origin == null) {
    throw new IllegalArgumentException("origin cannot be null");
  }
  // Note: Wildcard "*" is allowed but not recommended for production
  // See class javadoc for security warnings about wildcard CORS
  if (this.allowedOrigins == null) {
    this.allowedOrigins = new HashSet<>();
  }
  this.allowedOrigins.add(origin);
  return this;
}

Impact

Severity: HIGH
Allowing wildcard CORS creates severe security vulnerabilities:

  1. XSS Amplification: Any website can make authenticated requests to the API
  2. CSRF Attacks: Bypass same-origin policy, enable cross-site request forgery
  3. Data Theft: Malicious sites can steal sensitive data from API responses
  4. Credential Theft: If cookies/auth headers sent, attackers can steal credentials

Attack Scenario

// Attacker's website at evil.com
fetch('https://platform-api.company.com/api/applications', {
  credentials: 'include'  // Send cookies
}).then(r => r.json()).then(data => {
  // Steal application secrets and send to attacker
  sendToAttacker(data);
});

Recommendation

  1. Reject wildcard in production: Throw exception if origin is "*" and not in dev mode
  2. Add validation: Only allow https:// origins in production
  3. Add dev mode flag: Allow wildcard only if explicitly enabled
  4. Validate origin format: Ensure protocol, host, port are specified

Example Fix

public Builder addAllowedOrigin(String origin) {
  if (origin == null) {
    throw new IllegalArgumentException("origin cannot be null");
  }
  
  // SECURITY: Reject wildcard unless explicitly in development mode
  if ("*".equals(origin)) {
    boolean allowWildcard = Boolean.getBoolean("platform.api.dev.allowWildcardCors");
    if (!allowWildcard) {
      throw new IllegalArgumentException(
        "Wildcard CORS origin '*' is not allowed in production. " +
        "Specify exact origins like 'https://console.example.com'. " +
        "To allow wildcard in development, set -Dplatform.api.dev.allowWildcardCors=true"
      );
    }
    LOGGER.warn("SECURITY WARNING: Wildcard CORS enabled - FOR DEVELOPMENT ONLY");
  }
  
  // Validate HTTPS in production (unless localhost for dev)
  if (!origin.equals("*") && !origin.startsWith("https://") && 
      !origin.startsWith("http://localhost") && 
      !origin.startsWith("http://127.0.0.1")) {
    throw new IllegalArgumentException(
      "CORS origin must use HTTPS in production: " + origin
    );
  }
  
  this.allowedOrigins.add(origin);
  return this;
}

References

Labels

security, critical

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions