Falsche Verhaltensreihenfolge: Autorisierung vor Parsing und Kanonisierung

Beschreibung

Falsche Verhaltensreihenfolge: Autorisierung vor Parsing und Kanonisierung ist eine Schwachstelle, bei der ein Webserver oder eine Anwendung Autorisierungsprüfungen auf URL-Pfade durchführt, bevor diese vollständig geparst und normalisiert wurden. Dieser Reihenfolgefehler ermöglicht Angreifern, Zugriffskontrollen zu umgehen, indem sie alternative Pfaddarstellungen verwenden, die semantisch äquivalent, aber textuell unterschiedlich sind. Zum Beispiel, wenn /admin geschützt ist, aber der Server die Autorisierung vor der Pfadnormalisierung prüft, können Anfragen an /./admin, //admin, /admin/../admin oder URL-kodierte Varianten wie /%61dmin den Schutz umgehen.

Risiko

Pfad-Kanonisierungs-Umgehungsschwachstellen erzeugen kritische Zugriffskontrrollfehler. Angreifer können auf geschützte Ressourcen, administrative Funktionen oder sensible Dateien zugreifen, indem sie URLs erstellen, die Autorisierungsprüfungen in ihrer Rohform bestehen, aber nach der Verarbeitung zu geschützten Pfaden aufgelöst werden. Pfadäquivalenztechniken umfassen Punkt-Segmente (./, ../), doppelte Schrägstriche, URL-Kodierung, Unicode-Normalisierung, Groß-/Kleinschreibungsvariationen und nachgestellte Zeichen. Die Auswirkungen reichen von unbefugtem Datenzugriff bis zur vollständigen administrativen Übernahme.

Lösung

Führen Sie URL-Parsing und Kanonisierung immer vor Autorisierungsprüfungen durch. Normalisieren Sie Pfade durch Auflösen von Punkt-Segmenten, Zusammenführen mehrerer Schrägstriche, Dekodieren URL-kodierter Zeichen und gegebenenfalls Normalisieren der Groß-/Kleinschreibung. Führen Sie Autorisierungsprüfungen auf dem kanonisierten, endgültigen Pfad durch, der tatsächlich verwendet wird. Achten Sie darauf, Doppeldekodierungs-Schwachstellen zu vermeiden, indem Sie genau einmal dekodieren. Verwenden Sie etablierte Bibliotheken für die Pfadnormalisierung. Testen Sie Autorisierung mit verschiedenen Pfadkodierungs- und Äquivalenztechniken.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Autorisierungskontrollen können durch nicht-kanonische Pfaddarstellungen umgangen werden, die Zugriffsprüfungen umgehen, aber zu geschützten Ressourcen aufgelöst werden.

Beispielcode

Verwundbarer Code

// Verwundbar: Autorisierung vor Kanonisierung
public class VerwundbarerFilter implements Filter {
    private Set<String> protectedPaths = Set.of("/admin", "/api/admin", "/management");

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestPath = httpRequest.getRequestURI();

        // Verwundbar: Rohen Pfad vor Normalisierung prüfen
        if (isProtectedPath(requestPath)) {
            if (!isAuthenticated(httpRequest)) {
                ((HttpServletResponse) response).sendError(403, "Zugriff verweigert");
                return;
            }
        }
        chain.doFilter(request, response);
    }

    private boolean isProtectedPath(String path) {
        // Verwundbar: Prüft gegen rohen, nicht-normalisierten Pfad
        for (String protectedPath : protectedPaths) {
            if (path.startsWith(protectedPath)) return true;
        }
        return false;
    }
    // Umgehungen: /./admin, //admin, /%61dmin
}
# Verwundbar: Flask ohne Pfadkanonisierung
from flask import Flask, request, abort

app = Flask(__name__)
PROTECTED_PATHS = ['/admin', '/api/internal']

@app.before_request
def authorize_request():
    raw_path = request.path  # Verwundbar: Roher Pfad
    
    # Verwundbar: Prüfung vor Normalisierung
    if raw_path in PROTECTED_PATHS or any(raw_path.startswith(p + '/') for p in PROTECTED_PATHS):
        if not session.get('is_admin'):
            abort(403)
    # Umgehungen: /./admin, //admin, /ADMIN
// Verwundbar: Express ohne Kanonisierung
const express = require('express');
const app = express();
const PROTECTED_PATHS = ['/admin', '/api/admin'];

app.use((req, res, next) => {
    const requestPath = req.path;  // Verwundbar: Roher Pfad

    const isProtected = PROTECTED_PATHS.some(p =>
        requestPath.startsWith(p) || requestPath === p
    );

    if (isProtected && !req.session?.isAdmin) {
        return res.status(403).send('Zugriff verweigert');
    }
    next();
});
// Umgehungen: /./admin, //admin, /admin%2F

