web-dev-qa-db-fra.com

Variables globales et sécurité de l'information

J'ai l'impression que c'est une bonne pratique de programmation de créer des variables dans des portées spécifiques (comme une portée de fonction) et d'éviter la portée globale pour rendre les choses plus modulaires et mieux organisées. Cependant, je ne sais pas s'il y a également un problème de sécurité.

Voici un exemple de variables globales dans Bash qui ont bien fonctionné pour moi plus d'un an:

cat <<-EOF >> "$HOME"/.profile
    set -x
    complete -r
    export war="/var/www/html" # Web Application Root;
    export dmp="phpminiadmin" # Database Management Program;

    export -f war
    war() {
        cd $war/
    }
EOF

source "$HOME"/.profile 2>/dev/null

Je n'ai jamais eu de problème avec les variables globales dans Bash ou JavaScript, probablement parce que je n'ai écrit que de petits scripts pour un usage personnel dans des environnements minimalistes.

Pourquoi de nombreux programmeurs évitent-ils d'utiliser des variables globales et existe-t-il des exemples de violations de sécurité causées par l'utilisation de variables globales?

47
user123574

Boycottez Globals!

Je vole commentaire de Steffen Ullrich , mais le principal problème avec les variables globales est qu'elles rendent difficile la bonne organisation et la maintenance du code. Son lien est parfait, mais vous n'aurez aucun mal à trouver d'innombrables articles sur les problèmes des variables globales en ligne.

Lorsque vous utilisez des variables globales, il devient facile de perdre de vue où dans votre programme la variable est modifiée, surtout si vous n'avez pas un simple flux linéaire. Par conséquent, les variables globales peuvent parfaitement fonctionner dans de petits scripts, mais peuvent provoquer des maux de tête massifs lorsqu'une application commence à évoluer.

