Question

Lorsque vous écrivez du code, programmez-vous consciemment de manière défensive pour garantir une qualité élevée du programme et pour éviter la possibilité que votre code soit exploité de manière malveillante, par ex.via des exploits de débordement de tampon ou une injection de code ?

Quel est le niveau de qualité « minimum » que vous appliquerez toujours à votre code ?

Était-ce utile?

La solution

Dans mon travail, notre code doit être de qualité supérieure.
Nous nous concentrons donc sur deux choses principales :

  1. Essai
  2. Révisions de codes

Ceux-là rapportent l’argent à la maison.

Autres conseils

Semblable à abyx, dans l'équipe dans laquelle je fais partie, les développeurs utilisent toujours des tests unitaires et des révisions de code.En plus de cela, je vise également à m'assurer de ne pas incorporer de code que les gens peut utilisation - J'ai tendance à écrire du code uniquement pour l'ensemble de méthodes de base requis pour que l'objet en question fonctionne comme cela a été spécifié.J'ai découvert que l'incorporation de méthodes qui ne seront peut-être jamais utilisées, mais qui fournissent des fonctionnalités, peuvent involontairement introduire une « porte dérobée » ou une utilisation involontaire/imprévue dans le système.

Il est beaucoup plus facile de revenir en arrière plus tard et d'introduire les méthodes, les attributs et les propriétés demandés plutôt que d'anticiper quelque chose qui pourrait ne jamais arriver.

Je recommanderais d'être sur la défensive pour les données qui entrent dans un « composant » ou un cadre.Au sein d'un « composant » ou d'un cadre, il faut penser que les données sont « correctes ».

Penser comme ça.C'est à l'appelant de fournir les paramètres corrects, sinon TOUTES les fonctions et méthodes doivent vérifier chaque paramètre entrant.Mais si la vérification n'est effectuée que pour l'appelant, elle n'est nécessaire qu'une seule fois.Ainsi, un paramètre doit être « correct » et peut donc être transmis aux niveaux inférieurs.

  1. Vérifiez toujours les données provenant de sources externes, d'utilisateurs, etc.
  2. Un « composant » ou un framework doit toujours vérifier les appels entrants.

