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
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: 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
- MITRE Corporation. "CWE-551: Incorrect Behavior Order: Authorization Before Parsing and Canonicalization." https://cwe.mitre.org/data/definitions/551.html
- OWASP. "Path Traversal Cheat Sheet."
- CWE-41. "Improper Resolution of Path Equivalence."