web-dev-qa-db-fra.com

Pourquoi le gouvernement américain interdit-il les langages dynamiques pour les projets sécurisés?

Je connais des personnes qui travaillent actuellement sur un projet pour l'armée américaine (faible niveau de sécurité, données de type ressources humaines non combattantes).

Un état initial du code du projet a été soumis aux militaires pour examen, et ils ont exécuté le programme via une sorte d'outil d'analyse de sécurité. Il a renvoyé un rapport sur les problèmes de sécurité connus dans le code et les modifications requises qui devaient être mises en œuvre avant la livraison du produit final.

L'un des éléments qui devait être résolu était la suppression d'une partie du projet qui était écrite en Ruby car il s'agit d'un langage dynamique.

Quel est le contexte/la raison pour ne pas autoriser l'utilisation d'un langage dynamique dans un environnement sécurisé? Est-ce que le gouvernement tarde à adopter de nouvelles technologies? Ou les langages dynamiques posent-ils un risque de sécurité supplémentaire par rapport aux langages statiques (ala C++ ou Java )?

122
Patrick

Il y a un certain nombre de choses `` soignées '' qui peuvent être faites dans des langages dynamiques qui peuvent être cachées dans des parties du code qui ne sont pas immédiatement évidentes pour un autre programmeur ou auditeur quant à la fonctionnalité d'un morceau de code donné.

Considérez cette séquence dans irb (interactive Ruby Shell):

irb(main):001:0> "bar".foo
NoMethodError: undefined method `foo' for "bar":String
        from (irb):1
        from /usr/bin/irb:12:in `<main>'
irb(main):002:0> class String
irb(main):003:1> def foo
irb(main):004:2> "foobar!"
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> "bar".foo
=> "foobar!"

Ce qui s'est passé là-bas, j'ai essayé d'appeler la méthode foo dans une constante String. Cela a échoué. J'ai ensuite ouvert la classe String et défini la méthode foo o return "foobar!", puis l'a appelé. Cela a fonctionné.

Ceci est connu comme une classe ouverte et me donne des cauchemars chaque fois que je pense à écrire du code en Ruby qui a une sorte de sécurité ou d'intégrité. Bien sûr, cela vous permet de faire des choses intéressantes assez rapidement .. . mais je pourrais faire en sorte que chaque fois que quelqu'un stocke une chaîne, il la stocke dans un fichier ou l'envoie sur le réseau. Et ce petit peu de redéfinition de la chaîne peut être glissé n'importe où dans le code.

Beaucoup d'autres langages dynamiques ont des choses similaires qui peuvent être faites. Perl a Tie :: Scalar qui peut en arrière-plan changer le fonctionnement d'un scalaire donné (c'est un peu plus évident et nécessite une commande spécifique que vous pouvez voir, mais un scalaire transmis depuis quelque part sinon cela pourrait être un problème). Si vous avez accès au livre de recettes Perl, recherchez la recette 13.15 - Création de variables magiques avec cravate.

En raison de ces choses (et d'autres font souvent partie de langages dynamiques), de nombreuses approches de l'analyse statique de la sécurité dans le code ne fonctionnent pas. Perl et Undecidability montre que c'est le cas et souligne même ces problèmes triviaux avec la coloration syntaxique (whatever / 25 ; # / ; die "this dies!"; pose des défis car whatever peut être défini pour prendre ou non des arguments au moment de l'exécution , ce qui annule complètement un surligneur de syntaxe ou un analyseur statique).


Cela peut devenir encore plus intéressant dans Ruby avec la possibilité d'accéder à l'environnement dans lequel une fermeture a été définie (voir YouTube: Keeping Ruby Reasonable) de RubyConf 2011 par Joshua Ballanco.) J'ai été mis au courant de cette vidéo à partir d'un commentaire Ars Technica par MouseTheLuckyDog .

Considérez le code suivant:

def mal(&block)
    puts ">:)"
    block.call
    t = block.binding.eval('(self.methods - Object.methods).sample')
    block.binding.eval <<-END
        def #{t.to_s}
          raise 'MWHWAHAW!'
        end
    END
end

class Foo
    def bar
        puts "bar"
    end

    def qux
        mal do
            puts "qux"
        end
    end
end

f = Foo.new
f.bar
f.qux

