2009-07-31 7 views
2

Cette question est inspirée de Joël "Making Wrong Code incorrect Regardez"Pouvons-nous tirer parti du système de type pour sécuriser les programmes?

http://www.joelonsoftware.com/articles/Wrong.html

Parfois, vous pouvez utiliser les types de faire respecter la sémantique des objets au-delà de leurs interfaces. Par exemple, l'interface Java Serializable ne définit pas réellement les méthodes, mais le fait qu'un objet implémente Serializable dit quelque chose sur la façon dont il devrait être utilisé. Pouvons-nous avoir des interfaces/sous-classes UnsafeString et SafeString dans, disons Java, qui sont utilisées de la même manière que la notation hongroise de Joel et Serializable de Java pour que ça ne soit pas seulement mauvais - il ne compile pas ? Est-ce faisable en Java/C/C++ ou les systèmes de type sont-ils trop faibles ou trop dynamiques?

De plus, au-delà de la désinfection des entrées, quelles autres fonctions de sécurité peuvent être implémentées de cette manière?

+1

Ceci est également discuté dans Podcast 58 (http://blog.stackoverflow.com/2009/06/podcast-58/) – Brian

+0

c'est essentiellement ce qu'est un système de type * *. Sans le système de type, n'importe quelle variable pourrait être traitée comme n'importe quelle autre. Vous seriez en mesure d'accéder à la mémoire hors limites en traitant, disons, un int comme un double. Cela vous permettrait d'obtenir la valeur des 4 octets après la fin de l'int. Ou les entiers non signés vous empêchent de stocker accidentellement des valeurs signées. (bien que la façon dont ils le convertissent en silence ne soit pas idéale) – jalf

+0

Bien, mais il semble qu'il y ait un niveau de propriétés plus élevé que nous pouvons appliquer dans le système de type qui a peu à voir avec la façon dont nous manipulons la représentation des données.Essentiellement, j'imagine que le système de types est davantage intégré dans la logique métier. –

Répondre

3

Le système de type déjà applique un grand nombre de ces fonctions de sécurité. C'est essentiellement ce que c'est pour.

Pour un exemple très simple, cela vous empêche de traiter un flottant comme un int. C'est un aspect de la sécurité - il garantit que le type sur lequel vous travaillez va se comporter comme prévu. Il garantit que seules les méthodes de chaîne sont appelées sur une chaîne. L'assemblage n'a pas cette garantie, par exemple.

Il appartient également au système de types de s'assurer que vous n'appelez pas de fonctions privées sur une classe. C'est une autre caractéristique de sécurité.

Le système de type Java est trop anémique pour appliquer efficacement beaucoup de contraintes intéressantes, mais dans de nombreux autres langages (y compris C++), le système de type peut être utilisé pour appliquer des règles beaucoup plus étendues.

En C++, la métaprogrammation de template vous donne beaucoup d'outils pour interdire le "mauvais" code. Par exemple:

class myclass : boost::noncopyable { 
... 
}; 

impose à la compilation que la classe ne peut pas être copiée. La suivante produira des erreurs de compilation:

myclass m; 
myclass m2(m); // copy construction isn't allowed 
myclass m3; 
m3 = m; // assignment also not allowed 

De même, nous pouvons assurer à la compilation qu'une fonction de modèle uniquement est appelée sur les types qui remplissent certains critères (par exemple, ils doivent être itérateurs à accès aléatoire, tandis que les bilinéaire ne sont pas autorisés, ou doivent être des types POD, ou ne doivent pas être de type entier (char, short, int, long), mais tous les autres types doivent être légaux

Un exemple de métaprogrammation de modèle en C++ implémente une librairie pour le calcul des unités physiques qui permet de multiplier une valeur de type "meter" par une autre valeur du même type, et détermine automatiquement que le résultat doit être de type "square meter". aleur de type "mile" avec une valeur de type "hour" et obtenir une unité de type "miles par heure".

Encore une fois, une fonction de sécurité qui vous empêche de mélanger vos types et de mélanger accidentellement vos unités. Vous obtiendrez une erreur de compilation si vous calculez une valeur et essayez de l'affecter au mauvais type. essayer de diviser, disons, litres par meters^2 et en affectant le résultat à une valeur de, disons, kilogrammes, entraînera une erreur de compilation.

La plupart de ces opérations nécessitent un travail manuel, mais le langage vous fournit les outils dont vous avez besoin pour créer les vérifications de type souhaitées. Certains de ces éléments pourraient être mieux supportés directement dans la langue, mais les contrôles les plus créatifs devraient être implémentés manuellement dans tous les cas.

+0

+1 pour le commentaire sur les unités physiques. Je recommanderais à tous les programmeurs de voir les concepts impliqués. Cela montre vraiment le genre de puissance et de flexibilité que je pense que le C++ a sur n'importe quel autre langage. (http://learningcppisfun.blogspot.com/2007/01/units-and-dimensions-with-c-templates.html). –

+0

Pas "autre". Comme mentionné dans certaines des autres réponses, une grande partie de cela aurait été triviale dans Ada, par exemple – jalf

-1

Vous ne pouvez pas avoir une sous-classe UnsafeString de String en Java, depuis java.lang.String is final.

En général, vous ne pouvez pas fournir de sécurité au niveau de la source - si vous souhaitez protéger contre le code malveillant, vous devez le faire au niveau binaire (par exemple, le bytecode Java). C'est pourquoi private/protected ne peut pas être utilisé comme mécanisme de sécurité en C++: il est possible de le contourner avec des manipulations de pointeur.

+0

Nous ne protégeons pas contre le code Java malveillant. Nous protégeons contre les intrusions malveillantes (pour les scripts inter-sites par exemple). Utiliser le compilateur pour éliminer les 'odeurs' plutôt que la méthode hongroise de Joel est le désir ici. –

+0

Mais bon appel sur la partie finale de la classe. Cependant, ce serait juste un obstacle plutôt qu'une impasse. –

1

Oui, vous pouvez faire une telle chose. Je ne connais pas Java, mais en C++ ce n'est pas habituel et il n'y a pas de support pour cela, donc vous devez faire un peu de travail manuel. Il est coutumier dans d'autres langages, Ada par exemple, qui ont l'équivalent d'un typedef qui introduit un nouveau type qui ne peut être implicitement converti dans le type original (ce nouveau type "hérite" de certaines opérations de base de celui qu'il est créé, donc il reste utile). BTW, en général l'héritage n'est pas un bon moyen d'introduire les nouveaux types, car même s'il n'y a pas de conversion implicite dans un sens, il y en a un dans l'autre.

