JeuWeb - Crée ton jeu par navigateur
Discussion en ligne PHP/Mysql Securisé - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Discussion en ligne PHP/Mysql Securisé (/showthread.php?tid=7639)



Discussion en ligne PHP/Mysql Securisé - didawin - 04-05-2016

Bonjour à tous,

Dans le cadre de mon projet, je souhaite avoir dans mon footer, une barre de discussion "instantané" sécurisée.
J'arrive à afficher les messages (stockés en BDD) et à en envoyer,  fonctionnellement : la solution fonctionne.
Cependant je ne m'y connais pas trop en terme de sécurité sur les injections sql.
(Vous pouvez tester sur le jeu -> Haishin.fr mais il faut être connecté pour envoyer un message.)

Je veux dire par la que les messages sont envoyé par formulaire GET, sur une page php, qui envoie les données à la BDD tels qu'il les reçois.
Lors de l'affichage je fait un bête select, puis met le résultat dans un tableau.
La seule chose que j'ai implémenté pour le moment est un pattern HTML5 pour interdire les caractères spéciaux. (qui peut être supprimé par n'importe qui au moment de l'envoi)
Ma question est donc, si un joueur écrit comme message un truc du genre 'INSERT INTO......', est ce que cette requête risque d'être exécutée lors de l'affichage ? 

Code PHP :
<?php 
$query
= sprintf("SELECT DES MESSAGES");

$result = mysql_query($query);
$message = array();
while(
$row = mysql_fetch_assoc($result)) {
array_push($message,$row);
}

Si oui, est ce qu'un mysql real escape string au moment de l'envoie et réception peut contrer cela ? Si non je ne vois pas quoi faire ?
D'avance merci.

EDIT: l'insertion s'effectue de cette manière :
Code PHP :
<?php 
if (!($stmt = $mysqli->prepare("INSERT INTO discussion(XXXXXXXXX) VALUES (?,?,?,?)"))) {

   echo "Echec de la préparation : (" . $mysqli->errno . ") " . $mysqli->error;
}

EDIT2:
J'ai donc essayé comme message :
0); INSERT INTO discussion(USERNAME,MESSAGE) VALUES ('',''
La deuxième requête ne s'est pas exécute mais la première oui, du coup, est ce correct/sécurisé ? ou est ce un coup de chance et ce n'est pas sécurisé du tout ? :/


RE: Discussion en ligne PHP/Mysql Securisé - Xenos - 04-05-2016

Yop,

Citation :les messages sont envoyé par formulaire GET
Déjà, problème car GET est prévu pour récupérer une ressource et non pour en créer une (et GET est idempotente). En d'autres mots, si je fais une requête GET, rien ne change coté serveur. Si je fais 1x ou 100x une requête GET, j'ai le même résultat. Donc, dans ton cas, passe par un POST et non un GET.

Par exemple, cette "image" te fait envoyer un message, ce qui est complètement anormal (bon, même en POST, on peut faire pareil, mais c'est un peu plus délicat, c'est juste pour la démo):
[Image: SendMessage.php?message=XSS-from-jeuweb!]

D'ailleurs, t'as un pâté dans tes redirections... L'URL http://haishin.fr/SendMessage.php?message=Looping! doit rediriger vers elle-même, ce qui envoie du message en boucle (jusqu'à ce que le navigateur détecte la boucle et stoppe le train).



Sinon, tu peux avoir une injection SQL si ta requête est simplement une concaténation avec les données du POST. Si tu passes par une requête préparée (INSERT INTO ... (...) VALUES (?, ?, ?...)) alors tu ne devrais pas avoir de soucis.