La principale raison pour laquelle j'éviterais les globaux est qu'ils font des tests automatisés d'unité/d'intégration un cauchemar. Les petites applications peuvent survivre sans tests, mais essayer de gérer une application plus grande sans de bons tests n'est qu'un cauchemar (croyez-moi, j'ai essayé dans mes jours jeunes et insensés).

Cela peut vous donner l'impression que les globaux fonctionnent bien dans de très petites applications, mais comme les applications ne se développent généralement qu'avec le temps et que les choses qui commencent temporairement deviennent permanentes, ce n'est vraiment pas une bonne idée de les utiliser. Pourquoi commencer du mauvais pied, alors qu'il est si facile d'utiliser des variables de portée appropriée?

Sécurité

L'utilisation de variables globales n'a pas d'implications directes pour la sécurité, mais elles facilitent la résolution des problèmes de sécurité:

Si un programmeur modifie une fonction dans le fichier A mais ne sait pas ou oublie que la valeur d'une certaine variable dans cette fonction provient de l'entrée utilisateur dans le fichier B, il pourrait finir par ne pas être sûr les choses sur l'entrée de l'utilisateur sans protections appropriées.

Variables globales == décès

Je ne connais pas de violations qui se sont produites spécifiquement en raison de variables globales, mais il est facile de soutenir que l'utilisation de variables globales a littéralement tué des gens , donc je pense qu'il est raisonnable de ne jamais les utiliser.

78
Conor Mancone

Dans certains environnements de programmation, vous ne pouvez pas faire confiance à la portée globale car d'autres sources pourraient lire /jouer avec.

Cela a déjà conduit à certaines vulnérabilités critiques dans les produits sensibles à la sécurité, comme une exécution de code à distance dans l'extension de navigateur LastPass .

10
Benoit Esnard

Ils peuvent donner aux injections de code un accès plus facile à tout

En PHP il y a le superglobal $GLOBALS; En Python il y a la fonction globals(), et en Javascript il y a l'objet window (ou dans Node, process). Tous ces éléments rendent trivial l'énumération de toutes les variables globales.

Une fois que vous avez fait cela, vous pouvez à la fois aspirer les informations qu'ils contiennent et les modifier de manière malveillante. Par exemple, si des données sensibles sont stockées dans un global, un attaquant pourrait facilement les extraire. Si, par exemple, une URL de connexion à la base de données est stockée dans un global, un attaquant pourrait pointer l'URL pour qu'il se réfère au serveur de l'attaquant à la place, puis la victime tenterait de se connecter à ce serveur et d'envoyer des informations d'identification.

De plus, si vous avez beaucoup d'objets globaux , vous pouvez facilement appeler leurs méthodes.

Ils violent le "principe du moindre privilège"

Ce principe de l'ingénierie de sécurité dit essentiellement "ne donnez pas à quelqu'un plus d'accès que nécessaire pour faire son travail". Chaque fonction et méthode peut lire et écrire des variables globales même si elles sont totalement hors de propos pour son travail, ce qui signifie que si ce morceau de code est piraté même de manière limitée, il ouvrira une plus grande surface d'attaque.

(Bien sûr, la plupart des langages de script ont des règles d'encapsulation faibles, ce qui est également une violation de POLP, mais si les objets sont hors de portée, il est beaucoup plus difficile de leur faire quoi que ce soit.)

Ils sont un terrain fertile pour le code buggy

Pour des raisons telles que:

  • le programmeur oubliant que l'une des variables dans une fonction est un global et donc les modifications qui y sont apportées persisteront à la fin de la fonction,
  • la sensibilité non évidente du programme à l'ordre dans lequel les fonctions sont appelées
  • la facilité avec laquelle les défaillances en cascade peuvent se produire (par exemple, lorsqu'une fonction définit un global sur null et plus tard, une autre se bloque)
  • les interactions très complexes qui peuvent facilement se produire et sont difficiles à raisonner

Ils sont tout simplement désordonnés

Si vous pensez à une entreprise où il y a des papiers éparpillés partout, les choses ne sont pas organisées et classées soigneusement, mais traînent, qui va remarquer si quelque chose manque? Si un employé commet une fraude, personne ne remarquera l'écart, car les écarts sont partout. Si une paire de clés disparaît, quelqu'un supposera simplement qu'il est enterré sous quelque chose ou que quelqu'un a dû l'emprunter.

Maintenant, vous pourriez dire que c'est une exagération, mais si c'est le cas, je doute que vous ayez déjà travaillé avec un grand projet logiciel . Les globaux sont OK si votre site Web est assez petit pour être construit en quelques jours (et vous savez ce que vous faites). Ils peuvent gagner du temps pour faire avancer les choses.

Mais ils n'évoluent pas.

8
Artelius

Veuillez comparer les morceaux de code suivants:

1)

/file.php:
    $newconn = new PDO('mysql:Host=localhost;charset=utf8mb4;','username','password');

2)

../secrets.php:
    $mysqlusername = 'username';
    $mysqlpassword = 'password';

/file.php:
    require_once('../secrets.php');
    $newconn = new PDO('mysql:Host=localhost;charset=utf8mb4;',$mysqlusername,$mysqlpassword);

3

../secrets.php:
    function pdoconn(i) {
        $mysqlusername = ['username1','username2'];
        $mysqlpassword = ['password1','password2'];
        $conn = new PDO('mysql:Host=localhost;charset=utf8mb4;',$mysqlusername[i],$mysqlpassword[i]);
        return $conn;
    }

/file.php:
    require_once('../secrets.php');
    $newconn = pdoconn(0);

L'exemple 1 est hors de question - une configuration incorrecte sur les serveurs de production pourrait finir par montrer des paramètres sensibles à des parties non intentionnelles.

L'exemple 2 est meilleur, mais ces variables sont disponibles dans toute l'application et modifiables, ce qui pourrait entraîner des erreurs.

L'exemple 3 garde les choses très organisées et transférables.
Il peut être modifié pour utiliser des globaux si ../secrets.php était à la place:

../secrets.php:
$mysqlusername = 'username';
$mysqlpassword = 'password';
function pdoconn()  {
    $conn = new
PDO('mysql:Host=localhost;charset=utf8mb4;',$GLOBALS['mysqlusername'],$GLOBALS['mysqlpassword']);
return $conn;
}

Et je pense que cela montre pourquoi un global n'a pas de sens la plupart du temps d'une manière assez succincte.


Sommaire:

En ce qui concerne les failles de sécurité utilisant des variables globales (et pourquoi j'ai écrit ces exemples en PHP), il y avait tout à fait (à l'époque) changement controversé dans PHP 4.2. où register_globals a été désactivé. Je ne trouve aucun article maintenant, car ce changement a été effectué en 2002, mais il me semble me souvenir qu'il était responsable de quelques violations à l'époque. Copiant directement du manuel, il y a un exemple très clair de code vulnérable:

<?php
// define $authorized = true only if user is authenticated
if (authenticated_user()) {
    $authorized = true;
}

// Because we didn't first initialize $authorized as false, this might be
// defined through register_globals, like from GET auth.php?authorized=1
// So, anyone can be seen as authenticated!
if ($authorized) {
    include "/highly/sensitive/data.php";
}
?>
6
LTPCGO

Parce que l'utilisation de variables globales vous pose un tas de problèmes, et pas vraiment d'avantages.

Les globaux rendent votre code difficile à tester

Bien qu'ils ne soient pas directement liés à la sécurité, les tests unitaires sont essentiels au développement. Et pour tester correctement quelque chose, vous devez pouvoir contrôler le contexte exact dans lequel le code est exécuté. Regardez les deux morceaux de code:

Avec Globals

public Money CalculateExpenses(int departmentId)
{
    Department department = (from d in global_db.Departments
    where d.Id = departmentId
    select d).Single();

    return (from ex in department.Expenses
            select ex).Sum();
}

Sans globales

public Money CalculateExpenses(int departmentId, Database database)
{
    Department department = (from d in database.Departments
    where d.Id = departmentId
    select d).Single();

    return (from ex in department.Expenses
            select ex).Sum();
}

Vous pourriez dire que le code semble presque identique, sauf d'où viennent les données. Vous pourriez même penser que le code avec Globals est "plus propre", car vous n'avez pas à passer la base de données à chaque fois.

Jusqu'à ce que vous deviez passer un test unitaire. Parce qu'alors vous devrez insérer une logique pour ne pas vous connecter à la base de données de production, mais plutôt une base de données locale, peut-être spécifique au test. Maintenant, le code non global semble beaucoup mieux, car vous pouvez simplement transmettre la base de données que vous souhaitez utiliser au code.

En résumé: Le code sans globaux est plus facile à tester.

Les globaux changent de comportement si vous ne changez pas le code

Si votre code contient environ 200 lignes de code, l'utilisation de Globals ne semble pas si mauvaise. En fait, cela peut sembler une chose parfaitement valable à faire si vous réutilisez le même morceau de code partout (par exemple, une source de données aléatoire, la journalisation, etc.)

Cependant, une fois votre base de code suffisamment développée, l'introduction de globaux peut être absolument mortelle. Vous perdez simplement le contrôle de l'origine de vos données et des personnes qui y ont accès.

Prenons un exemple dans la langue préférée de tous: PHP 5. Exemple par Eevee .

    @fopen('http://example.com/not-existing-file', 'r');

Que fait ce code? Vous ne savez pas, car cela dépend du comportement global. Si PHP a été compilé avec --disable-url-fopen-wrapper, Alors cela ne fonctionnera pas. Même chose pour la configuration globale allow_url_fopen. Nous ne savons pas ce qu'il fera.

Le @ Au début désactivera les messages d'erreur, à moins que scream.enabled Soit défini dans la configuration globale PHP. Il ne sera pas non plus imprimé si le global error_reporting N'est pas défini. L'endroit où il sera imprimé, s'il s'imprime, dépendra du display_errors Global.

Donc, comme vous l'avez vu, le comportement d'une ligne inoffensive dépend de 5 variables globales différentes. Imaginez maintenant ce scénario dans une base de code avec un million de lignes et 100 programmeurs différents, répartis sur différentes équipes, et vous verrez rapidement pourquoi l'utilisation de Globals est odieuse et conduit à toutes sortes de problèmes.

Imaginez que vous alliez travailler un jour et votre code se casse soudainement, même si vous ne l'avez pas touché. C'est la magie des Globals.

Il y a plus d'exemples de pourquoi l'utilisation de Globals est mauvaise, qui peuvent être trouvés dans les autres réponses.

4
MechMK1

Attaques de dépassement de tampon

Bien qu'ils soient assez spécifiques au langage (C/C++ me vient à l'esprit) et difficiles à retirer, ils constituent une attaque potentielle.

