Permissiver Regulärer Ausdruck
Beschreibung
Permissiver Regulärer Ausdruck tritt auf, wenn ein Regex-Muster, das für Eingabevalidierung gedacht ist, zu nachsichtig ist und Eingaben durchlässt, die abgelehnt werden sollten. Häufige Probleme umfassen Verankerungsprobleme (keine Verwendung von ^ und $), gierige Quantifizierer, die zu viel matchen, Zeichenklassen-Fehler und Nichtberücksichtigung von Sonderzeichen oder Kodierungen. Der Regex scheint zu validieren, lässt aber tatsächlich bösartige Eingaben durch.
Risiko
Eingabevalidierungs-Bypasses ermöglichen Injection-Angriffe (SQL, XSS, Command). Fehlerhaft formatierte Daten gelangen ins System und verursachen Fehler oder unerwartetes Verhalten. Sicherheitsfilter, die auf Regex angewiesen sind, können umgangen werden. Datenintegritätsprobleme entstehen durch unsachgemäß validierte Eingaben. Authentifizierung kann umgangen werden, wenn Benutzername-/Passwort-Validierung zu permissiv ist.
Lösung
Verwenden Sie Anker (^ und $), um die gesamte Zeichenkette zu matchen. Testen Sie Regex gegen bekannt-schlechte Eingaben. Verwenden Sie nicht-gierige Quantifizierer, wo angemessen. Erwägen Sie die Verwendung dedizierter Validatoren anstelle von benutzerdefinierten Regex. Testen Sie Randfälle einschließlich leerer Strings, Sonderzeichen und verschiedener Kodierungen. Überprüfen Sie Regex auf häufige Fehler. Verwenden Sie Regex-Testtools, um Verhalten zu verifizieren.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sicherheit | Bereich: Validierungs-Bypass Bösartige Eingaben passieren Validierungsprüfungen. |
| Integrität | Bereich: Datenkorruption Ungültige Daten gelangen ins System. |
| Authentifizierung | Bereich: Bypass Ungültige Credentials können akzeptiert werden. |
Beispielcode + Lösungscode
Verwundbarer Code
// VERWUNDBAR: Fehlende Anker
function validateEmailVulnerable(email) {
// Fehlende ^ und $ - matcht Teilstring
return /\w+@\w+\.\w+/.test(email);
}
// Passiert Validierung:
validateEmailVulnerable("evil<script>@test.com"); // true! (matcht Teilstring)
validateEmailVulnerable("[email protected]<script>"); // true!
// VERWUNDBAR: Übermäßig permissive Zeichenklasse
function validateUsernameVulnerable(username) {
// Erlaubt zu viele Zeichen
return /^.{3,20}$/.test(username);
}
// Passiert:
validateUsernameVulnerable("admin'--"); // SQL-Injection-Zeichen passieren
validateUsernameVulnerable("<script>"); // XSS-Zeichen passieren
// VERWUNDBAR: Falsche Zeichenklasse
function validatePhoneVulnerable(phone) {
// [0-9] beabsichtigt aber falsche Syntax
return /^[0-9-]+$/.test(phone);
}
// Das - erstellt einen Bereich, passiert unerwartetes:
validatePhoneVulnerable("---"); // true
validatePhoneVulnerable("9-0"); // true
// VERWUNDBAR: Gierige Quantifizierer-Probleme
function extractURLVulnerable(text) {
// .* ist gierig, matcht zu viel
const match = text.match(/href="(.*)"/);
return match ? match[1] : null;
}
// Gegeben: href="http://good.com" onclick="evil()"
// Gibt zurück: http://good.com" onclick="evil()
// Weil .* gierig bis zum letzten " matcht
// VERWUNDBAR: Groß-/Kleinschreibung
function validateCommandVulnerable(cmd) {
// Fehlender case-insensitive Flag
return /^(GET|POST|PUT|DELETE)$/.test(cmd);
}
// Passiert unerwartetes:
validateCommandVulnerable("get"); // false - aber sollte vielleicht gültig sein
validateCommandVulnerable("Get"); // false
// VERWUNDBAR: Unicode-/Kodierungs-Probleme
function validateNameVulnerable(name) {
// \w matcht keine Unicode-Buchstaben
return /^\w+$/.test(name);
}
// Lehnt gültige Namen ab:
validateNameVulnerable("José"); // false
validateNameVulnerable("北京"); // false
# VERWUNDBAR: Teilweises Matching
import re
def validate_ip_vulnerable(ip):
# Fehlende ^ und $ - teilweises Match
pattern = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
return re.search(pattern, ip) is not None
# Passiert:
validate_ip_vulnerable("evil192.168.1.1evil") # True!
validate_ip_vulnerable("999.999.999.999") # True (ungültige IP)
# VERWUNDBAR: Escape-Sequenz-Probleme
def validate_path_vulnerable(path):
# Punkt nicht escaped - matcht jedes Zeichen
pattern = r'^/var/www/.+.html
```java
// VERWUNDBAR: Musterkompilierungs-Probleme
public class VulnerableValidator {
// Fehlendes CASE_INSENSITIVE könnte beabsichtigt sein, aber oft nicht
private static final Pattern EMAIL_PATTERN =
Pattern.compile("\\w+@\\w+\\.\\w+"); // Keine Anker!
public boolean validateEmail(String email) {
return EMAIL_PATTERN.matcher(email).find(); // find() nicht matches()!
}
// VERWUNDBAR: DOTALL-Modus-Probleme
private static final Pattern SCRIPT_PATTERN =
Pattern.compile("<script>.*</script>", Pattern.DOTALL);
public String removeScripts(String html) {
// Gieriges .* mit DOTALL entfernt zu viel
return SCRIPT_PATTERN.matcher(html).replaceAll("");
}
}
Lösungscode
// SICHER: Ordentliche Anker und Zeichenklassen
function validateEmailSafe(email) {
// Anker stellen sicher, dass gesamter String matcht
// Präzisere Zeichenklassen
const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return pattern.test(email);
}
// SICHER: Restriktive Benutzernamen-Validierung
function validateUsernameSafe(username) {
// Nur spezifische sichere Zeichen erlauben
// Anker stellen vollständiges Match sicher
return /^[a-zA-Z0-9_-]{3,20}$/.test(username);
}
// SICHER: Ordentliche Telefon-Validierung
function validatePhoneSafe(phone) {
// Bindestrich escapen oder ans Ende der Klasse setzen
return /^[0-9\-]+$/.test(phone);
// Oder spezifischeres Format:
// return /^\d{3}-\d{3}-\d{4}$/.test(phone);
}
// SICHER: Nicht-gieriger Quantifizierer
function extractURLSafe(text) {
// .*? ist nicht-gierig, matcht Minimum
const match = text.match(/href="(.*?)"/);
return match ? match[1] : null;
// Noch besser - Anführungszeichen aus Klasse ausschließen:
// const match = text.match(/href="([^"]*)"/);
}
// SICHER: Groß-/Kleinschreibungs-Behandlung
function validateCommandSafe(cmd) {
// Case-insensitive Flag
return /^(GET|POST|PUT|DELETE)$/i.test(cmd);
}
// SICHER: Unicode-Unterstützung
function validateNameSafe(name) {
// Unicode-Buchstaben-Kategorie
return /^[\p{L}\p{M}' -]+$/u.test(name);
}
// SICHER: Eingabelängen-Limits
function validateInputSafe(input, maxLength = 100) {
if (input.length > maxLength) {
return false;
}
return /^[a-zA-Z0-9_-]+$/.test(input);
}
import re
import ipaddress
# SICHER: Vollständiges String-Matching mit Ankern
def validate_ip_safe(ip):
# Anker verwenden
pattern = r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
```java
// SICHER: Ordentliche Java-Regex-Verwendung
public class SafeValidator {
// Verankertes Muster
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
public boolean validateEmail(String email) {
// matches() verwenden, das vollständiges Match erfordert
return EMAIL_PATTERN.matcher(email).matches();
}
// SICHER: Nicht-gierige Script-Entfernung
private static final Pattern SCRIPT_PATTERN =
Pattern.compile("<script[^>]*>.*?</script>",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
public String removeScripts(String html) {
// Nicht-gieriges .*? matcht jeden Script-Tag einzeln
return SCRIPT_PATTERN.matcher(html).replaceAll("");
}
// SICHER: Eingabevalidierung mit Grenzen
public boolean validateUsername(String username) {
if (username == null || username.length() > 50) {
return false;
}
return username.matches("^[a-zA-Z0-9_]{3,20}$");
}
}
Ausgenutzt in der Praxis
E-Mail-Validierungs-Bypass
Permissiver E-Mail-Regex erlaubte XSS-Payloads in "E-Mail"-Feldern.
URL-Filter-Bypass
Übermäßig permissive URL-Validierung erlaubte bösartige Umleitungen.
SQL-Injection
Eingabevalidierungs-Regex umgangen, was SQL-Injection ermöglichte.
Tools zum Testen/Ausnutzen
- regex101 — Regex testen und debuggen.
- RegexBuddy — Regex-Entwicklungstool.
- Fuzzing-Tools mit bösartigen Eingabemustern.
CVE-Beispiele
- CVEs durch Regex-Validierungs-Bypass in Webanwendungen.
- WAF-Bypass durch permissive Regex-Regeln.
Referenzen
- MITRE. "CWE-625: Permissive Regular Expression." https://cwe.mitre.org/data/definitions/625.html
- OWASP. "Input Validation Cheat Sheet."