Connaissance de base de la sécurité du site

Bonjour, Habr!

La sécurité est une affaire sérieuse. Et souvent, les problèmes dans ce domaine surviennent de manière inattendue et ont des conséquences extrêmement désagréables. Par conséquent, la connaissance de cette rubrique est extrêmement importante pour chaque développeur Web.

Je ferai une réservation tout de suite - je suis loin d'être un pro, mais je m'y efforce. Par conséquent, je serai heureux de critiquer, mais seulement objectif. Ce matériel est destiné aux débutants qui souhaitent accroître leur professionnalisme et leur valeur en tant que spécialiste.

Et pourtant, je montre l'implémentation de code la plus simple possible. Je connais les exceptions, je connais ORM, la protection fournie dans les frameworks. Mon objectif est de le montrer clairement pour que tout le monde comprenne.

Et donc, il est temps de terminer l'introduction et de commencer à pratiquer.

Le chemin de la mise en œuvre d'un novice à un résultat sain


Je n'ai pas l'habitude de travailler avec la théorie. Mon âme aspire à la pratique. Par conséquent, en parlant du sujet de la sécurité, nous considérerons presque tous les types d'attaques d'un point de vue pratique - comment mettre en œuvre et comment vous protéger. Oui, vous pouvez dire qu'enseigner le piratage n'est pas bon, mais, ne sachant pas comment l'attaque se produit, nous ne pouvons pas construire une défense compétente.

Xss


D'accord, le premier type d'attaque est XSS. Oui, le bon vieux XSS dont tout le monde a entendu parler. XSS (Cross Site Scripting) est un type d'attaque qui cible les visiteurs du site. Comment cela se produit: à travers le champ de saisie, l'attaquant écrit du code malveillant qui entre dans la base de données et fait son travail. Habituellement, de cette manière, les cookies sont volés aux utilisateurs, ce qui leur permet de se connecter à leurs comptes sans mot de passe et sans connexion.

Nous mettons en œuvre un exemple plus inoffensif.

Notre développeur a créé un formulaire simple pour ajouter des commentaires:

Fichier Index.php
<?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> 

Le code est très simple et n'a besoin d'aucune explication.

Il y a un intrus - John. John s'est ennuyé et est tombé sur le site de notre développeur.
John écrit le message suivant sur le formulaire:

 <script>document.body.style.backgroundColor = "#000";</script> 

Et maintenant, tous les utilisateurs du site ont un fond noir. John est satisfait et le développeur a acquis de l'expérience et des réprimandes.

Qu'est-il arrivé?

John a ajouté un commentaire avec du code JavaScript. Lors de la sortie de données sur une page, un commentaire texte est converti en code html. Le code html, voyant l'utilisation de la balise de script, l'a ajouté au balisage et l'interpréteur a déjà exécuté le code JavaScript. Autrement dit, John vient d'ajouter son morceau de code js au code de site existant.

Comment allons-nous y remédier?

Pour corriger ce malentendu, la fonction htmlspecialcars a été créée. L'essence de son travail est qu'elle remplace les caractères tels que les guillemets et les crochets par des caractères spéciaux. Par exemple, le caractère "<" sera remplacé par son code de caractère correspondant. Avec cette fonction, nous traitons les données du formulaire et maintenant le code js de John ne peut plus nuire à notre site. Bien sûr, si c'est le seul formulaire sur le site.

Les changements dans le code ressembleront à ceci:

Fichier Index.php
 <?php if ($_POST) { $username = htmlspecialchars(trim($_POST['name'])); $comment = htmlspecialchars(trim($_POST['comment'])); /// } 


Injection SQL


Un autre des types d'attaques les plus courants, qui ont déjà commencé à être oubliés. Ils l'oublient car il existe des requêtes et des cadres préparés.

Nous parlerons des demandes préparées.

Quelle est l'essence de l'attaque: l'attaquant entre une partie de la requête SQL dans le champ de saisie et soumet le formulaire. Pendant l'exécution de la requête, les données reçues sont ajoutées à la base de données. Mais puisque le code contient le code, lors de l'ajout d'enregistrements, il modifie la logique de notre script.

D'accord, simulez une situation. Notre développeur a protégé le formulaire des attaques XSS. Et John continue d'utiliser ses connaissances, soulignant les défauts du malheureux développeur.

Autrement dit, nous continuerons de travailler avec le même formulaire pour ajouter des commentaires.
Faisons quelques changements:

1) Modération préalable des commentaires.

