web-dev-qa-db-fra.com

Comment limiter l'impact et réduire le risque d'injection SQL pour un site Web existant?

Notre site Web est basé à 100% sur l'API (avec un client SPA). Récemment, un pirate a réussi à obtenir le mot de passe de notre administrateur (haché avec SHA-256) via l'injection SQL (et le craquage pwd) comme ceci:

https://example.com/api/products?userId=3645&type=sqlinject here

Ce n'est qu'un exemple, mais il est mortel pour nous. Heureusement, c'était un hacker sympa et il vient de nous envoyer un mail à propos du hack. Heureusement pour un site Web soumis à des attaques constantes tout au long de ses 5 ans d'existence.

Oui, nous savons que nous devons vérifier les entrées des utilisateurs partout et formater/échapper/préparer correctement la déclaration avant d'envoyer des données à MySQL. Nous le savons et nous le réparons. Mais nous n'avons pas suffisamment de développeurs/testeurs pour le rendre sûr à 100%.

Supposons maintenant que nous avons 0,1% de chances d'être injectés SQL d'une manière ou d'une autre. Comment pouvons-nous rendre plus difficile pour le pirate de le trouver et limiter les dommages autant que possible?

Ce que nous faisons en ce qui concerne les solutions rapides/mesures supplémentaires:

  1. À la "porte" de sortie partagée par toutes les API, nous n'envoyons plus l'exception PDOException et le message comme auparavant. Mais nous devons encore dire au client qu'une exception s'est produite:

    {type: exception, code: err643, message: 'contact support for err643'}
    

    Je suis conscient que si un hacker voit une exception, il continuera d'essayer ...

  2. L'utilisateur PHP utilise pour se connecter à MySQL ne peut CRUD qu'aux tables. Est-ce assez sûr?

  3. Renommez toutes les tables (nous utilisons l'open source pour que le pirate puisse deviner le nom des tables).

Que devons-nous faire d'autre?

Mise à jour: comme il y a beaucoup de commentaires, je voudrais donner quelques informations secondaires

  1. Tout d'abord, il s'agit de l'application LEGACY, développée par des étudiants, pas de niveau entreprise. L'équipe de développement est introuvable, maintenant seulement 1-2 gars de test de développement hybride gérant des centaines de classes d'accès aux données.
  2. Le mot de passe est haché avec SHA-256, oui ça craint. Et nous le modifions en mode php recommandé.
  3. L'injection SQL a 17 ans. Pourquoi est-elle toujours là?
  4. Les instructions préparées sont-elles 100% sûres contre l'injection SQL?
34
Phung D. An

Ne passez pas beaucoup de temps sur les solutions de contournement ou les demi-corrections. Chaque minute que vous passez à essayer de mettre en œuvre tout ce qui est suggéré ici est une minute que vous auriez pu passer à mettre en œuvre des déclarations préparées. C'est la seule vraie solution.

Si vous avez une vulnérabilité SQLi, l'attaquant finira probablement par gagner quoi que vous fassiez pour essayer de la ralentir. Sachez donc que même si vous pouvez apporter des améliorations, vous jouez à la fin d'une partie à moins que vous ne résolviez la cause première du problème.

Quant à ce que vous avez essayé jusqu'à présent: masquer les messages d'erreur est un bon début. Je vous recommande d'appliquer des autorisations encore plus strictes (voir ci-dessous). C'est discutable si la modification des noms de table aide , mais cela ne fera probablement pas de mal.

Ok, alors qu'en est-il de ces autorisations? Limitez autant que possible l'utilisateur de la base de données, jusqu'au niveau de la table ou même de la colonne. Vous pouvez même créer plusieurs utilisateurs de base de données pour verrouiller encore plus les choses. Par exemple, utilisez uniquement un utilisateur de base de données autorisé à écrire lorsque vous écrivez réellement. Ne vous connectez avec un utilisateur qui a le droit de lire la colonne de mot de passe que lorsque vous avez réellement besoin de lire la colonne de mot de passe. De cette façon, si vous avez une vulnérabilité d'injection dans une autre requête, elle ne peut pas être utilisée pour divulguer des mots de passe.

Une autre option consiste à utiliser un pare-feu d'application Web (WAF), tel que mod_security pour Apache. Vous pouvez le configurer pour bloquer les demandes qui semblent suspectes. Cependant, aucun WAF n'est parfait. Et il faut du temps et de l'énergie pour le configurer. Vous feriez probablement mieux d'utiliser ce temps pour implémenter les instructions préparées.

Encore une fois, ne laissez rien de tout cela vous attirer dans un faux sentiment de sécurité. Il n'y a aucune substitution à la résolution de la cause première du problème.

80
Anders

La seule façon correcte consiste à utiliser des instructions préparées.

  1. Si vous déguisez des messages d'erreur, c'est un peu plus difficile, mais cela n'arrêtera pas les attaquants.
  2. Vous pouvez restreindre les droits, mais tous les droits accordés à l'utilisateur pourraient être acquis par l'attaquant. Il est également possible d'exécuter des commandes Shell à partir d'une injection SQL dans certains cas.
  3. Renommer des tables ne vous aidera pas. L'outil sqlmap forcera brutalement les noms de table pour vous char par char. Cela ne prend pas beaucoup de temps.

Vous pouvez également limiter le nombre d'appels, pour rendre plus difficile pour les attaquants ou faire des alertes sur les appels suspects et arrêter ce manuel, mais la seule façon correcte de résoudre ce problème consiste à utiliser des instructions préparées. Accédez simplement à votre code source et jetez un œil aux instructions SQL avec un contenu dynamique et remplacez-les.

65
trietend

Nous le savons, mais nous n'avons pas suffisamment de développeurs/testeurs pour le rendre sûr à 100%.

C'est le vrai problème. Tant que vous n'aurez pas embauché plus de personnes ou réattribué des priorités, vous n'êtes pas en sécurité. L'arrêt de 99,9% des attaquants signifie que vos données ont été compromises. Les solutions de pansement sont une chose mauvaise, et elles ne fonctionnent pas fonctionnent. Ne perdez pas de temps à refactoriser votre modèle d'accès SQL alors que vous pourriez résoudre des problèmes réels.

Quant à la solution de code, l'utilisation d'instructions préparées, comme le suggèrent de nombreuses personnes ici, est le moyen le plus simple d'utiliser SQL en toute sécurité.

C'est beaucoup plus facile que d'essayer d'utiliser php pour nettoyer les arguments vous-même, et cela coûte beaucoup moins de temps. Il suffit de grep votre base de code entière pour tout ce qui concerne SQL et de le corriger.

25
Gloweye

Purgez pour toujours tout SQL de votre code

La meilleure solution à ce problème consiste à supprimer tout le code SQL en tant que chaînes littérales de votre code et à ne plus jamais le réintroduire. À la place, utilisez ORM des logiciels comme Hibernate ou Entity Framework. De toute façon, ce logiciel est beaucoup plus agréable à utiliser que le SQL brut et paramètre automatiquement votre SQL lorsqu'il finit par aller dans la base de données. Si cela vous est possible, ayez cette politique.

Si vous n'avez pas le temps d'apprendre et de mettre en œuvre ces packages, ou si vous ne pouvez pas les utiliser pour une autre raison, la réponse de trietend est la meilleure chose à faire, en utilisant déclarations préparées . Cela vous permettra d'être à l'abri de l'injection SQL. C'est-à-dire, jusqu'à ce que vous mettiez la pression sur vos développeurs pour qu'ils proposent des solutions rapides, auquel cas ils peuvent à nouveau oublier de paramétrer une seule variable, vous laissant ainsi où vous avez commencé.

Nous le savons, mais nous n'avons pas suffisamment de développeurs/testeurs pour le rendre sûr à 100%.

Ce que vous entendez par cette déclaration n'est pas tout à fait clair, car les déclarations préparées sont triviales dans tous les langages Web populaires (PHP, Python, Java, node.js, etc.) et sont plus ou moins une mesure 100% efficace contre l'injection SQL .

Voulez-vous dire que vous sous-traitez le développement et que vous ne pouvez pas vous permettre d'AQ le code qu'ils écrivent pour vous sous contrat? Vous ne pouvez pas vous permettre de ne pas le faire: vous êtes toujours la partie responsable ici.

Voulez-vous dire que vos développeurs sont trop occupés à implémenter des fonctionnalités pour prendre quelques heures pour mettre à jour la base de code? Tout autre que les déclarations préparées prendra plus de temps et coûtera plus cher (par exemple, ORM, obsfucation, autorisations affinées, etc.).

Voulez-vous dire que vos développeurs ne sont pas suffisamment compétents pour effectuer cette tâche simple? C'est un problème plus important (ou pour être plus précis, l'injection SQL ne devrait pas être votre plus grande préoccupation à ce stade).