Lösungscode

// Behoben: Kanonisierung vor Autorisierung
public class SichererFilter implements Filter {
    private Set<String> protectedPaths = Set.of("/admin", "/api/admin");

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // Behoben: Pfad VOR Autorisierung kanonisieren
        String canonicalPath = canonicalizePath(httpRequest.getRequestURI());

        if (isProtectedPath(canonicalPath)) {
            if (!isAuthenticated(httpRequest)) {
                ((HttpServletResponse) response).sendError(403, "Zugriff verweigert");
                return;
            }
        }
        chain.doFilter(request, response);
    }

    private String canonicalizePath(String path) {
        if (path == null) return "/";

        // Behoben: URL-kodierte Zeichen dekodieren (einmal!)
        try {
            path = URLDecoder.decode(path, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) { }

        // Behoben: Pfad mit Java URI normalisieren
        try {
            URI uri = new URI(path).normalize();
            path = uri.getPath();
        } catch (URISyntaxException e) {
            return null;
        }

        // Behoben: Mehrfache Schrägstriche zusammenführen
        path = path.replaceAll("/+", "/");

        // Behoben: Groß-/Kleinschreibung normalisieren
        path = path.toLowerCase();

        return path;
    }

    private boolean isProtectedPath(String path) {
        if (path == null) return true;  // Ungültige Pfade ablehnen
        for (String protectedPath : protectedPaths) {
            if (path.equals(protectedPath) || path.startsWith(protectedPath + "/")) {
                return true;
            }
        }
        return false;
    }
}
# Behoben: Flask mit ordnungsgemäßer Pfadkanonisierung
from flask import Flask, request, abort, session
from urllib.parse import unquote
import posixpath
import re

app = Flask(__name__)
PROTECTED_PATHS = ['/admin', '/api/internal']

def canonicalize_path(path):
    if not path: return '/'
    
    # Behoben: URL dekodieren (einmal)
    path = unquote(path)
    
    # Behoben: Pfad normalisieren (. und .. auflösen)
    path = posixpath.normpath(path)
    
    # Behoben: Mehrfache Schrägstriche zusammenführen
    path = re.sub(r'/+', '/', path)
    
    # Behoben: Groß-/Kleinschreibung normalisieren
    path = path.lower()
    
    return path

@app.before_request
def authorize_request():
    # Behoben: VOR Prüfung kanonisieren
    canonical_path = canonicalize_path(request.path)
    
    for protected in PROTECTED_PATHS:
        if canonical_path == protected or canonical_path.startswith(protected + '/'):
            if not session.get('is_admin'):
                abort(403)
// Behoben: Express mit sicherer Pfadkanonisierung
const express = require('express');
const path = require('path');
const app = express();
const PROTECTED_PATHS = ['/admin', '/api/admin'];

function canonicalizePath(urlPath) {
    if (!urlPath) return '/';

    // Behoben: URL dekodieren (einmal)
    try {
        urlPath = decodeURIComponent(urlPath);
    } catch (e) {
        return null;
    }

    // Behoben: Mit path-Modul normalisieren
    urlPath = path.posix.normalize(urlPath);
    urlPath = urlPath.replace(/\/+/g, '/');
    urlPath = urlPath.toLowerCase();

    return urlPath;
}

app.use((req, res, next) => {
    // Behoben: VOR Autorisierung kanonisieren
    const canonicalPath = canonicalizePath(req.path);

    if (canonicalPath === null) {
        return res.status(400).send('Ungültiger Pfad');
    }

    const isProtected = PROTECTED_PATHS.some(p =>
        canonicalPath === p || canonicalPath.startsWith(p + '/')
    );

    if (isProtected && !req.session?.isAdmin) {
        return res.status(403).send('Zugriff verweigert');
    }
    next();
});

CVE-Beispiele

  • CVE-2021-41773: Apache HTTP Server Pfaddurchlauf durch unzureichende Pfadnormalisierung.
  • CVE-2020-5410: Spring Cloud Config Server Pfaddurchlauf-Schwachstelle.
  • CVE-2019-11043: PHP-FPM Schwachstelle durch Pfadbehandlungsprobleme.

Referenzen

  1. MITRE Corporation. "CWE-551: Incorrect Behavior Order: Authorization Before Parsing and Canonicalization." https://cwe.mitre.org/data/definitions/551.html
  2. OWASP. "Path Traversal Cheat Sheet."
  3. CWE-41. "Improper Resolution of Path Equivalence."