L'essentiel est que seuls les commentaires approuvés par le modérateur seront affichés sur la page. Nous mettons en œuvre la pré-modération dans sa forme la plus simple afin de ne pas être distrait de la partie principale de l'article.

Pour implémenter l'idée, ajoutez le champ «is_moderate» au tableau avec commentaires, qui prendra deux valeurs - «1» (afficher le commentaire) ou «0» (ne pas afficher). Par défaut, bien sûr, «0».

2) Modifiez la demande.

C'est pour plus de clarté. Laissez la demande d'ajout de commentaires ressembler à ceci:

 "INSERT INTO `comments` SET `username`='$username', `message`='$comment'" 

Maintenant, le code du formulaire est le suivant:

Fichier Index.php
 <?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 ' !'; } } ?> 


D'accord, John se connecte au site et, voyant que les commentaires ont commencé à être modérés, il a décidé de se moquer du développeur. De plus, la forme d'attaques XSS se reflète maintenant avec succès et John a déjà été privé de la possibilité de s'amuser. Il laisse un commentaire de ce type: "LOL", is_moderate = "1" et contourne la modération.

Pourquoi?

Lorsque vous remplacez le commentaire de John dans notre requête, les guillemets se brisent. Autrement dit, comme mentionné ci-dessus, John a pu exécuter du code SQL arbitraire.

Lors de l'exécution d'une demande avec le commentaire de John, la demande est la suivante:

 "INSERT INTO `comments` SET `username`='John', `message`='LOL', `is_moderate`='1'" 

De plus, SQL Injection peut être implémenté non seulement lors de la soumission d'un formulaire. Cela peut également se produire lors de la réception d'enregistrements par leur identifiant, du traitement du formulaire de recherche et d'autres situations moins évidentes.

Comment y remédier?

La méthode pour résoudre le problème est connue depuis longtemps - les requêtes préparées. Les demandes préparées sont des demandes qui subissent un traitement spécial avant d'être exécutées. Le traitement consiste à échapper des guillemets supplémentaires. Vous devez avoir entendu parler d'une telle fonctionnalité. En PHP, il est implémenté comme ceci: "\ '".

La solution la plus populaire est l'AOP. PDO est une interface pour travailler avec une base de données. Ce qui a une interface assez pratique. Utilisez-le à bon escient.

PDO offre la possibilité d'utiliser des masques et des espaces réservés pour implémenter des requêtes préparées.

Ensuite, notre demande lors de l'utilisation de masques ressemblera à ceci:

Fichier Index.php
 <?php $query = $pdo->prepare("INSERT INTO `comments` SET `username`=:username, `message`=:comment"); $params = ['username' => $username,'comment' => $comment]; $query->execute($params); 


Et lorsque vous utilisez des espaces réservés comme celui-ci:

Fichier Index.php
 <?php $query = $pdo->prepare("INSERT INTO `comments` SET `username`=?, `message`=?"); $params = [$username,$comment]; $query->execute($params); 


Maintenant, l'attaque de John n'est plus pertinente. Du moins pour ce formulaire.

Soit dit en passant, notre modération, même sous cette forme, protège déjà contre un autre type d'attaque - le SPAM. Nous avons tous entendu parler de lui. Le SPAM est l'envoi de tout message dans lequel, à l'aide de connaissances en ingénierie sociale, les attaquants exécutent leurs attaques. Maintenant, le seul qui sera attaqué est le modérateur. Et puis, s'il n'est pas aussi stupide, il supprimera les ordures de la base de données, ou s'il est paresseux, il rejettera la publication et c'est tout.

Attaque CSRF


CSRF - Falsification de demande intersite. C'est dangereux car peu de gens le savent. Bien que cela soit assez simple.

Comment cela se produit: un attaquant d'un autre site crée un formulaire et oblige la victime à suivre ce formulaire. Autrement dit, une demande POST est envoyée. Ainsi, une requête HTTP est truquée et une action malveillante est effectuée sur le site de la victime.

Par exemple, un attaquant pourrait envoyer à votre ami une lettre en VK en votre nom, mais vous ne le saurez pas.

Cela semble un peu déroutant. Je propose d'envisager dans la pratique.

Notre développeur a fait le formulaire, il est bien fait. Elle sait déjà se défendre contre XSS, l'injection SQL et maintient la pression du Spam. Cela ressemble à ceci:

