Permissive Cross-Domain-Richtlinie mit nicht vertrauenswürdigen Domains

Beschreibung

Permissive Cross-Domain-Richtlinie mit nicht vertrauenswürdigen Domains tritt auf, wenn Software eine Cross-Domain-Richtliniendatei (wie crossdomain.xml oder clientaccesspolicy.xml) verwendet, die Domains enthält, denen nicht vertraut werden sollte. Diese Richtliniendateien kontrollieren, welche externen Domains Cross-Origin-Anfragen an eine Webanwendung stellen können. Übermäßig permissive Richtlinien, insbesondere solche, die Wildcards (*) verwenden, ermöglichen es jeder Website, mit der Anwendung zu interagieren, was möglicherweise zu Datendiebstahl, CSRF-Angriffen und anderen Cross-Origin-Schwachstellen führt.

Risiko

Übermäßig permissive CORS-Richtlinien und Cross-Domain-Dateien setzen Anwendungen erheblichen Risiken aus. Angreifer können authentifizierte Anfragen von bösartigen Seiten stellen und Benutzerdaten stehlen oder unautorisierte Aktionen durchführen. Die Wildcard (*) in Access-Control-Allow-Origin kombiniert mit Credentials ermöglicht es jeder Seite, Benutzer zu imitieren. Fehlkonfiguriertes CORS hat zu zahlreichen Datenlecks geführt, bei denen Angreifer sensible Informationen aus authentifizierten Sitzungen extrahierten. APIs konfigurieren häufig CORS falsch und legen interne Daten offen.

Lösung

Verwenden Sie niemals Wildcard (*) für Access-Control-Allow-Origin, wenn Credentials involviert sind. Führen Sie eine strikte Whitelist erlaubter Origins. Validieren Sie den Origin-Header gegen die Whitelist, bevor Sie ihn reflektieren. Vertrauen Sie nicht benutzersupplied Origins. Entfernen oder beschränken Sie crossdomain.xml- und clientaccesspolicy.xml-Dateien. Implementieren Sie ordnungsgemäße CORS-Header mit spezifischen Origins. Verwenden Sie SameSite-Cookies als zusätzlichen Schutz. Prüfen Sie regelmäßig Cross-Origin-Konfigurationen.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Datendiebstahl

Bösartige Seiten können Antworten von der anfälligen Anwendung lesen und Benutzerdaten stehlen.
IntegritätBereich: Unautorisierte Aktionen

Cross-Origin-Anfragen können zustandsändernde Operationen als authentifizierter Benutzer durchführen.
ZugriffskontrolleBereich: Authentifizierungsumgehung

Angreifer können auf authentifizierte Ressourcen zugreifen, indem sie Benutzersitzungen ausnutzen.

Beispielcode + Lösungscode

Anfälliger Code

<!-- ANFÄLLIG: crossdomain.xml mit Wildcard -->
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="*" />  <!-- Erlaubt JEDE Domain! -->
</cross-domain-policy>

<!-- ANFÄLLIG: clientaccesspolicy.xml -->
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers="*">
                <domain uri="*"/>  <!-- Erlaubt JEDE Domain! -->
            </allow-from>
            <grant-to>
                <resource path="/" include-subpaths="true"/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>
# ANFÄLLIG: Wildcard CORS mit Credentials
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, origins="*", supports_credentials=True)  # GEFÄHRLICH!

# ANFÄLLIG: Origin ohne Validierung reflektieren
@app.after_request
def add_cors_headers(response):
    origin = request.headers.get('Origin')
    # Reflektiert JEDEN Origin!
    response.headers['Access-Control-Allow-Origin'] = origin
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response

# ANFÄLLIG: Regex-Umgehung
ALLOWED_ORIGINS_PATTERN = r'.*\.example\.com'

@app.after_request
def add_cors_regex(response):
    origin = request.headers.get('Origin')
    if re.match(ALLOWED_ORIGINS_PATTERN, origin):
        # Angreifer verwendet: evil.com.example.com oder evil-example.com
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Access-Control-Allow-Credentials'] = 'true'
    return response
// ANFÄLLIG: Spring CORS mit Wildcard
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("*")  // Jeder Origin!
            .allowCredentials(true)  // Mit Credentials!
            .allowedMethods("*");
    }
}

// ANFÄLLIG: Filter reflektiert Origin
@WebFilter("/*")
public class VulnerableCorsFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        String origin = request.getHeader("Origin");
        // Reflektiert jeden Origin!
        response.setHeader("Access-Control-Allow-Origin", origin);
        response.setHeader("Access-Control-Allow-Credentials", "true");

        chain.doFilter(req, res);
    }
}
// ANFÄLLIG: Express mit permissivem CORS
const cors = require('cors');

app.use(cors({
    origin: true,  // Reflektiert jeden Origin
    credentials: true
}));

// ANFÄLLIG: Manuelle CORS mit Reflektion
app.use((req, res, next) => {
    // Reflektiert welchen Origin auch immer gesendet wird
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.header('Access-Control-Allow-Credentials', 'true');
    next();
});

// ANFÄLLIG: Unzureichende Origin-Prüfung
app.use((req, res, next) => {
    const origin = req.headers.origin;
    // Schwache Prüfung - Angreifer verwendet example.com.evil.com
    if (origin && origin.includes('example.com')) {
        res.header('Access-Control-Allow-Origin', origin);
        res.header('Access-Control-Allow-Credentials', 'true');
    }
    next();
});

Korrigierter Code

<!-- SICHER: Nur spezifische Domains -->
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <site-control permitted-cross-domain-policies="master-only"/>
    <allow-access-from domain="app.example.com" secure="true"/>
    <allow-access-from domain="www.example.com" secure="true"/>