Computerphile a un grande vidéo à ce sujet, que je recommande de tout cœur, mais voici une version courte de la mécanique de ces attaques et de la façon dont elle interagit avec les variables globales.

Un débordement de tampon se produit lorsque les données écrites dans un tampon corrompt également les valeurs de données dans les adresses de mémoire adjacentes au tampon de destination en raison d'une vérification des limites insuffisante. Cela peut se produire lors de la copie de données d'un tampon vers un autre sans d'abord vérifier que les données tiennent dans le tampon de destination. ( Wikipedia )

Dans le cas des variables globales, la disposition de la mémoire de votre programme est assez statique car (dans la plupart des langues), leur segment de mémoire sera affecté au démarrage du programme. Cela donne à l'attaquant une cohérence: il est beaucoup plus facile de cibler la zone de mémoire d'une zone différente lorsque vous tirez toujours au même endroit (dans le cas des globaux) que lorsque vous êtes toujours en mouvement entre les tirs.

Ce n'est pas spécifique aux variables globales, mais contenir des informations significatives dans de telles structures facilite (à ne pas confondre avec l'activation!) De ce type d'attaques.

Il faut se méfier!

Bien que les variables globales présentent potentiellement un léger risque pour la sécurité, si vous craignez des attaques par dépassement de tampon, votre mécanisme de défense préféré devrait vérifier la taille de l'entrée, pas le code de refactorisation pour se débarrasser des variables globales.