Quand tu envoie les données au client, tu peux avoir une injection aussi (qu'on appelle XSS). Si tu envoies les données au client sous forme HTML en faisant une bête concaténation de chaine ou un bête "echo" (<ul><li><?php echo $myData; ?></li></ul> ou <?php echo '<ul><li>' . $myData . '</li></ul>';?> qui revient au même) alors tu envoies une chaine de caractère au client, qui n'a pas moyen de savoir comment elle a été construire. Du coup, si du code HTML se ballade dans MyData, il sera interprété par le client (XSS). Cela se règle en l'échappant lors de l'envoie des données (<li><?php echo htmlentities($myData); ?></li>, en précisant quand même l'encodage, cf doc). Même principe pour du JSON (avec json_encode()). Même principe pour une sortie XML (avec DomDocument ou SimpleXML). Même principe avec du CSV (là, je ne sais pas comment on échappe ses données).


Pour le reste, ce que MySQL te retourne ne sont que des données, donc si la donnée dans la table est litéralement "DROP DATABASE..." alors MySQL va juste te retourner la chaine de texte "DROP DATABASE..." (il ne l'exécute pas, pourquoi le ferait-il?!). En revanche, tu aurais de gros emm***des si tu re-exécutais ce que MySQL t'as envoyé (façon while ($row = mysql_fetch_assoc($r)) { mysql_query($row); } mais je ne vois pas pourquoi tu aurais à faire cela!).



PS: ton pattern coté client est trop rigide, et n'autorise pas les espaces ni les ponctuations.

PSS: Si j'appelle la page http://haishin.fr/SendMessage.php sans paramètre et en étant connecté, alors une erreur SQL intervient: tu n'as pas vérifié que le message existe (et est non vide par exemple).


RE: Discussion en ligne PHP/Mysql Securisé - didawin - 04-05-2016

Citation :Déjà, problème car GET est prévu pour récupérer une ressource et non pour en créer une (et GET est idempotente). En d'autres mots, si je fais une requête GET, rien ne change coté serveur. Si je fais 1x ou 100x une requête GET, j'ai le même résultat. Donc, dans ton cas, passe par un POST et non un GET.

D'accord je vais modifier cela, c'est vrai que ça me paraît plus logique maintenant d'utiliser POST que GET Big Grin


Citation :Par exemple, cette "image" te fait envoyer un message, ce qui est complètement anormal (bon, même en POST, on peut faire pareil, mais c'est un peu plus délicat, c'est juste pour la démo):
[Image: SendMessage.php?message=XSS-from-jeuweb!]
Oui j'ai vu tes tests, je compte passer en POST et limiter le nombre de message par joueur sur un laps de temps (à déterminer)


Citation :D'ailleurs, t'as un pâté dans tes redirections... L'URL http://haishin.fr/SendMessage.php?message=Looping! doit rediriger vers elle-même, ce qui envoie du message en boucle (jusqu'à ce que le navigateur détecte la boucle et stoppe le train).
Je vais regarder et essayer de comprendre pourquoi ça boucle :/ (même si j'imagine qu'en POST, ce problème n'existeras plus...)



Citation :Sinon, tu peux avoir une injection SQL si ta requête est simplement une concaténation avec les données du POST. Si tu passes par une requête préparée (INSERT INTO ... (...) VALUES (?, ?, ?...)) alors tu ne devrais pas avoir de soucis.

Quand tu envoie les données au client, tu peux avoir une injection aussi (qu'on appelle XSS). Si tu envoies les données au client sous forme HTML en faisant une bête concaténation de chaine ou un bête "echo" (<ul><li><?php echo $myData; ?></li></ul> ou <?php echo '<ul><li>' . $myData . '</li></ul>';?> qui revient au même) alors tu envoies une chaine de caractère au client, qui n'a pas moyen de savoir comment elle a été construire. Du coup, si du code HTML se ballade dans MyData, il sera interprété par le client (XSS). Cela se règle en l'échappant lors de l'envoie des données (<li><?php echo htmlentities($myData); ?></li>, en précisant quand même l'encodage, cf doc). Même principe pour du JSON (avec json_encode()). Même principe pour une sortie XML (avec DomDocument ou SimpleXML). Même principe avec du CSV (là, je ne sais pas comment on échappe ses données).

Merci pour ces explications ! Smile je comprend mieux maintenant pour le premier cas.
Pour le second cas, je ne comprennais pas trop, jusqu'à ce que je vois ton exemple (test barré dans la discussion Wink )
Je comprend maintenant l'effet que ça peux avoir et vais corriger ça (cf:htmlentities)


Citation :Pour le reste, ce que MySQL te retourne ne sont que des données, donc si la donnée dans la table est litéralement "DROP DATABASE..." alors MySQL va juste te retourner la chaine de texte "DROP DATABASE..." (il ne l'exécute pas, pourquoi le ferait-il?!). En revanche, tu aurais de gros emm***des si tu re-exécutais ce que MySQL t'as envoyé (façon while ($row = mysql_fetch_assoc($r)) { mysql_query($row); } mais je ne vois pas pourquoi tu aurais à faire cela!).

Je vois, donc si on récupére un varchar par exemple (avec une requête dedans) au moment de l'afficher sur la page, le serveur ne vas pas l'executer puisqu'il ne fait que l'afficher, idem au niveau de l'envoie car j'ai une requête préparée et non pas juste un SELECT..... FROM ...... where A=$variable; <- Dans ce cas, une injection est possible. J'ai bien compris ? Big Grin

Merci pour ton aide Smile

Citation :PS: ton pattern coté client est trop rigide, et n'autorise pas les espaces ni les ponctuations.
Oupssss :/ je n'ai pas testé ça, je vais corriger Smile

Citation :PSS: Si j'appelle la page http://haishin.fr/SendMessage.php sans paramètre et en étant connecté, alors une erreur SQL intervient: tu n'as pas vérifié que le message existe (et est non vide par exemple).
Effectivement, j'ai ajouté un check sur la connection (sinon redirection vers login.php) mais je n'ai pas vérifier ça :/ j'ajoute à ma todo list Wink

EDIT: Je dois aussi modifier l'heure du serveur :/


RE: Discussion en ligne PHP/Mysql Securisé - Xenos - 04-05-2016

Pour le soucis du "je peux te faire poster un message si tu visite ma page web" (aka CSRF), tu peux passer par un système de token ({stocké en session ou dans un cookie} et {dans un input hidden du formulaire d'envoie de message}).

Pour l'injection SQL, t'as bien compris.