PHP: URL auf korrekte Syntax und Existenz prüfen

PHP: URL auf korrekte Syntax und Existenz prüfen

Egal wie groß oder klein das Webprojekt auch ist, Eingaben von Benutzern stellen fast immer die Grundlage für weitere Aktion dar. Seien es Formulare zur Kontaktaufnahme, Nutzerregistrierung oder Parameterübergaben mittels GET. Gleichzeitig sind Benutzereingaben jedoch auch eine der größten Gefahren für Webanwendungen und daher mit höchster Vorsicht zu behandeln. Denn vom Programmierer werden hier meist nur bestimmte Werte erwartet, in Wahrheit steht es dem Benutzer aber völlig frei, welche Daten er übermittelt. Eine der wichtigsten Grundregeln bzgl. Sicherheit in Webanwendungen lautet deshalb:

Prüfe alle Eingaben!

Für ein aktuelles Projekt war ich deshalb auf der Suche nach einer Möglichkeit eine URL auf Gültigkeit zu prüfen. Denn allzu oft werden in ein Formularfeld zur Angabe der Website einfach Fantasiedomains, wie ich://habe.keine oder http://foo.bar, eingetragen.

Da mit PHP 5.2 die filter_var-Funktion eingeführt wurde, hoffte ich, dass der Parameter FILTER_VALIDATE_URL in Kombination mit FILTER_FLAG_SCHEME_REQUIRED oder FILTER_FLAG_HOST_REQUIRED mich meinem Ziel etwas näher bringen würde. Jedoch mußte ich nach einigen Tests feststellen, dass FILTER_VALIDATE_URL nur sehr bedacht eingesetzt werden sollte. Denn laut filter_var sind beispielsweise auch  folgende URLs gültig:

filter_var('keine://domain', FILTER_VALIDATE_URL) !== false; //true
filter_var('foo://bar', FILTER_VALIDATE_URL) !== false; //true
filter_var('javascript://test%0Aalert(xss)', FILTER_VALIDATE_URL) !== false; //true

Besonders das Beispiel mit dem Javascript-Code zeigt anschaulich, dass filter_var für eine zuverlässige URL-Prüfung in Webanwendungen deshalb ungeeignet ist und zu großen Problemen führen kann.

Daher entschied ich mich, für die  Syntaxüberprüfung der URL weiterhin auf reguläre Ausdrücke zu setzen. Als Regex für die URL-Syntax  verwende ich eine sehr umfangreiche und gut geteste Variante von Diego Perini. Kombiniert man diesen zusätzlich mit einer Abfrage des HTTP-Statuscodes, lassen sich alle nicht existierenden Domains dadurch ausfiltern.

function urlValidate($url)
{
    $url = trim($url);
    if(preg_match('%^(?:(?:https?)://)(?:\S+(?::\S*)?@|\d{1,3}(?:\.\d{1,3}){3}|(?:(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)(?:\.(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)*(?:\.[a-z\x{00a1}-\x{ffff}]{2,6}))(?::\d+)?(?:[^\s]*)?$%iu', $url))
    {
        if(ini_get('allow_url_fopen'))
        {
            $headers = @get_headers($url, 1);
            if (preg_match('/^HTTP\/.*\s+(200|401)/', $headers[0]))
            {
                return true;
            }
            elseif (preg_match('/^HTTP\/.*\s+(300|301|302|303|307|308)/', $headers[0]))
            {
                if ($headers !== false && isset($headers['Location']))
                {
                    if($headers['Location'] != $url)
                    {
                        return urlValidate($headers['Location']);
                    }
                    else	// Never ending story
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            else
	    {
                return false;
            }
        }
        elseif (function_exists('curl_version'))
        {
            $user_agent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0) Gecko/20121026 Firefox/16.0";
            $ch = @curl_init($url);
            @curl_setopt($ch, CURLOPT_HEADER, 1);
            @curl_setopt($ch, CURLOPT_NOBODY, 1);
            @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
            @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            @curl_setopt($ch, CURLOPT_HEADER, 1);
            @curl_setopt($ch, CURLOPT_TIMEOUT, 5);
            @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
            @curl_exec($ch);
            $http_statuscode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if(preg_match('/^200|301|302$/', $http_statuscode))
            {
                return true;
            }
        }
        else
        {
            throw new Exception('curl and allow_url_fopen are not avaiable.');
        }
    }
    return false;
}

Der Aufruf der Funktion ist denkbar einfach:

if(urlValidate('http://www.datenreise.de/php-url-korrekte-syntax-und-existenz-pruefen'))
{
    echo "URL existiert";
}
else
{
    echo "URL existiert nicht";
}

Um IDN-Domains zu prüfen, müssen diese vor dem Funktionsaufruf in Punycode umgewandelt werden. Beispiel (börse.de):

urlValidate('http://www.xn--brse-5qa.de')

Dieser Artikel war hilfreich für dich?
Bitte unterstütze Datenreise.de – Danke!

Spenden


Artikel bewerten:
SchlechtAusreichendBefriedigendGutSehr gut
4,80/5 (35 Bewertungen)

PHP: URL auf korrekte Syntax und Existenz prüfen 4,80 out of 5 based on 35 ratings
Loading...