3
Mister Amen

Je n'ai jamais eu de problème avec les variables globales dans Bash ou JavaScript, probablement parce que je n'ai écrit que de petits scripts pour un usage personnel dans des environnements minimalistes.

Pourquoi de nombreux programmeurs évitent-ils d'utiliser des variables globales et existe-t-il des exemples de violations de sécurité causées par l'utilisation de variables globales?

Dans les petits projets, les variables globales sont très bien. La plupart de leurs problèmes n'apparaissent pas. Et la personnalisation de l'environnement Bash ci-dessus est en effet minuscule.

Les variables globales commencent à causer des problèmes lorsque votre programme échelles ; ce qui peut se produire de différentes manières, mais la partie centrale du problème est la quantité d'état que nécessite la compréhension de chaque morceau de code.

Imaginez une base de code de 10 millions de lignes. Il contient 1 million de variables. Tous sont mondiaux.
Dans un morceau de code donné de 100 lignes, vous pouvez utiliser 20 variables.
Chaque fois que vous appelez une fonction, vous ne savez pas laquelle de ces 20 variables cette fonction va modifier; lorsque votre fonction démarre, vous ne savez pas d'où vient l'état variable.
Donc, pour comprendre ce que vos 100 lignes de code signifient, vous devez soit comprendre et garder en tête les 10 millions de lignes de code, soit vous avez besoin d'une sorte de convention sur la provenance des données, quelles données peuvent et ne peut pas être modifié lorsque vous appelez une fonction d'assistance, etc.

En revanche, si votre code est presque entièrement alimenté par des arguments de fonction, des variables locales et des types de retour, vous n'avez qu'à comprendre votre fonction pour comprendre ce qu'elle fait . De plus, si cette fonction appelle une autre fonction, vous savez quelles informations vous lui transmettez et quelles informations elle lui transmet.
Vous ne savez peut-être pas ce que fait avec ces informations, mais vous savez ce que cela ne fait pas ' t faire .
Par exemple, il ne peut pas modifier integer x et vous êtes certain, car vous n'avez pas passé x à la fonction, et vous ne lui avez pas non plus attribué de valeur de retour!

Rendre le code plus facile à raisonner localement est un objectif très important. Vous voulez pouvoir lire une fonction, savoir ce qu'elle fait et identifier les bogues.

Écrire du code est beaucoup plus facile et beaucoup moins important que de créer un code clair et facile à raisonner.
Presque tout le code d'un grand projet persistant est lu 100 fois plus souvent qu'il n'est modifié, et il est écrit et conçu une fois.
A partir de cette règle - rendre les choses plus faciles à raisonner localement - nous atteignons la règle "éviter l'état global".

Variable globale contre super-objet

Les variables globales lues/écrites sont un exemple d'état global. Mais il peut aussi en être un super-objet que tout dans votre projet ait un pointeur à passer comme premier argument.