S'il y a un bug et qu'une valeur incorrecte est utilisée dans un appel.Quelle est vraiment la bonne chose à faire ?On a seulement une indication que les "données" sur lesquelles le programme travaille sont fausses et certains aiment ASSERTS mais d'autres souhaitent utiliser le rapport d'erreurs avancé et une éventuelle récupération d'erreurs.Dans tous les cas, les données s'avèrent défectueuses et dans certains cas, il est bon de continuer à travailler dessus.(notez que c'est bien si les serveurs ne meurent pas au moins)

Une image envoyée depuis un satellite pourrait être un cas pour essayer une récupération d'erreur avancée sur... une image téléchargée depuis Internet pour afficher une icône d'erreur pour...

Je recommande aux gens d'écrire du code fasciste dans l'environnement de développement et bienveillant en production.

Pendant le développement, vous souhaitez détecter les mauvaises données/logiques/codes le plus tôt possible pour éviter que les problèmes ne passent inaperçus ou n'entraînent des problèmes ultérieurs dont la cause première est difficile à suivre.

En production, gérez les problèmes avec autant de grâce que possible.Si quelque chose est vraiment une erreur irrécupérable, gérez-la et présentez ces informations à l'utilisateur.

À titre d'exemple, voici notre code pour normaliser un vecteur.Si vous lui fournissez de mauvaises données en développement, il criera, en production, il renvoie une valeur de sécurité.

inline const Vector3 Normalize( Vector3arg vec )
{
    const float len = Length(vec);
    ASSERTMSG(len > 0.0f "Invalid Normalization");
    return len == 0.0f ? vec : vec / len;
}

Je travaille toujours pour prévenir des choses comme les attaques par injection.Cependant, lorsque vous travaillez sur un site intranet interne, la plupart des fonctionnalités de sécurité semblent être des efforts inutiles.Je les fais toujours, peut-être pas aussi bien.

Eh bien, il existe un certain ensemble de bonnes pratiques en matière de sécurité.Au minimum, pour les applications de base de données, vous devez faire attention à l'injection SQL.

D'autres choses comme le hachage des mots de passe, le cryptage des chaînes de connexion, etc.sont également une norme.

À partir de là, cela dépend de l’application réelle.

Heureusement, si vous travaillez avec des frameworks tels que .Net, de nombreuses protections de sécurité sont intégrées.

Vous devez toujours programmer de manière défensive, je dirais même pour les applications internes, simplement parce que les utilisateurs pourraient, par pure chance, écrire quelque chose qui casse votre application.Certes, vous n'avez probablement pas à vous soucier d'essayer de vous tromper d'argent, mais quand même.Programmez toujours de manière défensive et supposez que l’application échouera.

L’utilisation du développement piloté par les tests aide certainement.Vous écrivez un seul composant à la fois, puis énumérez tous les cas potentiels d'entrées (via des tests) avant d'écrire le code.Cela garantit que vous avez couvert toutes les bases et que vous n'en avez écrit aucune. cool code que personne n’utilisera mais pourrait casser.

Bien que je ne fasse rien de formel, je passe généralement du temps à examiner chaque cours et à m'assurer que :

  1. s'ils sont dans un état valide, ils restent dans un état valide
  2. il n'y a aucun moyen de les construire dans un état invalide
  3. Dans des circonstances exceptionnelles, ils échoueront aussi gracieusement que possible (il s'agit souvent d'un nettoyage et d'un lancer)

Ça dépend.

Si je pirate vraiment quelque chose pour mon propre usage, j'écrirai le meilleur code auquel je n'aurai pas à penser.Laissez le compilateur être mon ami pour les avertissements, etc.mais je ne créerai pas automatiquement de types pour le plaisir.

Plus le code est susceptible d'être utilisé, même occasionnellement, j'augmente le niveau de contrôle.

  • nombres magiques minimaux
  • meilleurs noms de variables
  • longueurs de tableau/chaîne entièrement vérifiées et définies
  • programmation par assertions de contrat
  • vérifications de valeur nulle
  • exceptions (selon le contexte du code)
  • commentaires explicatifs de base
  • documentation d'utilisation accessible (si perl etc.)

Je prendrai une définition différente de la programmation défensive, comme celle préconisée par Java efficace par Josh Bloch.Dans le livre, il explique comment gérer les objets mutables que les appelants transmettent à votre code (par exemple, dans les setters) et les objets mutables que vous transmettez aux appelants (par exemple, dans les getters).

  • Pour les setters, assurez-vous de cloner tous les objets mutables et de stocker le clone.De cette façon, les appelants ne peuvent pas modifier l'objet transmis après coup pour briser les invariants de votre programme.
  • Pour les getters, soit renvoyez une vue immuable de vos données internes, si l'interface le permet ;ou bien renvoyer un clone des données internes.
  • Lorsque vous appelez des rappels fournis par l'utilisateur avec des données internes, envoyez une vue immuable ou un clone, le cas échéant, à moins que vous n'ayez l'intention que le rappel modifie les données, auquel cas vous devez les valider après coup.

Le message à retenir est de vous assurer qu'aucun code extérieur ne peut contenir d'alias pour les objets mutables que vous utilisez en interne, afin que vous puissiez conserver vos invariants.

Je suis convaincu qu'une programmation correcte protégera contre ces risques.Des choses comme éviter les fonctions obsolètes, qui (au moins dans les bibliothèques Microsoft C++) sont généralement obsolètes en raison de vulnérabilités de sécurité, et valider tout ce qui traverse une frontière externe.

Les fonctions appelées uniquement à partir de votre code ne devraient pas nécessiter une validation excessive des paramètres, car vous contrôlez l'appelant, c'est-à-dire qu'aucune frontière externe n'est franchie.Les fonctions appelées par le code d'autres personnes doivent supposer que les paramètres entrants seront invalides et/ou malveillants à un moment donné.

Mon approche pour gérer les fonctions exposées consiste simplement à m'effondrer, avec un message utile si possible.Si l'appelant ne parvient pas à obtenir les bons paramètres, le problème réside dans son code et il doit le résoudre, pas vous.(Vous avez évidemment fourni une documentation pour votre fonction, puisqu'elle est exposée.)

L'injection de code n'est un problème que si votre application est capable d'élever l'utilisateur actuel.Si un processus peut injecter du code dans votre application, il pourrait facilement écrire le code en mémoire et l'exécuter quand même.Sans pouvoir accéder pleinement au système, les attaques par injection de code sont inutiles.(C'est pourquoi les applications utilisées par les administrateurs ne doivent pas être accessibles en écriture par les utilisateurs mineurs.)

D'après mon expérience, utiliser positivement une programmation défensive ne signifie pas nécessairement que vous finissez par améliorer la qualité de votre code.Ne vous méprenez pas, vous devez programmer de manière défensive pour détecter les types de problèmes que les utilisateurs rencontreront - les utilisateurs n'aiment pas que votre programme plante sur eux - mais il est peu probable que cela rende le code plus facile à maintenir, essai, etc

Il y a plusieurs années, nous avons pris pour politique d'utiliser des assertions à tous les niveaux de nos logiciels et ce, parallèlement aux tests unitaires, aux révisions de code, etc.ainsi que nos suites de tests d'applications existantes - ont eu un effet positif significatif sur la qualité de notre code.

Java, JAR signés et JAAS.

Java pour empêcher le débordement de tampon et les exploits de pointeur/pile.

N'utilisez pas JNI.(Java Native Interface), il vous expose aux bibliothèques DLL/partagées.

JAR signés pour empêcher le chargement des classes de constituer un problème de sécurité.

JAAS peut permettre à votre application de ne faire confiance à personne, même à elle-même.

J2EE dispose d'un support intégré (certes limité) pour la sécurité basée sur les rôles.

Il y a des frais généraux pour certains de ces éléments, mais les failles de sécurité disparaissent.

Réponse simple : Ça dépend.Trop de codage défensif peut causer des problèmes de performances majeurs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top