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?
Antworten
Zu viele Anzeigen?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.
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.
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.
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!
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.
- See previous answers
- Weitere Antworten anzeigen
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? :)
76 Stimmen
Ich will damit sagen, dass "Benutzen Sie PDO oder MySQLi" nicht genug Information ist, um Anfängern zu erklären, wie man sie sicher benutzt. Sie und ich wissen, dass vorbereitete Anweisungen wichtig sind, aber ich gehe nicht davon aus, dass jeder, der diese Frage liest, dies weiß. Deshalb habe ich die ausdrücklichen Anweisungen hinzugefügt.
0 Stimmen
Ist also die Verwendung von PDO::prepare() oder PDO::quote() in diesem Fall die richtige Antwort?
37 Stimmen
Die Bemerkung von Andy ist völlig richtig. Ich habe meine mysql-Website vor kurzem auf PDO umgestellt, weil ich dachte, dass ich nun irgendwie vor Injektionsangriffen sicher sei. Erst während des Prozesses stellte ich fest, dass einige meiner SQL-Anweisungen immer noch mit Benutzereingaben erstellt wurden. Das habe ich dann mit Prepared Statements behoben. Einem völligen Neuling ist nicht ganz klar, dass es einen Unterschied gibt, da viele Experten zwar die Verwendung von PDO erwähnen, aber nicht auf die Notwendigkeit von vorbereiteten Anweisungen hinweisen. Die Annahme ist, dass dies offensichtlich ist. Aber nicht für einen Neuling.
0 Stimmen
Ich schätze, diese Antwort funktioniert: stackoverflow.com/questions/60174/
12 Stimmen
@Christian: GhostRider und AndyLester haben Recht. Lasst uns dies eine Lektion in Kommunikation sein. Ich war einmal ein Anfänger und es war scheiße, weil Experten einfach nicht wissen, wie man kommuniziert.
1 Stimmen
Die Bemerkung von Andy ist völlig richtig. Ich habe vor kurzem meine mysql-Website auf PDO umgestellt und dachte, dass ich nun irgendwie vor Injektionsangriffen sicher sei. Erst während des Prozesses stellte ich fest, dass einige meiner SQL-Anweisungen immer noch mit Benutzereingaben erstellt wurden. Das habe ich dann mit vorbereiteten Anweisungen behoben. Für einen absoluten Neuling ist es nicht ganz klar, dass es einen Unterschied gibt, da viele Experten die Verwendung von PDO erwähnen, aber nicht näher darauf eingehen.