f.bar
f.qux

Ce code est entièrement visible, mais la méthode mal pourrait être ailleurs ... et avec les classes ouvertes, bien sûr, elle pourrait être redéfinie ailleurs.

Exécution de ce code:

 ~/$ Ruby foo.rb 
 Bar 
>:) 
 Qux 
 Bar 
 .] b.rb: 20: dans `qux ': MWHWAHAW! (RuntimeError) 
 De b.rb: 30: dans `` 
 ~/$ Ruby foo.rb 
 Bar 
 >:) 
 qux 
 b.rb: 20: en `bar ': MWHWAHAW! (RuntimeError) 
 De b.rb: 29: dans '' 

Dans ce code, la fermeture a pu accéder à toutes les méthodes et autres liaisons définies dans la classe à cette étendue. Il a choisi une méthode aléatoire et l'a redéfinie pour déclencher une exception. (voir la classe Binding dans Ruby pour avoir une idée de ce à quoi cet objet a accès))

Les variables, méthodes, valeur de self et éventuellement un bloc itérateur accessible dans ce contexte sont tous conservés.

Une version plus courte qui montre la redéfinition d'une variable:

def mal(&block)
    block.call
    block.binding.eval('a = 43')
end

a = 42
puts a
mal do 
  puts 1
end
puts a

Ce qui, une fois exécuté, produit:

 42 
 1 
 43 

C'est plus que la classe ouverte que j'ai mentionnée ci-dessus qui rend l'analyse statique impossible. Ce qui est démontré ci-dessus est qu'une fermeture qui est passée ailleurs, emporte avec elle l'environnement complet dans lequel elle a été définie. C'est ce qu'on appelle un environnement de première classe (tout comme quand vous pouvez passer des fonctions , ce sont des fonctions de première classe, c'est l'environnement et toutes les liaisons disponibles à ce moment-là). On pourrait redéfinir toute variable définie dans le cadre de la fermeture.