Voulez-vous dire que votre base de code est un tel gâchis de logique de spaghetti au format fusil de includes que même les développeurs compétents prendront une éternité pour effectuer ce qui serait autrement une simple tâche de quelques heures? De même, un plus gros problème.

Quelle que soit celle-ci, la solution consiste à utiliser des déclarations préparées, vers hier.

9
Jared Smith

Une fois que le conseil initial "déclaration préparée" a été mis en œuvre, je recommanderais quelques lectures sur l'injection SQL. Demander "comment empêcher l'injection SQL" est une question assez large! La sécurité est un sujet que vous (plutôt, toute votre équipe de développement) devez connaître très bien afin de réduire le risque de compromis. Un trou mine tous vos autres efforts.

Visitez le projet de sécurité des applications Web ouvertes (OWASP) ici: https://www.owasp.org/index.php/SQL_Injection

En particulier le matériel supplémentaire:

  • Aide-mémoire sur la prévention des injections SQL
  • Aide-mémoire sur le paramétrage des requêtes
  • Article de guide sur la façon d'éviter les vulnérabilités d'injection SQL

Et aussi

  • Comment examiner le code pour les vulnérabilités d'injection SQL .
5
Nicolas

Une mesure provisoire serait de se pencher sur un pare-feu d'application Web (WAF) - il existe quelques sociétés qui fournissent cela en tant que service, et quelques fournisseurs de cloud qui proposent également des offres. CloudFlare semble en proposer un, j'ai eu un client qui utilise Incapsula, et je sais que l'offre AWS WAF a des jeux de règles gérés pour l'injection SQL.

