1271 Stimmen

Wie kann ich Benutzereingaben mit PHP bereinigen?

Gibt es irgendwo eine Auffangfunktion, die gut funktioniert, um Benutzereingaben für SQL-Injection- und XSS-Angriffe zu säubern und gleichzeitig bestimmte Arten von HTML-Tags zuzulassen?

48 Stimmen

Um Sql-Injection zu vermeiden, verwendet man heutzutage PDO oder MySQLi.

97 Stimmen

Die Verwendung von PDO oder MySQLi ist nicht genug. Wenn Sie Ihre SQL-Anweisungen mit nicht vertrauenswürdigen Daten erstellen, wie select * from users where name='$name' dann spielt es keine Rolle, ob Sie PDO oder MySQLi oder MySQL verwenden. Sie sind immer noch in Gefahr. Sie müssen parametrisierte Abfragen verwenden oder, wenn es sein muss, Escape-Mechanismen für Ihre Daten einsetzen, aber das ist weit weniger empfehlenswert.

30 Stimmen

@AndyLester Wollen Sie damit andeuten, dass jemand PDO ohne vorbereitete Anweisungen verwendet? :)

1299voto

troelskn Punkte 110542

Es ist ein weit verbreiteter Irrglaube, dass Benutzereingaben gefiltert werden können. PHP hat sogar ein (inzwischen veraltetes) "Feature", genannt magische Anführungszeichen die auf dieser Idee aufbaut. Das ist Blödsinn. Vergessen Sie das Filtern (oder Reinigen, oder wie auch immer man es nennt).

Was Sie tun sollten, um Probleme zu vermeiden, ist ganz einfach: Wann immer Sie ein Datenelement in einen fremden Code einbetten, müssen Sie es gemäß den Formatierungsregeln dieses Codes behandeln. Sie müssen sich jedoch darüber im Klaren sein, dass diese Regeln zu kompliziert sein können, um sie alle manuell zu befolgen. In SQL gibt es zum Beispiel unterschiedliche Regeln für Zeichenketten, Zahlen und Bezeichner. Um Ihnen die Arbeit zu erleichtern, gibt es in den meisten Fällen ein spezielles Werkzeug für eine solche Einbettung. Wenn Sie z. B. eine PHP-Variable in einer SQL-Abfrage verwenden möchten, müssen Sie eine vorbereitete Anweisung verwenden, die sich um die richtige Formatierung/Behandlung kümmert.

Ein weiteres Beispiel ist HTML: Wenn Sie Zeichenketten in HTML-Markup einbetten, müssen Sie diese mit htmlspecialchars . Das bedeutet, dass jeder einzelne echo o print Anweisung sollte verwendet werden htmlspecialchars .

Ein drittes Beispiel könnten Shell-Befehle sein: Wenn Sie Zeichenketten (wie Argumente) in externe Befehle einbetten und diese mit exec verwenden, dann müssen Sie escapeshellcmd y escapeshellarg .

Ein sehr überzeugendes Beispiel ist auch JSON. Die Regeln sind so zahlreich und kompliziert, dass Sie niemals in der Lage wären, sie alle manuell zu befolgen. Deshalb sollten Sie eine JSON-Zeichenkette niemals manuell erstellen, sondern immer eine spezielle Funktion verwenden, json_encode() die jedes Bit der Daten korrekt formatiert.

Und so weiter und so fort ...

El nur Fall, in dem Sie Daten aktiv filtern müssen, ist, wenn Sie vorformatierte Eingaben akzeptieren. Dies ist beispielsweise der Fall, wenn Sie Ihren Benutzern die Möglichkeit geben, HTML-Auszeichnungen zu posten, die Sie auf der Website anzeigen möchten. Sie sollten dies jedoch um jeden Preis vermeiden, denn egal wie gut Sie filtern, es wird immer eine potenzielle Sicherheitslücke sein.

247voto

Andy Lester Punkte 86147

Versuchen Sie nicht, SQL-Injection zu verhindern, indem Sie die Eingabedaten bereinigen.

Stattdessen, die Verwendung von Daten bei der Erstellung Ihres SQL-Codes nicht zulassen . Verwenden Sie Prepared Statements (d.h. die Verwendung von Parametern in einer Vorlagenabfrage), die gebundene Variablen verwenden. Dies ist die einzige Möglichkeit, sich gegen SQL-Injection zu schützen.

Bitte besuchen Sie meine Website http://bobby-tables.com/ für weitere Informationen zur Vermeidung von SQL-Injection.

82voto

Daniel Papasian Punkte 15817

Nein. Sie können Daten nicht allgemein filtern, ohne zu wissen, wofür sie gebraucht werden. Manchmal möchten Sie eine SQL-Abfrage als Eingabe verwenden und manchmal möchten Sie HTML als Eingabe verwenden.

Sie müssen die Eingaben auf einer Whitelist filtern, d. h. sicherstellen, dass die Daten einer bestimmten Spezifikation entsprechen, die Sie erwarten. Dann müssen Sie die Daten vor der Verwendung entschlüsseln, je nach dem Kontext, in dem Sie sie verwenden.

Der Prozess des Escapings von Daten für SQL - zur Verhinderung von SQL-Injection - unterscheidet sich stark von dem Prozess des Escapings von Daten für (X)HTML, um XSS zu verhindern.

64voto

SchizoDuckie Punkte 9293

PHP hat das neue schöne filter_input Funktionen, die Sie zum Beispiel von der Suche nach der "ultimativen E-Mail-Regex" befreien, da es jetzt eine eingebaute FILTER_VALIDATE_EMAIL Typ


Meine eigene Filterklasse (die JavaScript verwendet, um fehlerhafte Felder hervorzuheben) kann entweder durch eine Ajax-Anfrage oder einen normalen Formularbeitrag ausgelöst werden. (siehe das Beispiel unten) <? /** * Pork Formvalidator. validiert Felder mit Regexes und kann sie sanitisieren. Verwendet die in PHP filter_var eingebauten Funktionen und zusätzliche Regexe. * @package pork */

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 * 
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;

    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }

    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }

    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       

}

Natürlich, denken Sie daran, dass Sie Ihre Sql-Abfrage escaping zu tun müssen, je nachdem, welche Art von db Sie verwenden (mysql_real_escape_string() ist nutzlos für einen Sql-Server zum Beispiel). Wahrscheinlich möchten Sie dies automatisch in Ihrer entsprechenden Anwendungsschicht wie ein ORM behandeln. Außerdem, wie oben erwähnt: für die Ausgabe in HTML verwenden Sie die anderen php-spezifischen Funktionen wie htmlspecialchars ;)

Um wirklich HTML-Eingaben mit z.B. gestrichenen Klassen und/oder Tags zuzulassen, sollten Sie eines der speziellen xss-Validierungspakete verwenden. SCHREIBEN SIE KEINE EIGENEN REGEXE ZUM PARSEN VON HTML!

52voto

Peter Bailey Punkte 103278

Nein, die gibt es nicht.

Zunächst einmal ist die SQL-Injektion ein Problem der Eingabefilterung und XSS ein Problem der Ausgabeumgehung - Sie würden also nicht einmal diese beiden Operationen gleichzeitig im Lebenszyklus des Codes ausführen.

Grundlegende Faustregeln

  • Für SQL-Abfrage, Parameter binden
  • 使用方法 strip_tags() um unerwünschtes HTML herauszufiltern
  • Alle anderen Ausgaben werden mit htmlspecialchars() und beachten Sie dabei die Parameter 2 und 3.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X