Hallo Habr!
Sicherheit ist eine ernste Angelegenheit. Und oft treten Probleme in diesem Bereich unerwartet auf und haben äußerst unangenehme Folgen. Daher ist das Wissen in diesem Thema für jeden Webentwickler äußerst wichtig.
Ich werde sofort eine Reservierung vornehmen - ich bin weit entfernt von einem Profi, aber ich bemühe mich darum. Daher werde ich gerne kritisieren, aber nur objektiv. Dieses Material ist für Anfänger gedacht, die ihre Professionalität und ihren Wert als Spezialist steigern möchten.
Und doch zeige ich die einfachste mögliche Code-Implementierung. Ich kenne Ausnahmen, ich kenne ORM, den bereitgestellten Schutz in Frameworks. Mein Ziel ist es, es klar zu zeigen, damit jeder es versteht.
Und so ist es Zeit, mit der Einführung fertig zu werden und mit dem Üben zu beginnen.
Der Weg von der Implementierung eines Anfängers zu einem vernünftigen Ergebnis
Ich bin es nicht gewohnt, mit Theorie zu arbeiten. Meine Seele sehnt sich nach Übung. Wenn wir über das Thema Sicherheit sprechen, werden wir daher fast alle Arten von Angriffen aus praktischer Sicht betrachten - wie man sie implementiert und wie man sich verteidigt. Ja, Sie können sagen, dass es nicht gut ist, Hacking zu lehren, aber ohne zu wissen, wie der Angriff abläuft, können wir keine kompetente Verteidigung aufbauen.
Xss
Okay, die erste Art von Angriff ist XSS. Ja, das gute alte XSS, von dem jeder gehört hat. XSS (Cross Site Scripting) ist eine Art von Angriff, der auf Site-Besucher abzielt. So geschieht dies: Über das Eingabefeld schreibt der Angreifer schädlichen Code, der in die Datenbank eingeht und seine Aufgabe erfüllt. In der Regel werden auf diese Weise Cookies von Benutzern gestohlen, sodass diese sich ohne Kennwort und ohne Anmeldung bei ihren Konten anmelden können.
Wir implementieren ein harmloseres Beispiel.
Unser Entwickler hat ein einfaches Formular zum Hinzufügen von Kommentaren erstellt:
Index.php Datei<?php $opt = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); $query = $pdo->prepare("SELECT * FROM `comments`"); $query->execute(); $comments = $query->fetchAll(); if ($_POST) { $username = trim($_POST['name']); $comment = trim($_POST['comment']); $query = $pdo->prepare("INSERT INTO `comments` (`username`,`message`) VALUES ('$username', '$comment')"); $query->execute(); if ($query) { echo ' !'; header("Location: index.php"); } else { echo ' !'; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>XSS</title> </head> <body> <form method="POST" class="addComment"> <input type="text" name="name" placeholder="Username"> <textarea name="comment"></textarea> <input type="submit" value=" "> </form> <div class="h2"></div> <div class="comments"> <?php if ($comments): foreach ($comments as $comment):?> <div class="comment"> <div class="comment_username"><?php echo $comment['username'];?></div> <div lass="comment_comment"><?php echo $comment['message'];?></div> </div> <?php endforeach;?> <?php else:?> <div class="no_comments"> </div> <?php endif;?> </div> </body> </html>
Der Code ist sehr einfach und bedarf keiner Erklärung.
Es gibt einen Eindringling - John. John langweilte sich und stolperte über die Seite unseres Entwicklers.
John schreibt die folgende Nachricht auf das Formular:
<script>document.body.style.backgroundColor = "#000";</script>
Und jetzt haben alle Benutzer der Website einen schwarzen Hintergrund. John ist zufrieden und der Entwickler hat Erfahrung und Verweis gesammelt.
Was ist jemals passiert?
John fügte einen Kommentar mit JavaScript-Code hinzu. Bei der Ausgabe von Daten auf eine Seite wird ein Textkommentar in HTML-Code konvertiert. Der HTML-Code, der die Verwendung des Skript-Tags sah, fügte ihn dem Markup hinzu, und der Interpreter führte den JavaScript-Code bereits aus. Das heißt, John hat gerade seinen js-Code zum vorhandenen Site-Code hinzugefügt.
Wie werden wir das beheben?
Um dieses Missverständnis zu korrigieren, wurde die Funktion htmlspecialcars erstellt. Das Wesentliche ihrer Arbeit ist, dass sie Zeichen wie Anführungszeichen und Klammern durch Sonderzeichen ersetzt. Beispielsweise wird das Zeichen "<" durch den entsprechenden Zeichencode ersetzt. Mit dieser Funktion verarbeiten wir die Daten aus dem Formular und jetzt kann Johns js-Code unserer Site keinen Schaden mehr zufügen. Natürlich, wenn dies das einzige Formular auf der Website ist.
Änderungen im Code sehen folgendermaßen aus:
Index.php Datei <?php if ($_POST) { $username = htmlspecialchars(trim($_POST['name'])); $comment = htmlspecialchars(trim($_POST['comment']));
SQL Injection
Eine weitere der häufigsten Arten von Angriffen, die bereits in Vergessenheit geraten sind. Sie vergessen es, weil es vorbereitete Abfragen und Frameworks gibt.
Wir werden über die vorbereiteten Anfragen sprechen.
Was ist das Wesentliche des Angriffs: Der Angreifer gibt einen Teil der SQL-Abfrage in das Eingabefeld ein und sendet das Formular. Während der Ausführung der Abfrage werden die empfangenen Daten zur Datenbank hinzugefügt. Da der Code den Code enthält, ändert er beim Hinzufügen von Datensätzen die Logik unseres Skripts.
Okay, simuliere eine Situation. Unser Entwickler hat das Formular vor XSS-Angriffen geschützt. Und John nutzt weiterhin sein Wissen und weist auf die Mängel des unglücklichen Entwicklers hin.
Das heißt, wir werden weiterhin mit demselben Formular arbeiten, um Kommentare hinzuzufügen.
Nehmen wir einige Änderungen vor:
1) Vormoderation von Kommentaren.
Unter dem Strich werden nur die Kommentare angezeigt, die der Moderator genehmigt hat. Wir implementieren die Vormoderation in ihrer einfachsten Form, um nicht vom Hauptteil des Artikels abgelenkt zu werden.
Um die Idee umzusetzen, fügen Sie der Tabelle das Feld "is_moderate" mit Kommentaren hinzu, die zwei Werte annehmen - "1" (Kommentar anzeigen) oder "0" (nicht anzeigen). Standardmäßig natürlich "0".
2) Ändern Sie die Anfrage.
Dies dient der Klarheit. Lassen Sie die Aufforderung zum Hinzufügen von Kommentaren folgendermaßen aussehen:
"INSERT INTO `comments` SET `username`='$username', `message`='$comment'"
Der Formularcode lautet nun wie folgt:
Index.php Datei <?php $opt = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); $query = $pdo->prepare("SELECT * FROM `comments` WHERE `is_moderate`='1'"); $query->execute(); $comments = $query->fetchAll(); if ($_POST) { $username = htmlspecialchars(trim($_POST['name'])); $comment = htmlspecialchars(trim($_POST['comment'])); $query = $pdo->prepare("INSERT INTO `comments` SET `username`='$username', `message`='$comment'"); $query->execute(); if ($query) { echo ' !'; } else { echo ' !'; } } ?>
Okay, John meldet sich auf der Website an und als sich herausstellte, dass die Kommentare moderiert wurden, beschloss er, den Entwickler zu verspotten. Darüber hinaus spiegelt sich die Form der XSS-Angriffe nun erfolgreich wider und John wurde bereits die Möglichkeit genommen, Spaß zu haben. Er hinterlässt einen Kommentar dieses Typs: "LOL ', is_moderate =' 1" und umgeht die Moderation.
Warum?
Wenn Sie Johns Kommentar in unserer Abfrage ersetzen, werden Anführungszeichen unterbrochen. Das heißt, wie oben erwähnt, konnte John beliebigen SQL-Code ausführen.
Wenn Sie eine Anfrage mit Johns Kommentar ausführen, lautet die Anfrage wie folgt:
"INSERT INTO `comments` SET `username`='John', `message`='LOL', `is_moderate`='1'"
Darüber hinaus kann SQL Injection nicht nur beim Absenden eines Formulars implementiert werden. Dies kann auch passieren, wenn Datensätze anhand ihrer Kennung empfangen, das Suchformular verarbeitet werden und andere nicht so offensichtliche Situationen auftreten.
Wie kann ich das beheben?
Die Methode zur Lösung des Problems ist seit langem bekannt - vorbereitete Abfragen. Vorbereitete Anforderungen sind Anforderungen, die vor ihrer Ausführung einer speziellen Verarbeitung unterzogen werden. Die Verarbeitung besteht darin, zusätzliche Anführungszeichen zu vermeiden. Sie müssen von einer solchen Funktion gehört haben. In PHP wird es folgendermaßen implementiert: "\ '".
Die beliebteste Lösung ist PDO. PDO ist eine Schnittstelle zum Arbeiten mit einer Datenbank. Was hat eine ziemlich bequeme Oberfläche. Verwenden Sie es nur mit Bedacht.
PDO bietet die Möglichkeit, Masken und Platzhalter zum Implementieren vorbereiteter Abfragen zu verwenden.
Dann sieht unsere Anfrage bei der Verwendung von Masken folgendermaßen aus:
Index.php Datei <?php $query = $pdo->prepare("INSERT INTO `comments` SET `username`=:username, `message`=:comment"); $params = ['username' => $username,'comment' => $comment]; $query->execute($params);
Und wenn Sie Platzhalter wie diesen verwenden:
Index.php Datei <?php $query = $pdo->prepare("INSERT INTO `comments` SET `username`=?, `message`=?"); $params = [$username,$comment]; $query->execute($params);
Jetzt ist Johns Angriff nicht mehr relevant. Zumindest für diese Form.
Übrigens schützt unsere Moderation auch in dieser Form bereits vor einer anderen Art von Angriff - SPAM. Wir haben alle von ihm gehört. SPAM ist das Senden von Nachrichten, in denen Angreifer mithilfe von Social-Engineering-Kenntnissen ihre Angriffe ausführen. Jetzt wird nur noch der Moderator angegriffen. Und wenn er nicht so dumm ist, wird er entweder den Müll aus der Datenbank löschen oder, wenn er faul ist, die Veröffentlichung ablehnen und das wars.
CSRF-Angriff
CSRF - Cross-Site Request Forgery. Es ist gefährlich, weil nur wenige Menschen davon wissen. Obwohl dies recht einfach ist.
Wie es passiert: Ein Angreifer von einem anderen Ort fälscht ein Formular und zwingt das Opfer, diesem Formular zu folgen. Das heißt, eine POST-Anfrage wird gesendet. Daher wird eine HTTP-Anfrage gefälscht und auf der Website des Opfers wird eine böswillige Aktion ausgeführt.
Zum Beispiel könnte ein Angreifer Ihrem Freund in Ihrem Namen einen Brief in VK senden, aber Sie werden nichts davon wissen.
Es klingt ein bisschen verwirrend. Ich schlage vor, dies in der Praxis zu berücksichtigen.
Unser Entwickler hat das Formular erstellt, er ist gut gemacht. Sie weiß bereits, wie sie sich gegen XSS und SQL-Injection verteidigen kann und hält den Druck von Spam aufrecht. Es sieht so aus:
Die Datei index.php auf der Website des Entwicklers <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> </head> <body> <form action="action.php" method="POST"> <input type="text" name="username"> <textarea name="message"></textarea> <input type="submit" value=" "> </form> </body> </html>
Aber John ist nicht so einfach. Er erhält den Formularcode (einfach aus dem Quellcode der Site im Browser) und fügt das Formular seiner Site hinzu.
Johns index.php Datei <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> <style> form input[type=submit]{ padding: 15px; font-size: 20px; color:
Bitte beachten Sie, dass auf Johns Website der Ort, an dem das Formular verarbeitet wird, eine Datei von der Website unseres Entwicklers ist. Jetzt sendet jeder Benutzer, der auf die Schaltfläche klickt, keine guten Kommentare.
Dies ist ein CSRF-Angriff. In der einfachsten Version natürlich.
Der Entwickler hat wieder Probleme ...
Wie behebe ich eine Sicherheitsanfälligkeit?
An einem Punkt wird der Entwickler googeln, was csrf ist, und die Schutzlogik lautet wie folgt: Zum Schutz müssen Sie ein csrf-Token (eine Reihe von Buchstaben und Zahlen) erstellen und es an das Formular hängen. Es ist auch erforderlich, dasselbe Token für den Benutzer festzulegen (z. B. über eine Sitzung). Vergleichen Sie dann bei der Verarbeitung des Formulars diese Token. Wenn sie übereinstimmen, können wir einen Kommentar hinzufügen.
Wir setzen dies um:
Die Datei index.php auf der Website des Entwicklers <?php session_start(); $token = ''; if (function_exists('mcrypt_create_iv')) { $token = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); } else { $token = bin2hex(openssl_random_pseudo_bytes(32)); } $_SESSION['token'] = $token; ?> ... <form action="action.php" method="POST"> <input type="text" name="username"> <textarea name="message"></textarea> <input type="hidden" name="csrf_token" value="<?php echo $token;?>"> <input type="submit" value=" "> </form>
Datei action.php <?php session_start(); if ($_POST) { if ($_SESSION['token'] == $_POST['csrf_token']) { echo ' !'; } else { echo '!'; } }
Brute Force- und Publick-Passwörter
Vielleicht die berühmteste Art von Angriff. Er hörte es fast jeden ersten Film über Hacker und dergleichen.
Was ist der Punkt: Es gibt ein Formular für die Autorisierung im Site-Admin-Bereich. Wir brauchen einen Benutzernamen und ein Passwort, die John nicht kennt. Aber er hat eine Datei mit beliebten Benutzernamen und Passwörtern. Und er rennt freudig los, um sie auf unserer Seite auszuprobieren.
Was kann ein Entwickler ablehnen? Zum Beispiel eine Beschränkung der Anzahl von Autorisierungsversuchen in einem bestimmten Zeitraum.
Lassen Sie die ursprüngliche Version des Autorisierungsformulars folgendermaßen aussehen:
Index.php Datei <?php $opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); if (isset($_POST['autoriz'])) { $username = htmlspecialchars(trim($_POST['login'])); $password = htmlspecialchars(trim($_POST['password'])); $query = $pdo->prepare("SELECT * FROM `users` WHERE `username`=:username AND `password`=:password"); $query->execute(['username' => $username,'password' => $password]); $find_user = $query->fetchAll(); if ($find_user) { echo ' !'; } else { echo ' !'; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Brute Force Public Passwords</title> </head> <body> <form method="POST"> <input type="text" name="login" placeholder="Login"> <input type="password" name="password" placeholder="Password"> <input type="submit" value="" name="autorize"> </form> </body> </html>
So beheben Sie das Problem: Wie bereits erwähnt, besteht die einfachste Möglichkeit darin, die Anzahl der Autorisierungsversuche pro Zeitraum zu begrenzen.
Wenn wir versuchen, uns anzumelden, fügen wir dem Benutzer in Cookies den aktuellen Zeitwert hinzu. Und jetzt, wenn wir versuchen, uns anzumelden, werden wir sehen, dass sich der Benutzer nicht mehr als einmal in 5 Sekunden anmelden kann. Wenn das Passwort oder der Login nicht korrekt eingegeben wurde, erhöhen wir das Timeout bis zum nächsten Versuch um 5 Sekunden.
Index.php Datei <?php $count_next_minit = $_COOKIE['count_try'] ? $_COOKIE['count_try'] : 1; $seconds_to_new_try = 5; $opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); if (isset($_POST['autorize'])) { if ($_COOKIE['last_try']) { if ($_COOKIE['last_try'] < time() - $seconds_to_new_try * $count_next_minit) { $username = htmlspecialchars(trim($_POST['login'])); $password = htmlspecialchars(trim($_POST['password'])); $query = $pdo->prepare("SELECT * FROM `users` WHERE `username`=:username AND `password`=:password"); $query->execute(['username'=>$username,'password'=>$password]); $find_user = $query->fetchAll(); setcookie('last_try', time(), time() + 3600); if ($_COOKIE['count_try']) { $old_value = (int)$_COOKIE['count_try']; setcookie('count_try', $old_value + 1, time() + 3600); } else { setcookie('count_try', 1, time() + 3600); } if ($find_user) { var_dump(' !'); } else { var_dump(' !'); } }else{ var_dump(' ! ' . $seconds_to_new_try * $count_next_minit . ' '); } }else{ setcookie('last_try', time(), time() + 3600); } } ?>
Rückverfolgung
Backtrace ist eine Möglichkeit, über Systemfehlermeldungen anzugreifen. Dies ist sowohl MySQL als auch PHP.
Zum Beispiel hat John eine falsche URL eingegeben und eine Fehlermeldung erhalten, dass in der Datenbank kein Datensatz mit einer solchen ID vorhanden ist (wenn der Datensatz über die ID aus der Adressleiste empfangen wird - site.ru/article?id=12). Es gibt sogar sogenannte "Dorks" - spezifische Muster von Website-Adressen, bei deren Eingabe der Benutzer Fehler sieht. Dies eröffnet John die Möglichkeit, mit dem Bot diese Adressliste zu durchsuchen und zu versuchen, diese Sicherheitsanfälligkeit auf Ihrer Website zu finden.
Wie kann ich das beheben? In diesem Fall werden wir auf Beispiele verzichten, da das Problem einfach durch Schließen der Fehlerausgabe gelöst wird. Normalerweise wird dies durch Hosting implementiert, sodass Sie im Admin-Bereich Protokolle in eine separate Datei schreiben können. Es ist jedoch nicht überflüssig, die Ausgabe von Fehlern selbst einzuschränken.
Dies kann mit der Funktion error_reporting () erfolgen.
Zu den Argumenten gehören: E_ERROR, E_WARNING, E_PARSE, E_NOTICE, E_ALL. Die Namen sprechen für sich.
Wenn Sie beispielsweise error_reporting (E_NOTICE) verwenden, werden alle Fehler ausgeblendet, mit Ausnahme von Fehlern vom Typ Notice (Warnungen, z. B., dass das Array $ _POST keine Daten enthält).
Um die Ausgabe aller Fehler zu deaktivieren (die wir tatsächlich benötigen), müssen Sie diese Funktion wie folgt verwenden: error_reporting (0)
Logische Fehler
Logische Fehler gehören zu den schlimmsten. Weil dies Fehler durch Nachlässigkeit sind. Sie tauchen unerwartet auf und manchmal wissen wir nicht einmal, wo die Ursache des Problems liegt. Dies sind Fehler in der Logik der Site.
Sie könnten beispielsweise vergessen, das Vorhandensein von Autorisierungsdaten in der Sitzung und von Cookies für eine der Seiten des Admin-Panels zu überprüfen. Daher haben sie jedem Benutzer den Zugriff auf diese Seite geöffnet.
In diesem Fall werden Sie nur eines retten: Denken Sie beim Schreiben von Programmcode darüber nach, wie Sie ihn hacken können.
DDOS
DOS ist eine Art Angriff auf eine Technik, insbesondere einen Computer. Der Angriff zielt darauf ab, die Maschine aufgrund von Überlastung zu deaktivieren. DDOS unterscheidet sich nur dadurch, dass eine größere Anzahl von Computern an dem Angriff beteiligt ist.
Das heißt, John ruft seine Freunde an und sie beginnen gemeinsam, Anfragen an die Site zu senden. Was hat das Botnetz damit zu tun? Ein Botnetz besteht aus vielen infizierten Computern. Die Besonderheit ist, dass ein Angreifer seine Arbeit bis zu einem gewissen Grad kontrollieren kann (Prozesse starten usw.). Sie senden so viele Anfragen, dass der Server der Last nicht standhalten kann und bestenfalls sehr langsam zu arbeiten beginnt oder sich für eine nicht festgelegte Zeitspanne weigert.
Der Schutz vor solchen Angriffen wird entweder von den Hosts selbst oder von speziellen Diensten wie Cloudflare bereitgestellt.
Funktionsweise des Schutzes: Der Cloudflare-Dienst stellt Ihnen eigene DNS-Server zur Verfügung, über die der Datenverkehr geleitet wird. Dort wird es gefiltert und durch Algorithmen geleitet, die nur den Eigentümern und Entwicklern des Dienstes bekannt sind. Danach gelangt der Benutzer auf Ihre Website. Ja, natürlich gibt es Serverbetrieb, Seitengenerierung und alles andere, aber darüber sprechen wir jetzt nicht.
Wenn man von DDOS spricht, kann man auch die IP-Adressblockierung erwähnen, die Cloudflare ebenfalls bereitstellt.
Ja, all dies bietet keine 100% ige Schutzgarantie, erhöht jedoch manchmal die Wahrscheinlichkeit, dass Ihre Website zum Zeitpunkt des Angriffs über Wasser bleibt.
MITM
Man In The Middle ist eine Art Angriff, wenn ein Angreifer Ihre Pakete abfängt und sie fälscht. Wir alle haben gehört, dass Daten in Paketen über das Netzwerk übertragen werden. Bei Verwendung des http-Protokolls werden die Daten also in der üblichen, nicht verschlüsselten Form übertragen.
Zum Beispiel schreiben Sie einem Freund „Hallo“ und er erhält „Sende mir Geld, hier ist die Brieftasche“.
Um dieses Problem zu lösen, wurde das https-Protokoll erstellt. Bei der Verwendung werden die Daten verschlüsselt und John kann mit dem empfangenen Datenverkehr nichts anfangen.
Um das https-Protokoll für die Site zu erhalten, benötigen Sie ein SSL-Zertifikat. Nun, oder TLS. Im Allgemeinen ist TLS im Wesentlichen ein SSL-Empfänger, da es auf SSL 3.0 basiert. Es gibt keine signifikanten Unterschiede in ihrer Arbeit.
Das SSL-Zertifikat bietet dieselbe Cloudflare kostenlos.
Hintertür
Ein bisschen mehr Theorie. Für diese Art von Angriff kann auf viele Arten implementiert werden und Sie müssen nur die Essenz erfassen. Backdoor ist eine Art verdeckter Angriff, bei dem das Skript selbst etwas im Hintergrund ausführt. Meistens sind dies Plugins oder WordPress-Themes, die von einem Torrent heruntergeladen wurden. Das Plugin / Theme selbst wird ziemlich gut funktionieren, aber ein bestimmter Teil des Skripts, der dem Code des Plugins / Themes hinzugefügt wurde, wird heimlich etwas tun. Zum Beispiel der gleiche SPAM. Dies ist der Grund für alle Warnungen bezüglich der Unerwünschtheit des Herunterladens von Dateien von einem Torrent.
Abschließend
Ja, das ist natürlich nicht die gesamte Bandbreite der Angriffe. Dieses Wissen erhöht jedoch bereits die Sicherheit Ihrer Projekte.