Bon ou mauvais, se plaignant de Ruby ou pas (il y a des utilisations où l'on voudrait que puisse arriver à l'environnement d'une méthode (voir Safe en Perl)), la question de "pourquoi serait Ruby être restreint dans un projet gouvernemental" est vraiment répondu dans cette vidéo lié ci-dessus.

Étant donné que:

  1. Ruby permet d'extraire l'environnement de toute fermeture
  2. Ruby capture toutes les fixations dans le cadre de la fermeture
  3. Ruby maintient toutes les liaisons en direct et modifiables
  4. Ruby a de nouvelles liaisons qui masquent les anciennes liaisons (plutôt que de cloner l'environnement ou d'interdire une nouvelle liaison)

Avec les implications de ces quatre choix de conception, il est impossible de savoir ce que fait un morceau de code.

Plus d'informations à ce sujet peuvent être lues sur blog Abstract Heresies . Le message en particulier concerne le programme où un tel débat a eu lieu. (lié à SO: Pourquoi Scheme ne prend-il pas en charge les environnements de première classe? )

Au fil du temps, cependant, j'ai réalisé qu'il y avait plus de difficulté et moins de puissance avec des environnements de première classe que je ne l'avais pensé à l'origine. À ce stade, je crois que les environnements de première classe sont inutiles au mieux, et dangereux au pire.

J'espère que cette section montre l'aspect dangereux des environnements de première classe et pourquoi il serait demandé de supprimer Ruby de la solution fournie. Ce n'est pas seulement cela Ruby is un langage dynamique (comme mentionné ailleurs, d'autres langues dynamiques ont été autorisées dans d'autres projets), mais qu'il existe des problèmes spécifiques qui rendent certaines langues dynamiques encore plus difficiles à raisonner.

126
user40980

En supposant que l'évaluation ne concerne que la sécurité, et pas seulement une analyse d'acceptation (c'est-à-dire qu'ils n'acceptent pas Ruby parce qu'ils ne veulent pas prendre en charge Ruby), alors:

Les outils d'analyse de sécurité ont généralement un mauvais moment avec des comportements dynamiques.

Par exemple:

Exécutez n'importe quel projet . NET écrit avec des fonctionnalités modernes comme ASP.NET MVC et Entity Framework via quelque chose comme Veracode et voir quel type de liste de faux positifs vous recevez dans votre rapport.

Veracode répertorie même de nombreuses techniques de base dans les bibliothèques de base .NET 4 comme des "frameworks non pris en charge" comme non pris en charge ou bêta, même si la plupart d'entre elles ont plusieurs années à ce stade.

Si vous traitez avec une entité qui dépend fortement d'un tel outil, elle est presque obligée de considérer ces personnes précaires si elles n'ont pas l'expertise technique et les ressources pour évaluer manuellement un projet et voir s'il est correctement écrit et sécurisé.

Dans les opérations civiles où les systèmes informatiques ne contrôlent généralement rien de dangereux ou de terriblement coûteux, l'atténuation est que vous discutez des faux positifs et qu'ils sont généralement acceptés comme tels en vrac.

Dans les opérations bancaires, vous avez toujours une chance d'atténuation des faux positifs, mais vous allez passer beaucoup plus de temps à discuter des détails de chaque article. Cela devient rapidement prohibitif et vous commencez à utiliser des méthodes plus traditionnelles.

Dans l'armée, l'aviation, l'industrie lourde et autres, les systèmes peuvent contrôler des choses qui ont de terribles modes de défaillance de ces systèmes, de sorte qu'ils peuvent avoir des règles très strictes sur les langues, les compilateurs, etc.

Les organisations écrivent également généralement leur politique de sécurité pour le pire des cas qu'elles connaissent, donc même si vous écrivez quelque chose de trivial, si vous l'écrivez pour une organisation qui a des systèmes non triviaux, la valeur par défaut va généralement être de le maintenir à un niveau plus élevé, sauf si quelqu'un demande une exception spécifique.

50
Bill

Les langages dynamiques peuvent être utilisés dans des applications militaires et de défense. J'ai personnellement utilisé et livré Perl et Python dans les applications DoD. J'ai également vu PHP et JavaScript utilisé et déployé. Dans mon expérience, la plupart des le code non compilé que j'ai vu a été les scripts Shell et Perl car les environnements requis sont approuvés et installés sur une variété de systèmes cibles possibles.

Le fait que ces langues soient dynamiques n'est probablement pas le problème. Les interprètes pour ces langues doivent être approuvés pour une utilisation sur les systèmes cibles. Si l'interpréteur n'est pas approuvé pour utilisation (ou peut-être qu'il l'est, mais qu'il n'est pas déployé sur les systèmes cibles), alors la langue ne peut pas être utilisée. L'utilisation d'un interprète donné (ou de n'importe quelle application) sur un système sécurisé nécessite un certain nombre d'obstacles de sécurité: analyse de la source, capacité de compilation à partir de la source pour les environnements cibles, analyse supplémentaire des binaires, aucun conflit avec l'infrastructure existante, etc.

33
Thomas Owens

J'ai passé un peu de temps à interviewer le DOD (Département de la Défense), pour un poste d'écriture de code pour le MMU du F-16 . Sans violer aucune non-divulgation: le MMU est l'unité informatique qui contrôle presque toutes les fonctions du F-16. Il est (évidemment) essentiel qu'aucune erreur, comme des bogues d'exécution, ne se produise Il est tout aussi essentiel que le système effectue des opérations de calcul en temps réel.

Pour cela et pour d'autres raisons historiques, tout le code de ce système est écrit ou compilé dans ADA, un langage de programmation statique orienté objet .

En raison des fonctionnalités de support critiques pour la sécurité d'Ada, il est désormais utilisé non seulement pour des applications militaires, mais également dans des projets commerciaux où un bogue logiciel peut avoir de graves conséquences, par exemple avionique et contrôle du trafic aérien, fusées commerciales (par exemple Ariane 4 et 5), satellites et autres systèmes spatiaux, transports ferroviaires et bancaires. Par exemple, le logiciel du système fly-by-wire du Boeing 777 a été écrit en Ada.

Je déteste trop citer, mais cela explique très bien pourquoi des langages exactement statiques (comme ADA) sont utilisés pour des projets comme celui-ci:

