Regulärer Ausdruck ohne Anker

Beschreibung

Regulärer Ausdruck ohne Anker ist eine Eingabevalidierungsschwachstelle, bei der Software einen regulären Ausdruck zur Validierung oder Filterung von Eingaben verwendet, aber keine Anker (^ für Start, $ für Ende) einschließt, die den Match auf den gesamten Eingabestring beschränken. Ohne Anker matched die Regex, wenn das Muster irgendwo innerhalb der Eingabe erscheint, was bösartigen Daten vor oder nach dem gematchten Teil erlaubt, die Validierung zu umgehen.

Risiko

Nicht verankerte reguläre Ausdrücke bieten falsche Sicherheit. Bei Verwendung zur Validierung scheinen sie für normale Eingaben korrekt zu funktionieren, blockieren aber gestaltete bösartige Eingaben nicht. Für Pfadvalidierung können Angreifer "../"-Traversal-Sequenzen vor oder nach dem gematchten Muster einfügen. Für IP-Adressvalidierung können Angreifer Adressen mit oktalen oder hexadezimalen Repräsentationen prefixen, die deren Bedeutung ändern. Die Konsequenzen hängen davon ab, was die nicht verankerte Regex schützt: Pfadtraversierung, Injection-Angriffe, Authentifizierungsumgehung oder andere Sicherheitsverletzungen werden möglich.

Lösung

Verwenden Sie immer Anker, wenn reguläre Ausdrücke ganze Eingabestrings validieren. Verwenden Sie ^ am Anfang und $ am Ende von Mustern, die komplette Strings matchen sollen. Verstehen Sie, was Ihre Regex matchen wird und was nicht - testen Sie mit bösartigen Eingaben, die zusätzliche Zeichen vor und nach dem erwarteten Muster enthalten. Erwägen Sie die Verwendung dedizierter Validierungsbibliotheken anstelle benutzerdefinierter Regex-Muster. In einigen Sprachen verwenden Sie Methoden, die implizit verankern (wie Javas matches() vs find()).

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Nicht verankerte Regex blockiert bösartige Eingaben nicht, die gültig aussehende Teile enthalten.
IntegritätBereich: Integrität

Anwendungsdaten ändern - Bösartige Daten passieren Validierung und beeinflussen Anwendungsverhalten.
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Pfadtraversierung über nicht verankerte Validierung kann sensible Dateien exponieren.

Beispielcode

Verwundbarer Code

// Verwundbar: Fehlende Anker bei Pfadvalidierung
$lang = $_GET['lang'];

// Verwundbar: Muster matched wenn [A-Za-z0-9]+ IRGENDWO in Eingabe erscheint
if (preg_match("/[A-Za-z0-9]+/", $lang)) {
    include("$dir/$lang");  // Pfadtraversierung möglich!
}

// Angriff: "../../etc/passwd" matched weil "etc" und "passwd" alphanumerisch sind
// Ergebnis: include("$dir/../../etc/passwd") - liest /etc/passwd
# Verwundbar: IP-Validierung ohne Anker
import re
import subprocess

ip_validator = re.compile(r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")

def vulnerable_ping(ip):
    # Verwundbar: Matched wenn gültiges IP-Muster irgendwo erscheint
    if ip_validator.match(ip):  # match() verankert nur am Anfang, nicht Ende
        subprocess.call(["ping", "-c", "1", ip])

# Angriff: "192.168.1.1; rm -rf /" matched weil gültige IP am Anfang erscheint
// Verwundbar: E-Mail-Validierung ohne Anker
function vulnerableValidateEmail(email) {
    // Verwundbar: Prüft nur ob Muster irgendwo erscheint
    const emailRegex = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;

    if (emailRegex.test(email)) {
        return true;  // Akzeptiert ungültige E-Mails
    }
    return false;
}

// Angriff: "malicious<script>@evil.com" passiert
// Angriff: "[email protected]<script>alert('xss')</script>" passiert

Gefixter Code

// Gefixt: Verankerte Pfadvalidierung
$lang = $_GET['lang'];

// Gefixt: ^ verankert Anfang, $ verankert Ende - muss GESAMTEN String matchen
if (preg_match("/^[A-Za-z0-9]+$/", $lang)) {
    include("$dir/$lang");
}

// "../etc/passwd" matched nicht mehr - enthält nicht-alphanumerische Zeichen
// Nur rein alphanumerische Strings wie "english" oder "de" werden matchen
# Gefixt: Korrekt verankerte IP-Validierung
import re
import subprocess

# Gefixt: ^ am Anfang, $ am Ende
ip_validator = re.compile(r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")

def fixed_ping(ip):
    # Gefixt: fullmatch() verankert implizit, oder verankertes Muster mit match() verwenden
    if ip_validator.fullmatch(ip):
        subprocess.call(["ping", "-c", "1", ip])

# "192.168.1.1; rm -rf /" matched nicht mehr
# Nur gültige IP-Adressen ohne zusätzlichen Inhalt matchen
// Gefixt: Verankerte E-Mail-Validierung
function fixedValidateEmail(email) {
    // Gefixt: ^ und $ verankern das Muster
    const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

    if (emailRegex.test(email)) {
        return true;
    }
    return false;
}

// "malicious<script>@evil.com" passiert nicht mehr - enthält <
// "[email protected]<script>..." passiert nicht mehr - hat zusätzlichen Inhalt
// Gefixt: Verankerte Benutzernamen-Validierung
public boolean fixedValidateUsername(String username) {
    // Gefixt: Anker im Muster
    Pattern pattern = Pattern.compile("^[a-zA-Z0-9_]+$");
    Matcher matcher = pattern.matcher(username);

    // matches() ist besser - verankert implizit
    return matcher.matches();
}

// Alternative: matches() verwenden, das implizit verankert
public boolean fixedValidateUsernameV2(String username) {
    return username.matches("[a-zA-Z0-9_]+");  // matches() verankert automatisch
}

// "admin'; DROP TABLE users;--" matched nicht mehr

CVE-Beispiele

  • CVE-2022-30034: Python RPC-Frameworks Web-UI verwendete nicht verankerte Regex zur Validierung von Benutzer-Login-E-Mails, was möglicherweise OAuth-Authentifizierungsumgehung ermöglichte.

Referenzen

  1. MITRE Corporation. "CWE-777: Regular Expression without Anchors." https://cwe.mitre.org/data/definitions/777.html
  2. OWASP. "Input Validation Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
  3. Regular-Expressions.info. "Anchors." https://www.regular-expressions.info/anchors.html