Le fichier index.php sur le site du développeur
 <!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> 


Mais John n'est pas si simple. Il reçoit le code du formulaire (simplement à partir du code source du site dans le navigateur) et ajoute le formulaire à son site.

Fichier index.php de John
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> <style> form input[type=submit]{ padding: 15px; font-size: 20px; color: #fff; background: #f00; cursor: pointer; } </style> </head> <body> <form action="localhost/note/action.php" method="POST"> <input type="hidden" name="username" value="lol"> <input type="hidden" name="message" value="  !   !"> <input type="submit" value=" !"> </form> </body> </html> 


Veuillez noter que sur le site Web de John, le lieu de traitement du formulaire est un fichier provenant du site Web de notre développeur. Alors maintenant, tout utilisateur qui clique sur le bouton n'enverra pas de bons commentaires.

Il s'agit d'une attaque CSRF. Dans la version la plus simple, bien sûr.

Le développeur a encore des problèmes ...

Comment corriger une vulnérabilité?

À un moment donné, le développeur recherchera ce qu'est csrf et la logique de protection sera la suivante: pour la protection, vous devez créer un jeton csrf (un ensemble de lettres et de chiffres) et le suspendre au formulaire. Vous devez également fixer le même jeton à l'utilisateur (par exemple, via une session). Et puis, lors du traitement du formulaire, comparez ces jetons. S'ils correspondent, nous pouvons ajouter un commentaire.

Nous implémentons ceci:

Le fichier index.php sur le site du développeur
 <?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> 


Fichier action.php
 <?php session_start(); if ($_POST) { if ($_SESSION['token'] == $_POST['csrf_token']) { echo ' !'; } else { echo '!'; } } 


Mots de passe Brute Force et Publick


Peut-être le type d'attaque le plus célèbre. Il l'a entendu presque tous les premiers films sur les pirates et autres.

Quel est le point: il y a un formulaire d'autorisation dans le panneau d'administration du site. Nous avons besoin d'un nom d'utilisateur et d'un mot de passe que John ne connaît pas. Mais il a un fichier avec des noms d'utilisateur et des mots de passe populaires. Et il court joyeusement pour les essayer sur notre site.

À quoi un développeur peut-il s'opposer? Par exemple, une restriction sur le nombre de tentatives d'autorisation dans une certaine période de temps.

Laissez la version initiale du formulaire d'autorisation ressembler à ceci:

Fichier Index.php
 <?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> 


Comment y remédier: l'option la plus simple, comme déjà mentionné, est de limiter le nombre de tentatives d'autorisation par période de temps.

Pour ce faire, lorsque nous essayons de nous connecter, nous ajouterons la valeur actuelle du temps à l'utilisateur dans les cookies. Et maintenant, lorsque vous essayez de vous connecter, nous verrons que l'utilisateur ne peut pas se connecter plus d'une fois en 5 secondes. De plus, si le mot de passe ou la connexion ne sont pas entrés correctement, nous augmenterons le délai d'attente jusqu'à la prochaine tentative de 5 secondes.

Fichier Index.php
 <?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); } } ?> 


Backtrace


Backtrace est un moyen d'attaquer via des messages d'erreur système. C'est à la fois MySQL et PHP.

Par exemple, John a entré une URL incorrecte et a reçu une erreur indiquant qu'il n'y a pas d'enregistrement dans la base de données avec un tel identifiant (si l'enregistrement est reçu via l'identifiant de la barre d'adresse - site.ru/article?id=12). Il existe même des soi-disant «dorks» - des schémas spécifiques d'adresses de sites Web, à la saisie desquels l'utilisateur voit des erreurs. Et cela ouvre la possibilité à John d'utiliser le bot pour parcourir cette liste d'adresses et essayer de trouver cette vulnérabilité sur votre site.

Comment y remédier? Dans ce cas, nous nous passerons d'exemples, car le problème est résolu simplement en fermant la sortie d'erreur. Habituellement, cela est mis en œuvre par l'hébergement, vous fournissant dans le panneau d'administration pour écrire des journaux dans un fichier séparé, mais il ne sera pas superflu de limiter vous-même la sortie des erreurs.

Cela peut être fait en utilisant la fonction error_reporting ().

Parmi les arguments qu'il faut, il y a: E_ERROR, E_WARNING, E_PARSE, E_NOTICE, E_ALL. Les noms parlent d'eux-mêmes.