Un grand nombre de vérifications au moment de la compilation sont prises en charge pour aider à éviter les bogues qui ne seraient pas détectables avant l'exécution dans certaines autres langues ou nécessiteraient des vérifications explicites pour être ajoutées au code source. Par exemple, la syntaxe requiert une fermeture de blocs nommée explicitement pour éviter les erreurs dues à des jetons de fin incompatibles. L'adhésion à un typage fort permet de détecter de nombreuses erreurs logicielles courantes (paramètres incorrects, violations de plage, références non valides, types incompatibles, etc.) soit pendant la compilation, soit autrement pendant l'exécution. Comme la concurrence fait partie de la spécification du langage, le compilateur peut dans certains cas détecter des blocages potentiels. Les compilateurs vérifient également fréquemment les identificateurs mal orthographiés, la visibilité des packages, les déclarations redondantes, etc. et peuvent fournir des avertissements et des suggestions utiles sur la façon de corriger l'erreur.

Ada prend également en charge les vérifications d'exécution pour se protéger contre l'accès à la mémoire non allouée, les erreurs de dépassement de tampon, les violations de plage, les erreurs hors tension, les erreurs d'accès à la baie et d'autres bogues détectables. Ces vérifications peuvent être désactivées dans un souci d'efficacité d'exécution, mais peuvent souvent être compilées efficacement. Il comprend également des installations pour faciliter la vérification du programme. Pour ces raisons, Ada est largement utilisé dans les systèmes critiques, où toute anomalie peut entraîner des conséquences très graves, par exemple, la mort accidentelle, des blessures ou des pertes financières graves. Des exemples de systèmes où Ada est utilisé comprennent l'avionique, les chemins de fer, les technologies bancaires, militaires et spatiales.