</cross-domain-policy>

<!-- Oder besser: Entfernen, wenn nicht benötigt -->
<!-- Wenn Flash/Silverlight nicht verwendet wird, crossdomain.xml komplett löschen -->
# SICHER: Strikte Origin-Whitelist
from flask import Flask, request
from flask_cors import CORS

app = Flask(__name__)

ALLOWED_ORIGINS = {
    'https://app.example.com',
    'https://www.example.com',
    'https://admin.example.com'
}

def check_origin(origin):
    return origin in ALLOWED_ORIGINS

CORS(app, origins=check_origin, supports_credentials=True)

# SICHER: Manuelle CORS mit strikter Validierung
@app.after_request
def add_cors_headers(response):
    origin = request.headers.get('Origin')

    if origin and origin in ALLOWED_ORIGINS:
        response.headers['Access-Control-Allow-Origin'] = origin
        response.headers['Access-Control-Allow-Credentials'] = 'true'
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
        response.headers['Access-Control-Max-Age'] = '86400'

    # Niemals Wildcard mit Credentials verwenden
    # Nicht vertrauenswürdige Origins nicht reflektieren

    return response

# SICHER: Ordnungsgemäße Subdomain-Validierung
def is_valid_origin(origin):
    if not origin:
        return False

    try:
        from urllib.parse import urlparse
        parsed = urlparse(origin)

        # Muss HTTPS sein
        if parsed.scheme != 'https':
            return False

        # Exakte Übereinstimmung oder gültige Subdomain
        allowed_domains = ['example.com', 'example.org']
        host = parsed.netloc.lower()

        for domain in allowed_domains:
            if host == domain or host.endswith('.' + domain):
                return True

        return False
    except:
        return False
// SICHER: Spring CORS mit Whitelist
@Configuration
public class SecureCorsConfig implements WebMvcConfigurer {

    private static final List<String> ALLOWED_ORIGINS = List.of(
        "https://app.example.com",
        "https://www.example.com",
        "https://admin.example.com"
    );

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins(ALLOWED_ORIGINS.toArray(new String[0]))
            .allowCredentials(true)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .allowedHeaders("Content-Type", "Authorization")
            .maxAge(86400);
    }
}

// SICHER: Benutzerdefinierter CORS-Filter mit Validierung
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecureCorsFilter implements Filter {

    private static final Set<String> ALLOWED_ORIGINS = Set.of(
        "https://app.example.com",
        "https://www.example.com"
    );

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        String origin = request.getHeader("Origin");

        if (origin != null && ALLOWED_ORIGINS.contains(origin)) {
            response.setHeader("Access-Control-Allow-Origin", origin);
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
            response.setHeader("Access-Control-Max-Age", "86400");
        }

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }

        chain.doFilter(req, res);
    }
}

// SICHER: Spring Security verwenden
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().configurationSource(corsConfigurationSource());
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of(
            "https://app.example.com",
            "https://www.example.com"
        ));
        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(List.of("*"));
        configuration.setMaxAge(86400L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
// SICHER: Express mit strikter Origin-Whitelist
const cors = require('cors');

const ALLOWED_ORIGINS = new Set([
    'https://app.example.com',
    'https://www.example.com',
    'https://admin.example.com'
]);

const corsOptions = {
    origin: (origin, callback) => {
        // Anfragen ohne Origin erlauben (mobile Apps, curl, etc.)
        // Nur wenn dies spezifisch benötigt wird
        if (!origin) {
            return callback(null, true);
        }

        if (ALLOWED_ORIGINS.has(origin)) {
            callback(null, true);
        } else {
            callback(new Error('Nicht von CORS erlaubt'));
        }
    },
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    maxAge: 86400
};

app.use(cors(corsOptions));

// SICHER: Manuelle Implementierung mit strikter Validierung
function validateOrigin(origin) {
    if (!origin) return false;

    try {
        const url = new URL(origin);

        // Muss HTTPS sein
        if (url.protocol !== 'https:') return false;

        // Gegen Whitelist prüfen
        const allowedHosts = ['app.example.com', 'www.example.com'];
        return allowedHosts.includes(url.hostname);

    } catch {
        return false;
    }
}

app.use((req, res, next) => {
    const origin = req.headers.origin;

    if (validateOrigin(origin)) {
        res.header('Access-Control-Allow-Origin', origin);
        res.header('Access-Control-Allow-Credentials', 'true');
        res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
        res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
        res.header('Access-Control-Max-Age', '86400');
    }

    if (req.method === 'OPTIONS') {
        return res.sendStatus(200);
    }

    next();
});

Ausgenutzt in der Praxis

Facebook CORS-Fehlkonfiguration (2019)

Eine CORS-Fehlkonfiguration in Facebooks API ermöglichte es Angreifern, private Daten zu lesen, indem sie Cross-Origin-Anfragen von bösartigen Websites stellten.

Verschiedene API-Datenlecks

Mehrere Organisationen haben durch CORS-Fehlkonfigurationen in ihren APIs Datenlecks erlitten, die es jeder Website ermöglichten, Benutzerdaten aus authentifizierten Sitzungen zu extrahieren.

Adobe Flash Cross-Domain-Angriffe

Vor der Einstellung von Flash ermöglichten permissive crossdomain.xml-Dateien zahlreiche Cross-Site-Datendiebstahl-Angriffe.


Tools zum Testen/Ausnutzen


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-942: Permissive Cross-domain Policy with Untrusted Domains." https://cwe.mitre.org/data/definitions/942.html

  2. OWASP. "CORS Security." https://owasp.org/www-community/attacks/CORS_OriginHeaderScrutiny