La façon dont cela fonctionne est que tout le trafic est envoyé vers/via le WAF (soit en l'incluant sur les serveurs, soit en définissant DNS pour pointer vers le WAF comme vous le feriez pour un CDN), et il recherche des éléments qui ressemblent à des tentatives d'injection SQL dans Les paramètres. Il n'attrapera pas toutes les attaques et peut bloquer le trafic légitime, mais c'est un correctif bandaid disponible.

4
CoderTao

Par "je n'ai pas assez de développeurs", je suppose que vous voulez dire que certaines personnes influentes de votre organisation pensent qu'il y a des priorités plus élevées, donc toutes les ressources dont vous disposez sont consacrées à ces choses. Ainsi, résoudre votre problème de sécurité est en train de perdre d'autres choses dans une analyse coûts-avantages.

Votre tâche ici est de changer d'avis, autant que d'implémenter le correctif. Quantifier ce qui est en jeu - quels seraient les dommages, en termes d'argent et de réputation, d'une autre violation? Il peut y avoir des lois dans votre juridiction qui exigent que vous signaliez les compromissions des données de votre utilisateur. Serait-ce gênant? Peut-il y avoir des reportages dans les médias sur les mauvaises pratiques de votre organisation?

Considérez l'investissement pour fixer la garantie comme une assurance. Rétrospectivement, une fois que vous savez que votre immeuble n'a pas brûlé pendant une période de dix ans, vous pourriez affirmer que vos primes étaient un gaspillage. Mais vous et votre organisation faites payez pour l'assurance, car vous faites face à un avenir incertain. C'est la gestion des risques.

Le risque a deux facteurs: la probabilité et l'impact. La probabilité d'une nouvelle violation est élevée - vous savez qu'elle peut et a été effectuée au moins une fois. Si les conséquences potentielles sont également mauvaises, vous devez augmenter la priorité de correction de vos failles de sécurité.

2
Concrete Gannet

Comme tout le monde l'a mentionné, corrigez le code sur le site.

Vous pouvez créer les correctifs que vous souhaitez sur un mur, mais jusqu'à ce que le mur soit correctement construit, tous les correctifs seront un problème de saignement.

Toute autre suggestion pour nettoyer le code est ce que Linus Torvalds appellerait masterbation.

  • Corrigez votre code (des points d'injection les plus évidents aux derniers paramètres I.E Get) et utilisez les instructions préparées. comment puis-je empêcher l'injection sql dans php

    • Vous pouvez essayer un [~ # ~] waf [~ # ~] (IE ModSecurity ) temporairement jusqu'à ce que vous puissiez pour arranger les choses.

    • Essayez peut-être une sorte d'interdiction de propriété intellectuelle sur les "personnes testant le site" ou de bloquer les requêtes dangereuses.

    • Si vous avez la possibilité de créer un écran de connexion temporaire (sûr) devant jusqu'à ce que cela soit résolu.

    • Essayez un service tiers jusqu'à ce que cela soit résolu, quelque chose comme cloudflare ou quoi que ce soit d'autre.

Les PDO sont disponibles depuis PHP 5.1 publié le 24 novembre 2005 ... Les développeurs doivent comprendre que ce sont des conséquences sur leurs mauvaises habitudes de codage et je pense personnellement qu'il devrait y avoir une responsabilité envers la qualité travail fourni.

En tout cas, les PDO sont faciles à mettre en œuvre. Si vos développeurs ne sont pas en mesure de fournir une meilleure qualité, je suggérerais une purge ...

1
Qndel

En plus de toutes les bonnes réponses à ce jour, si vous voulez résoudre le problème spécifique d'extraction des informations d'identification de l'utilisateur, vous pouvez refactoriser votre base de données pour avoir les mots de passe dans une table séparée avec des droits d'accès stricts, similaires à la façon dont les systèmes Unix les placent dans/etc/shadow.

Pour vérifier les informations d'identification, vous avez besoin d'une procédure stockée qui prend le mot de passe (ou le hachage) entré comme paramètre et renvoie un booléen. En d'autres termes, vous déchargez le travail de vérification des informations d'identification dans la base de données et le hachage réel ne quitte jamais la base de données.

Cela nécessite une refactorisation minimale et résout votre problème immédiat à la racine, au lieu d'une injection SQL spécifique tout en vous laissant vulnérable à la suivante.

0
Tom

La règle la plus importante est:

tilisez des bibliothèques, des outils et des API qui font de l'option sûre l'option la plus simple.

Nacht dit "utilisez simplement un ORM" qui est un sous-ensemble de cette règle.

Les instructions préparées enfreignent cette règle, car elles sont lourdes à utiliser, entraînent une surcharge de code, elles peuvent également être plus lentes que les instructions non préparées en fonction de votre base de données. Ils sont une solution valable, je ne critiquerai personne qui les utilise, mais ils ne sont pas nécessairement la meilleure solution et la plus pratique.

Et .. nous voulons que l'option sûre soit facile et pratique, donc le programmeur l'utilise par intérêt personnel et paresse. Nous voulons que le programmeur doive réellement se mettre en quatre et investir des efforts pour utiliser l'option dangereuse! ...

Une bien meilleure solution est une API comme Python dbapi. J'ai écrit un équivalent PHP il y a des années, qui était comme:

db_query( "SELECT * FROM mytable WHERE id=%s", $id )

C'est plus pratique que les déclarations préparées. Une seule ligne à taper. De plus, il renvoie le résultat sous une forme que foreach () peut utiliser, il est donc beaucoup plus facile à utiliser que fetch () et d'autres choses. Plus important encore, le code caché derrière cette API gérera correctement l'échappement. Cela rend les injections SQL non pertinentes. Cela gère environ 99% des requêtes si vous n'utilisez pas d'ORM. Les requêtes dynamiques (généralement la recherche) auront besoin d'une gestion spéciale, mais c'est toujours facile.

Si vous gardez cette règle à l'esprit, vous devrez à un moment donné vous demander pourquoi vous devez utiliser htmlspecialchars (), et pourquoi le langage n'a pas de fonctionnalité qui rendrait le paramètre sûr (c'est-à-dire pas de vulns XSS) le plus simple à utiliser? Pourquoi oh pourquoi? Et c'est là que vous abandonnez PHP.

0
peufeu