Un super-objet a toujours des avantages sur les variables globales; les variables globales ont souvent des mécanismes déroutants sur la façon dont elles sont initialisées et nettoyées (je vous regarde, C/C++), et si vous avez un super-objet avec tous vos états "globaux", vous pouvez instancier deux versions de votre super-objet et exécutez les deux en même temps dans le même processus (cela aura tendance à ne pas fonctionner, mais cela est généralement dû à un état global implicite l'OS vous impose).

Chaque variable globale que vous lisez, au lieu d'un paramètre, signifie que n'importe quel bit de code , n'importe où pourrait modifier son comportement. Chaque variable globale dans laquelle vous écrivez signifie que vous modifiez arbitrairement le comportement de certains codes.

Et ce ne sont pas seulement les humains qui trouvent que les états mondiaux sont pénibles; si votre état est local, l'écriture de faisceaux de tests qui "simulent" un état ou un environnement global devient beaucoup plus facile. S'il y a (disons) un objet "souris" global, l'écriture d'un test unitaire qui crée une fausse souris qui prétend faire certains mouvements devient plus difficile, il peut même être plus difficile d'écrire une macro qui lit un mouvement de souris enregistré dans du code, surtout si vous avez l'intention de le faire alors que l'interface utilisateur reste sensible à l'humain réel à l'aide d'une souris.

Sécurité

La sécurité est une fonction de comprendre ce que fait votre code. La sécurité, au sein d'un programme, signifie "votre code ne fait que ce qu'il est censé être autorisé à faire"; avec les variables globales, vous avez une plus faible capacité à comprendre ce que fait le code, donc moins de capacité à contrôler ce qu'il fait.

3
Yakk

Du point de vue de la sécurité, le problème avec les variables globales est qu'elles peuvent créer un comportement inattendu et interrompre la localisation. L'idée plus générale derrière cela s'appelle "Action à distance" où une partie d'un programme peut avoir un effet inattendu sur une autre.

Étant donné que la localisation est interrompue, au lieu d'avoir à comprendre seulement une partie du programme, vous et toute personne qui travaille avec le programme doivent maintenant comprendre n'importe quelle partie du programme qui pourrait impliquer votre variable globale. Cela augmente considérablement la complexité de la conception, et en général, les conceptions complexes sont plus susceptibles d'être boguées, et donc non sécurisées. Comme l'a dit un jour l'informaticien Edsger Dijkstra, "le programmeur compétent est pleinement conscient de la taille strictement limitée de son propre crâne".

Les programmes ont tendance à croître avec le temps en taille et en portée. Même si votre simple script, créé isolément, ne fait plus qu'une seule chose sans modularité, il peut être réutilisé ailleurs dans le futur où vous pourriez avoir oublié les implications désagréables des variables globales, ou pire, vous êtes parti et personne ne sait même qu'il contient des variables globales. Les variables globales sont comme laisser des trous dans le sol, ou des râteaux orientés dans le mauvais sens prêts à marcher, ou des clous dans votre entrée.

Ils sont, à bien des égards, en danger imminent et, bien que parfois nécessaires, ils devraient être traités comme tels.

1
Steve Sether
Pourquoi de nombreux programmeurs évitent-ils d'utiliser des variables globales et existe-t-il des exemples de violations de sécurité causées par l'utilisation de variables globales?

Parce qu'il est plus facile de modifier du code qui n'utilise pas de variables globales pour s'exécuter dans des applications involontaires, des applications que le développeur d'origine ne prévoyait pas. En réalité, si vous dépendez de l'état global, vous pouvez avoir au plus un de ces états dans une application sans refactorisation intensive (ce qui aurait pour effet de refactoriser l'état global).

Ces applications involontaires peuvent inclure l'exécution du code dans des environnements multithreads.

Cependant, ce que beaucoup de gens ne réalisent pas: malloc() est globale!

Les mêmes personnes qui plaident contre l'utilisation de variables globales utilisent toujours un état d'allocateur global.

Cela signifie que si un morceau de votre code alloue beaucoup de mémoire, ce qui fait que malloc() demande plus de mémoire au système d'exploitation, les blocs de mémoire sont mélangés avec ceux alloués par d'autres morceaux de votre code, et lorsque le même morceau de code libère toute sa mémoire qu'il utilisait, les appels free() ne retournent pas de mémoire au système d'exploitation en raison de la fragmentation. Le résultat final est que votre programme utilise plus de mémoire qu'il ne devrait.

Ainsi, ceux-là mêmes qui déconseillent d'utiliser des variables globales, utilisent en fait un allocateur avec un état global.

Je me demande pourquoi aucune des normes des principales organisations de normalisation (C89/C99/C11/C18 par ISO, POSIX par IEEE) n'a défini un allocateur qui vous permet d'avoir plusieurs états d'allocateur, c'est-à-dire des tas indépendants.

0
juhist

Si à peu près tous les langages de programmation peuvent déclarer une variable globalement, accessible uniquement dans le cadre de son code d'implémentation, alors ce n'est pas un problème. Il n'y a pas beaucoup de raisons d'utiliser des variables globales.

Pourtant, ce n'est pas le cas, c'est pourquoi lorsque je me suis retrouvé à écrire mes projets en C et non en C++, où il est naturellement pris en charge en tant que variable statique à la clause la plus externe d'une définition de classe, j'ai eu se contenter de variables globales.

0
Dehbop