La gestion dynamique de la mémoire d'Ada est de haut niveau et de type sécurisé. Ada n'a pas de "pointeurs" génériques (et vagues); il ne déclare pas non plus implicitement aucun type de pointeur. Au lieu de cela, toutes les allocations et désallocations dynamiques de mémoire doivent avoir lieu via des types d'accès explicitement déclarés. Chaque type d'accès a un pool de stockage associé qui gère les détails de bas niveau de la gestion de la mémoire; le programmeur peut utiliser le pool de stockage par défaut ou en définir de nouveaux (ceci est particulièrement pertinent pour l'accès à la mémoire non uniforme). Il est même possible de déclarer plusieurs types d'accès différents qui désignent tous le même type mais utilisent des pools de stockage différents. En outre, le langage prévoit des vérifications d'accessibilité, à la fois au moment de la compilation et au moment de l'exécution, qui garantissent qu'une valeur d'accès ne peut pas survivre au type de l'objet vers lequel elle pointe.

32
Michael Jasper

Le DoD et la NASA ont tous deux une longue histoire avec des échecs de programmation qui leur ont coûté des milliards de dollars. Les deux institutions ont accepté des processus qui devraient les empêcher de répéter les mêmes erreurs.

Is this the government being slow to adopting new technologies?

C'est une idée fausse - les langages dynamiques ne sont pas une nouvelle technologie, ils sont assez anciens. Le problème est que si vous avez déjà eu un problème causé par un langage dynamique (par exemple par une frappe faible/dynamique) et que ce problème vous a coûté beaucoup d'argent, vous pourriez accepter une politique qui vous empêcherait de refaire la même erreur - par exemple interdisant l'utilisation de langages dynamiques dans les systèmes sensibles.

Les langages dynamiques "avalent" souvent les bogues et se retrouvent avec un comportement inattendu. Ceci est très dangereux dans les systèmes sensibles. Si quelque chose ne va pas, vous voulez le savoir dès que possible.

Si la sécurité est concernée, il serait nécessaire de voir le cas d'utilisation réel. Par exemple, je ne pense pas qu'une page Web Ruby on Rails soit automatiquement moins sécurisée qu'une page Java page Web.

13
Sulthan

J'aimerais ajouter aux réponses existantes en décrivant SA-CORE-2014-005 de Drupal, qui est une vulnérabilité hautement critique qui permet l'injection SQL et finalement l'exécution de code arbitraire. Cela est dû aux règles de typage dynamique et de typage d'exécution laxiste de PHP.

L'intégralité du patch pour ce problème est:

-      foreach ($data as $i => $value) {
+      foreach (array_values($data) as $i => $value) {

Ce code fait partie d'une couche d'abstraction SQL conçue pour empêcher l'injection SQL. Il prend une requête SQL avec des paramètres nommés et un tableau associatif qui fournit une valeur pour chaque paramètre nommé. La valeur peut être un tableau, pour des cas comme WHERE x IN (val1, val2, val3), où les trois valeurs peuvent être transmises en tant que valeur de tableau unique pour un seul paramètre nommé.

La vulnérabilité se produit car le code suppose que $i Dans $i => $value Doit être un index entier de la valeur. Il continue et concatène cet "index" directement dans la requête SQL dans le cadre d'un nom de paramètre, car les entiers n'ont pas besoin d'être échappés, non?

Malheureusement pour Drupal, PHP ne fournit pas une telle garantie. Il est possible de passer dans un autre tableau associatif, dont les clés sont des chaînes, et cette boucle concaténera avec bonheur la clé de chaîne dans la requête, comme- est (rappelez-vous que le code pense qu'il ne peut jamais être qu'un entier).

Bien qu'il existe des façons d'avoir une erreur similaire dans une langue typée statiquement, elles sont peu probables. Un bon développeur considérerait ce que pourraient être $i Avant de le concaténer dans la requête. Avec un langage typé statiquement, il est très facile d'imposer que $i Doit être un entier, et dans un code sensible à la sécurité comme celui-ci, cela serait très certainement fait.

De plus, le code vérifie réellement si la valeur est un tableau avant d'itérer sur les éléments. Et là se trouve une deuxième partie de l'échec qui active cette vulnérabilité: un tableau associatif et un tableau "normal" renvoient true pour is_array. Bien qu'il soit également vrai qu'en C #, les dictionnaires et les tableaux sont IEnumerable, il est difficile de construire du code qui confondrait les clés de dictionnaire avec des indices de tableau comme celui-ci, même intentionnellement, et encore moins accidentellement.

6
Roman Starkov

Pour autant que je sache, la politique officielle du ministère de la Défense n'interdit généralement pas les langages dynamiques.

Les normes pour les logiciels développés ou achetés par le DoD sont promulguées par la Defense Information Systems Agency (DISA). Leur Application Security - Application Security & Development Security Technical Implementation Guide (STIG) n'interdit aucune langue particulière. Il ne mentionne pas Ruby, mais il mentionne Perl et Python qui sont de même dynamique. Il les mentionne dans le contexte de divers sujets (en suivant les normes de codage établies, en évitant les vulnérabilités d'injection de commande, etc.).

Ce que vous voyez est probablement un outil d'analyse trop strict (il y en a plusieurs différents mentionnés dans le STIG, chacun peut avoir sa propre interprétation des règles) et/ou une interprétation trop stricte de sa sortie.

2
nobody

La sécurité ou non d'une base de code dépend de la façon dont vous écrivez votre code, de la façon dont vous le testez et de la manière dont vous validez et surveillez votre processus de développement et de déploiement. Les langues ne sont ni sécurisées ni non sécurisées, c'est ainsi que vous codez.

La majorité des incidents de sécurité dus à des entrées malveillantes (injections sql, débordements de tampon), virus, rootkits et chevaux de Troie. Aucune langue ne peut vous en protéger.

Donc, interdire des classes de langues pour "insécurité" n'est pas une raison valable.

Je soupçonne que quelqu'un, pour quelque raison que ce soit - informé ou non - a décidé d'interdire ces langues. Au bout d'un moment, c'est devenu une vérité organisationnelle. Cela peut avoir été vrai à ce moment-là pour certains projets, mais les cultures de contrôle ne sont pas désireuses de changer les décisions (admettent qu'elles étaient erronées) et préfèrent plutôt règles simples. Ils prospèrent sur les règles et les règlements et cela ne fait pas importe qu'ils aient un sens ou non, c'est la sécurité perçue qui compte.

Cela se produit tout le temps dans les cultures de contrôle. Je le vois plus ou moins quotidiennement. Cela n'a aucun sens, mais c'est comme ça que ça se passe. Si vous souhaitez en savoir plus sur ce sujet très pertinent, je recommande le livre de Schneider " The Reengineering Alternative ". Voici un schéma de culture de Michael Sahoto/Agilitrix , basé sur le livre de Schneider: enter image description here

2
Martin Wickman