+2

Que voulez-vous dire, ce n'est pas habituel en C++? Un grand nombre de trucs de métaprogrammation doivent atteindre exactement ce genre de sécurité. La plupart des boost :: type_traits, ainsi que l'assert statique, uncopyable et un grand nombre d'autres utilitaires sont conçus pour cela. – jalf

+1

D'accord. Tant que votre mission est de rendre plus difficile d'oublier de désinfecter/échapper les données avant le stockage, la manipulation ou l'affichage, un type «dangereux» qui ne peut pas passer au type «sûr» est une solution raisonnable. – wowest

+0

On dirait que vous sauvegardez son point à moi, jalf, –

0

Vous pouvez en faire une quantité initiale dans Ada. Par exemple, vous pouvez créer des types entiers qui ne peuvent interopérer implicitement les uns avec les autres, et les énumérations Ada ne sont pas compatibles avec un type entier. Vous pouvez toujours convertir entre eux, mais vous devez explicitement le faire, ce qui attire l'attention sur ce que vous faites. Vous pourriez faire la même chose avec le C++ actuel, mais vous devriez envelopper tous vos entiers et enums dans les classes, ce qui est beaucoup trop de travail pour quelque chose qui devrait être simple (ou mieux encore, le défaut façon de faire les choses). Je comprends que la prochaine version de C++ va corriger au moins le problème de l'énumération.

0

En C++, je suppose que vous pouvez utiliser typedef pour créer un synonyme pour un type primitif. Votre synonyme pourrait impliquer quelque chose sur le contenu de cette variable, en remplaçant la fonction de la notation hongroise des applications. Intellisense rapportera le synonyme que vous avez utilisé lors de la déclaration, donc si vous n'aimez pas utiliser le hongrois réel, cela vous évitera de faire défiler (ou d'utiliser Go To Definition).

+0

J'ai fait cela avec des entiers qui représentaient des numéros de pages et de blocs physiques et logiques sur un système de fichiers flash. Les types pris en charge par défaut transtypage vers et depuis un entier, mais pas vers et depuis l'autre. A aidé à attraper quelques problèmes (généralement, j'ai utilisé les types personnalisés, mais pour certaines boucles et de tels nombres entiers étaient nécessaires). La construction réelle utilisait des entiers, mais les constructions d'émulation/test utilisaient les types personnalisés. Puisque l'utilisation de différents types devait assurer la sécurité de type à la compilation, le fait que la construction d'émulation/test soit propre signifiait que la construction réelle utilisant des entiers devrait également être propre. – supercat

0

Je suppose que vous pensez à quelque chose du genre de l'analyse de "l'altération" de Perl.

En Java, il devrait être possible d'utiliser des annotations personnalisées et un processeur d'annotation pour l'implémenter. Pas nécessairement facile cependant.

Questions connexes