Par exemple, si vous utilisez error_reporting (E_NOTICE), toutes les erreurs seront masquées, à l'exception des erreurs de type Avis (avertissements, par exemple, qu'il n'y a pas de données dans le tableau $ _POST).
Pour désactiver la sortie de toutes les erreurs (dont nous avons réellement besoin), vous devez utiliser cette fonction comme suit: error_reporting (0)

Erreurs logiques


Les erreurs logiques sont parmi les pires. Parce que ce sont des erreurs de négligence. Ils apparaissent de façon inattendue, et parfois nous ne savons même pas où est la racine du problème. Ce sont des erreurs dans la logique du site.

Eh bien, par exemple, vous pourriez oublier de vérifier la présence de données d'autorisation dans la session et de cookies pour l'une des pages du panneau d'administration. Ils ont donc ouvert l'accès à cette page à tout utilisateur.

Dans ce cas, une seule chose vous sauvera - lors de l'écriture du code du programme, pensez à comment le pirater.

DDOS


DOS est un type d'attaque d'une technique, en particulier d'un ordinateur. L'attaque vise à désactiver la machine en raison d'une surcharge. DDOS ne diffère que par le fait qu'un plus grand nombre d'ordinateurs sont impliqués dans l'attaque.

Autrement dit, John appelle ses amis et ils commencent ensemble à envoyer des demandes au site. Qu'est-ce que le botnet a à voir avec ça? Un botnet, c'est beaucoup d'ordinateurs infectés, la particularité est qu'un attaquant peut contrôler son travail dans une certaine mesure (processus de démarrage, etc.). Ils envoient tellement de requêtes que le serveur ne peut pas supporter la charge et, au mieux, commence à travailler très lentement, ou refuse pour une durée indéterminée.

La protection contre de telles attaques est assurée soit par les hôtes eux-mêmes, soit par des services spéciaux comme Cloudflare.

Fonctionnement de la protection: le service Cloudflare vous fournit ses propres serveurs DNS via lesquels le trafic passera. Là, il est filtré, passant par des algorithmes connus uniquement des propriétaires et des développeurs du service. Et après cela, l'utilisateur accède à votre site. Oui, bien sûr, il y a le fonctionnement du serveur, la génération de pages et tout le reste, mais nous n'en parlons pas maintenant.
De plus, en parlant de DDOS, on ne peut manquer de mentionner le blocage d'adresse IP que Cloudflare fournit également.

Oui, tout cela ne donnera pas une garantie de protection à 100%, mais augmentera parfois les chances de votre site de rester à flot au moment de l'attaque.

MITM


Man In The Middle est un type d'attaque lorsqu'un attaquant intercepte vos paquets et les usurpe. Nous avons tous entendu que les données sont transmises sur le réseau par paquets. Ainsi, lorsque vous utilisez le protocole http, les données sont transmises sous la forme habituelle et non cryptée.

Par exemple, vous écrivez «bonjour» à un ami et il reçoit «envoie-moi de l'argent, voici le portefeuille».

C'est pour résoudre ce problème que le protocole https a été créé. Lors de son utilisation, les données seront cryptées et John ne pourra rien faire avec le trafic reçu.
Et pour obtenir le protocole https du site, vous devez obtenir un certificat SSL. Eh bien, ou TLS. En général, TLS est essentiellement un récepteur SSL, car il est basé sur SSL 3.0. Il n'y a pas de différences significatives dans leur travail.

Le certificat SSL fournit le même Cloudflare, et gratuitement.

Porte dérobée


Un peu plus de théorie. Pour ce type d'attaque peut être mis en œuvre de plusieurs façons et il vous suffit de saisir l'essence. La porte dérobée est un type d'attaque secrète dans laquelle le script lui-même fait quelque chose en arrière-plan. Le plus souvent, ce sont des plugins ou des thèmes WordPress téléchargés depuis un torrent. Le plugin / thème lui-même fonctionnera assez bien, mais une certaine partie du script ajoutée au code du plugin / thème fera secrètement quelque chose. Le même SPAM, par exemple. C'est la raison de tous les avertissements concernant le caractère indésirable du téléchargement de fichiers à partir d'un torrent.

En conclusion


Oui, bien sûr, ce n'est pas toute la gamme des attaques. Mais cette connaissance augmentera déjà la sécurité de vos projets.

Source: https://habr.com/ru/post/fr451420/


All Articles