IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

L'ANSSI propose aux développeurs C un guide regroupant des règles et recommandations
Pour « favoriser la production de logiciels plus sécurisés, plus sûrs, d'une plus grande robustesse »

Le , par Stéphane le calme

271PARTAGES

22  0 
Le langage C offre une grande liberté aux développeurs. Cependant, il comporte des constructions ambiguës ou risquées qui favorisent l’introduction d’erreurs lors du développement. Le standard du langage C ne spécifie pas l’ensemble des comportements souhaités, et donc certains restent indéfinis ou non spécifiés. Libre alors aux développeurs de compilateurs, de bibliothèques ou de systèmes d’exploitation de faire leurs propres choix.

Pour l’Agence nationale de la sécurité des systèmes d'information (ANSSI), il est alors nécessaire de définir des restrictions quant à l’utilisation du langage C afin d’identifier les différentes constructions risquées ou non portables et d’en limiter voire interdire l’utilisation. Dans cette optique, l’agence a proposé un guide qui ambitionne de « favoriser la production de logiciels plus sécurisés, plus sûrs, d’une plus grande robustesse » en plus de favoriser leur portabilité d’un système à un autre, qu’il soit de type PC ou embarqué. Précisons que dans son document, l’Agence s’est limitée aux deux standards C90 et C99 qui restent « les plus utilisés ».

Convention de développement

Avant toute chose, tout projet de développement, quel qu’il soit doit suivre une convention de développement claire, précise et documentée. Cette convention de développement doit absolument être connue de tous les développeurs et appliquée de façon systématique. Chaque développeur a ses habitudes de programmation, de mise en page du code et de nommage des variables. Cependant, lors de la production d’un logiciel, ces différentes habitudes de programmation entre développeurs aboutissent à un ensemble hétérogène de fichiers sources, dont la vérification et la maintenance sont plus difficiles.

Aussi l’ANSSI propose la règle suivante : « Des conventions de codage doivent être définies et documentées pour le logiciel à produire. Ces conventions doivent définir au minimum les points suivants : l’encodage des fichiers sources, la mise en page du code et l’indentation, les types standards à utiliser, le nommage (bibliothèques, fichiers, fonctions, types, variables...), le format de la documentation. Ces conventions doivent être appliquées par chaque développeur ».

Cette règle autour d’une convention de développement est certes évidente et le but ici n’est pas d’imposer, par exemple, un choix de nommage de variable (tel que snake-case au lieu de camel-case), mais de s’assurer qu’un choix a bien été fait au début du projet de développement et que celui-ci est clairement explicité. En fait, au début de la réalisation d’un projet informatique, l’équipe de développement devrait toujours s’accorder sur les conventions de codage à appliquer. Le but est de produire un code source cohérent. Par ailleurs, le choix de conventions judicieuses permet de réduire les erreurs de programmation


L’ANSSI a donné un exemple de conventions de codage, prenant la peine de préciser que « certains choix sont arbitraires et discutables ». Et d’indiquer que « cet exemple de conventions peut être repris ou servir de base, si aucune convention de développement n’a été définie pour le projet à produire. Différents outils ou des éditeurs avancés sont en mesure de mettre en œuvre de façon automatique certaines de ces conventions de codage » :
  • Encodage des fichiers :
    • Les fichiers sources sont encodés au format UTF8.
    • Le caractère de retour à la ligne est le caractère « line feed » \n (retour à la ligne au format Unix)
  • Longueurs maximums :
    • une ligne de code ou de commentaire ne doit pas dépasser 100 caractères.
    • Une ligne de documentation ne doit pas dépasser 100 caractères.
    • Un fichier ne doit pas dépasser 4000 lignes (documentation et commentaires compris).
    • Une fonction ne doit pas dépasser 500 lignes.
  • Indentation du code :
    • L’indentation du code s’effectue avec des caractères espace : un niveau d’indentation correspond à 4 caractères espaces.
    • L’utilisation du caractère de tabulation comme caractère d’indentation est interdite.
    • La déclaration des variables et leur initialisation doivent être alignées à l’aide d’indentations.
    • Un caractère espace est laissé systématiquement entre un mot clé et la parenthèse ouvrante qui le suit.
    • L’accolade d’ouverture d’un bloc est placée sur une nouvelle ligne.
    • L’accolade de fermeture de bloc est également placée sur une nouvelle ligne.
    • Un caractère espace est laissé avant et après chaque opérateur.
    • Un caractère espace est laissé après une virgule.
    • Le point-virgule indiquant la fin d’une instruction est collé au dernier opérande de l’instruction.




Compilation

Les compilateurs proposent différents niveaux d’avertissement afin d’informer le développeur de l’utilisation de constructions risquées ou de la présence d’erreurs de programmation. D’un compilateur à l’autre, le comportement par défaut n’est pas identique pour une même version du standard C utilisée par exemple. Même les avertissements émis lors de la compilation sont directement liés à la version du compilateur. Il est donc primordial de connaître exactement le compilateur utilisé, sa version, mais aussi toutes les options activées avec, dans l’idéal, une justification pour chacune d’elles. De plus, les optimisations de code faites au niveau de la compilation et de l’édition de liens doivent être utilisées en pleine conscience des impacts associés au niveau de l’exécutable généré.

L’ANSSI estime que la maîtrise des actions à opérer à la compilation est nécessaire : « le développeur doit connaître et documenter les actions associées aux options activées du compilateur y compris en terme d’optimisation de code ».

Le niveau d’avertissement activé par défaut dans les compilateurs est souvent un niveau peu élevé,signalant peu de mauvaises pratiques. Pour l’Agence, ce niveau par défaut est insuffisant et doit donc être augmenté. Cela implique que les options de compilation utilisées doivent être explicitées.

Elle émet donc la règle suivante :

« Les options utilisées pour la compilation doivent être précisément définies pour l’ensemble des sources d’un logiciel. Ces options doivent notamment fixer précisément :
  • la version du standard C utilisée (par exemple C99 ou encore C90);
  • le nom et la version du compilateur utilisé;
  • le niveau d’avertissements (par exemple-Wextra pour GCC);
  • les définitions de symboles préprocesseurs (par exemple définir NDEBUG pour une compilation en release)».

Différentes options de durcissement à la compilation existent pour prévenir ou limiter l’impact, entre autres, des attaques sur le formatage de chaînes de caractères, des dépassements de tas ou de pile, de réécriture de section ELF ou de tas, l’exploitation d’une distribution non aléatoire de l’espace d’adressage. Ces options ne sont pas une garantie absolue contre ce type d’attaques, mais permettent d’ajouter des contremesures au code par rapport à une compilation sans durcissement. Ces options de durcissement peuvent avoir un lien direct avec les niveaux d’optimisation du compilateur (comme l’option-D_FORTIFY_SOURCE pour GCC qui n’est effective qu’avec un niveau d’optimisation supérieur ou égal à 1) et peuvent être actives, en partie, par défaut pour les versions les plus récentes du compilateur.

Face à cela, l’ANSSI en a fait une règle : « L’utilisation d’options de durcissement est obligatoire que ce soit pour imposer la génération d’exécutables relocalisables, une randomization d’adresses efficace ou la protection contre le dépassement de pile entre autres ». L’Agence demande cependant d’éviter les options d’optimisation du compilateur comme -fno-strict-overflow, -fwrapv,-fno-delete-null-pointer-checks, -fno-strict-aliasing qui peuvent affecter la sécurité.

La bonne pratique consiste à utiliser des générateurs de projets pour la compilation. En effet, l’utilisation d’un générateur de projets, tels que make, Cmake ou Meson, facilite la gestion des options de compilation. Celles-ci peuvent être définies de façon globale et appliquées à tous les fichiers sources à compiler.

Déclarations

Le langage C autorise la déclaration de plusieurs variables d’un même type simultanément en séparant chaque variable par une virgule. Cela permet d’associer à un groupe de variables un type donné et de regrouper ensemble des variables dont le rôle est lié. Cependant ce type de déclaration multiple ne doit être utilisé que sur des variables simples (pas de pointeur ou de variable structurée) et de même type.

L’ANSSI recommande donc que « Seules les déclarations multiples de variables simples de même type soient autorisées ».

Pour éviter également toute erreur dans l’initialisation de variables, les initialisations couplées à une déclaration multiple sont à proscrire. En effet, en cas d’initialisation unique à la fin de la déclaration multiple, seule la dernière variable déclarée est effectivement initialisée.

D’où la règle consistant à ne pas faire de déclaration multiple de variables associée à une initialisation : « les initialisations associées (i.e.consécutives et dans une même instruction) à unedéclaration multiple sont interdites ».



Déclaration libre de variables

Depuis C99, la déclaration de variables peut se faire partout dans le code. Cette fonctionnalité semble pratique, mais un abus peut complexifier de façon notable la lecture du code et peut entraîner de possibles redéfinitions de variables.

L’ANSSI recommande alors de regrouper les déclarations de variables en début du bloc dans lequel elles sont utilisées : « pour des questions de lisibilité et pour éviter les redéfinitions, les déclarations de variables sont regroupées en début de fichier, fonction ou bloc d’instructions selon leur portée ».

Cette recommandation n’est pas directement liée au sens strict à la sécurité, mais impactant la lisibilité, portabilité et/ou maintenabilité du code, elle concerne tout type de développement. Une pratique très courante pour les compteurs de boucle est de les déclarer directement dans la boucle associée. Ce cas de déclaration « libre » est accepté, mais il faut s’assurer que la variable associée à ce compteur de boucle ne masque pas une des autres variables utilisées dans le corps de la boucle.

Par exemple, dans le code suivant, les variables sont déclarées « au fil de l’eau » et non de façon groupée et structurée. Ce genre de pratique complexifie l’identification de toutes les variables déclarées augmentant par conséquent le risque de masquage des variables :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stddef.h> 
uint8_tglob_var;/*variable globale*/ 
uint8_tfonc(void){uint8_tvar1;/*variable locale*/ 
if(glob_var>=0) 
{ 
    /*...*/ 
} 
else { 
    var1=glob_var; 
    uint8_tvar2;/*autre variable locale déclarée au milieu d'un bloc*/ 
    /*...*/ 
} 
uint8_tglob_var2;/*autre variable globale déclarée entre deux fonctions*/ 
int main(void) 
{ 
    uint8_tx=fonc() ; 
    /*...*/ 
}

Le bon exemple consisterait à déclarer les variables de façon groupée et structurée en début des blocs, ce qui facilite la lecture :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stddef.h> 
uint8_tglob_var;/*variables globales déclarées ensemble*/ 
uint8_tglob_var2 
  
uint8_tfonc(void) 
{ 
    uint8_tvar1;/*variables locales déclarées ensemble au début de la fonction*/ 
    uint8_tvar2; 
    if(glob_var>= 0) 
   { 
        /*...*/ 
   } 
   else{ 
       var1=glob_var; 
   } 
   /*...*/ 
} 
int main(void) 
{  
    uint8_tx=fonc() ; 
    /*...*/ 
}
Le guide est très riche et aborde plusieurs aspects allant de la gestion des erreurs aux types et transtypages en passant par les pointeurs et tableaux.

Source : guide des règles de programmation pour le développement sécurisé de logiciels en C

Et vous ?

Que pensez-vous de cette initiative ?
Quelles sont les recommandations ou règles qui vous intéressent le plus.

Voir aussi :

Rust entre dans le top 20 de l'indice Tiobe de popularité des langages de programmation pour la première fois, C conserve la tête du classement et Java la seconde place
Le langage C dépasse Java et devient le langage le plus populaire selon l'index TIOBE et Rust se rapproche du top 20 du classement
Apple recherche des ingénieurs logiciels afin de convertir une base de code établie en C vers Rust
Le langage C ne sera-t-il jamais battu en termes de rapidité d'exécution et de faible consommation d'énergie ? Voici les résultats d'une étude sur 27 langages de programmation les plus populaires

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 06/06/2020 à 23:04
Bonjour
Citation Envoyé par Stéphane le calme Voir le message
Que pensez-vous de cette initiative ?
Sans déconner, c'est quoi ces règles à la c.. J'ai ouvert le topic en pensant trouver des trucs comme "utiliser strncpy() à la place de strcpy()" ou "utiliser calloc() à la place de malloc() quand on alloue une chaine". Mais oser pondre autant de pages pour sortir de telles absurdités ??? Ca pourrait presque faire une bonne blague si ce n'était pas aussi triste.
Et ils osent sous-titrer ce pignolage "pour favoriser la production de logiciels plus sécurisés, plus sûrs, d’une plus grande robustesse" ??? Elle est où la robustesse à indenter avec 4 espaces (quand on est au 4° sous-niveau on passe plus de temps à compter qu'à coder), à mettre l'accolade sur la ligne suivante (en plus qu'ils nous limitent en nombre de lignes !!!) et à aligner bien verticalement le "=0" de toutes les variables ?????
Bon ok dans le guide complet (ici on n'a qu'un extrait) il y a des trucs effectivement sensés comme encadrer les paramètres de macros avec des parenthèses comme define CARRE(x) (x) * (x) ce qui permet d'appeler CARRE(2+3) et avoir le bon résultat. Mais déjà ce genre de truc est expliqué dans tous les tutos, et malheureusement il n'est même pas écrit d'encadrer la macro avec des parenthèses comme #define SOMME(x, y) ((x) + (y)) ce qui permet d'avoir là aussi le bon résultat si on appelle 2 * SOMME(2, 3).
Et ils disent de ne pas faire réaliser d'opération aux arguments d'une macro (ex CARRE(i++) qui produit un effet de bord catastrophique) mais nulle part ils expliquent qu'il existe des fonctions qui sont en réalité des macros cachées pour optimiser leurs appels. Ce qui fait que si on réalise une opération dans l'argument de ce qu'on croit être la fonction, on peut obtenir aussi un effet de bord et que donc en fait il ne faut jamais réaliser d'opération dans tout argument de quoi que ce soit. Donc même quand ils sont "un peu" dans le vrai en expliquant des trucs réels (mais qui font partie du B+A=BA) ils en ratent plus de la moitié.
Et enfin il y a le n'importe quoi juste pour faire genre "voyez comme on a super cogité". Comme mettre sur une ligne les déclarations de variables qui vont ensembles, mais pas s'il y a des pointeurs, et faire attention à l'initialisation quand on a plusieurs variables ensembles... c'est d'un compliqué mais d'un compliqué... On a l'impression que plus la règle est complexe, plus elle sera efficace. Perso je mets toujours une variable par ligne, comme ça c'est plus simple pour tout le monde. et si je dois lier deux items ensembles (comme par exemple un tableau alloué et sa taille) alors je mets tout ça dans une stucture comme ça je suis certain que ces items seront toujours ensembles. Voilà. Comme ça tout est bien plus simple. Ok ça m'oblige à définir une structure mais justement c'est ça la base de la robustesse d'un code : commencer par préparer des fondations bien solides (et je n'ai pas besoin de 174 pages pour l'expliquer).

Citation Envoyé par Stéphane le calme Voir le message
Quelles sont les recommandations ou règles qui vous intéressent le plus.
Ben... aucune. A 99% c'est n'importe quoi et le pauvre "1%" de vrai et sensé c'est tellement basique que tout le monde le sait déjà. Perso je dirais "ne pas se préoccuper de ces recommandations inutiles"...

[edit]Ca y est, j'ai réalisé mon erreur. Ce n'est pas l'ANSI qui a pondu cette bouse, mais l'ANSSI (j'avais mal lu hier soir). Punaise maintenant tout s'explique !!! Ok, donc pour leur niveau c'est en effet un travail superbe, pas besoin d'en dire plus. Pardon aux gars de l'ANSI que j'ai insulté toute cette nuit dans mes rêves.
16  0 
Avatar de Pyramidev
Expert éminent https://www.developpez.com
Le 06/06/2020 à 18:46
Citation Envoyé par Page 32 du document
RECOMMANDATION — Regrouper les déclarations de variables en début du bloc dans lequel elles sont utilisées
Décidément, ils vivent dans le passé.

Regrouper les déclarations de variables en début de bloc, dans un langage comme C, implique souvent de séparer la déclaration de l'initialisation. Alors, le nombre de lignes est plus élevé, les variables ne peuvent pas être const et les variables ont une portée plus élevée.
En bonus, cela peut aussi accroître le risque de lire des variables non initialisées, quoique ce risque dépend des avertissements activés du compilateur et des éventuels outils d'analyse statique du code.

Le fait de trouver plus lisible de regrouper les déclarations de variables en début de bloc est surtout dû au fait que les vieux développeurs utilisaient des langages qui les obligeaient de déclarer les variables trop tôt. Du coup, plusieurs de ces vieux développeurs ont été conditionnés à trouver ça plus lisible et cet héritage culturel se propage même à certain développeurs plus jeunes.
12  0 
Avatar de Matthieu Vergne
Expert éminent https://www.developpez.com
Le 07/06/2020 à 1:10
Pour faire simple : tout pareil que Neckara.

Franchement, la tabulation a le mérite d'avoir une sémantique plus proche de l'indentation (1 tab = 1 niveau) qu'un nombre arbitraire d'espace ou chacun bataille pour faire 2, 3, 4, 6, 8, 42 espaces. En plus, la taille de la tabulation a justement cette flexibilité que l'espace n'a pas : elle est généralement réglable dans les paramètres du logiciel. En bref, utilisez la tabulation et réglez la sur la taille qui vous plaît, comme ça tout le monde utilise la même convention, chacun avec le rendu qui lui convient, et tout le monde il est content.

Pareil pour l'accolade à la ligne : inutile si on utilise de l'indentation, ça fait double emploi et rajoute des lignes inutilement.

Je rajouterai aussi que faire passer une fonction de 500 lignes pour de la bonne pratique, c'est se foutre du monde. Quant aux fichiers de 4000 lignes de code... Ce genre de limites devrait être beaucoup plus bas et en tant que recommandation, de façon à ne permettre les gros éléments que pour des exceptions. Par exemple une façade qui agrège plusieurs classes, qui a donc de bonnes chances d'être grosse mais qui en contre partie n'a aucune complexité.

Ça fait pleurer de lire des horreurs pareilles en 2020.
11  2 
Avatar de foetus
Expert éminent sénior https://www.developpez.com
Le 06/06/2020 à 21:13
Citation Envoyé par Neckara Voir le message
Tabulation 4 life.
La réponse n'est pas aussi tranchée

En début de ligne, moi aussi je mets des tabulations en C/ C++.

Mais en milieu de ligne (par exemple, pour aligner les signes égale dans 1 même colonne), je mets des espaces. Pour "figer mon ASCII art" <-
7  0 
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 09/06/2020 à 20:22
Citation Envoyé par moldavi Voir le message
C'est une très bonne initiative, et je trouve l'ensemble des règles proposées très correctes, pour des débutants.
Oui... sauf que ce n'est pas une école pour débutants qui a pondu ça. C'est l'autorité nationale en matière de sécurité et de défense des systèmes d'information. C'est l'organisme qui contrôle et définit les règles concernant la doctrine d'emploi et la sécurité de nos réseaux militaires. Réseaux qui emploient autre chose que des débutants et dont certains sont d'un haut niveau d'habilitation. Et donc aux programmeurs qui évoluent dans ces divers milieux on va leur dire "pour sécuriser tes programmes, il te faut aligner bien verticalement les "=0", compter les espaces pour indenter et mettre des accolades sur la ligne suivant le if (tout en te limitant par ailleurs en nombre de lignes)"...
6  1 
Avatar de Neckara
Inactif https://www.developpez.com
Le 06/06/2020 à 21:05
Citation Envoyé par Stéphane le calme  Voir le message
  • Indentation du code :
    • L’indentation du code s’effectue avec des caractères espace : un niveau d’indentation correspond à 4 caractères espaces.
    • L’utilisation du caractère de tabulation comme caractère d’indentation est interdite.


JAMAIS !

Tabulation 4 life.

Citation Envoyé par Stéphane le calme  Voir le message



    • La déclaration des variables et leur initialisation doivent être alignées à l’aide d’indentations.


Boff.

Citation Envoyé par Stéphane le calme  Voir le message


  • L’accolade d’ouverture d’un bloc est placée sur une nouvelle ligne.


Boff.
5  1 
Avatar de defZero
Membre extrêmement actif https://www.developpez.com
Le 06/06/2020 à 21:39
Que pensez-vous de cette initiative ?

Quelqu'un à l' ANSSI devait ce demander quoi foutre de ses journées apparemment .
Plus sérieusement, la plus part de leurs "directives", sont des bonnes pratiques déjà largement utilisé en environnement pro.
Après je ne sais pas pour l' ANSSI, mais la majorités des projets sur lesquelles j'ai bossé ces 20 dernières années avaient déjà largement résolut ce type de problème avec du tooling.
Je ne sais pas pour les autres, mais personnellement, quand je code en locale, je le fait avec mes conventions préféré et avant de commit mes changements passent à la moulinette via hooks, fmt ou autres, pour correspondre aux desiderata du chef de projet.
Donc, merci de bosser sur du vent, ça nous fait de l'air .

Petite pic à l'ANSSI, mais donner des directives en 2020 sur comment standardiser du code C90/99 pour de nouveaux projets, c'était vraiment pas votre meilleur idée.
A part des Transpiler vers C90/99, qui n'utilise pas au moins C11 (qui lui supporte Unicode en natif) dans ses nouveaux projets.
5  1 
Avatar de
https://www.developpez.com
Le 07/06/2020 à 10:14
Citation Envoyé par Madmac Voir le message
Et pour un langage interprété, la tabulation permet un démarrage plus rapide, car le programme a moins de caractères.
Oui... Et pour que ce soit encore plus rapide, on peut écrire tout le code sur une seule ligne et utiliser des noms de variable à une seule lettre.

Citation Envoyé par Matthieu Vergne Voir le message
Je rajouterai aussi que faire passer une fonction de 500 lignes pour de la bonne pratique, c'est se foutre du monde. Quant aux fichiers de 4000 lignes de code... Ce genre de limites devrait être beaucoup plus bas et en tant que recommandation, de façon à ne permettre les gros éléments que pour des exceptions. Par exemple une façade qui agrège plusieurs classes, qui a donc de bonnes chances d'être grosse mais qui en contre partie n'a aucune complexité.
Oui la façade qui agrège plusieurs classes, c'est un problème classique en C...

Chaque langage a ses particularités. Une même recommandation peut être bonne dans un langage mais mauvaise dans un autre. Le C est un langage procédural donc n'y a pas les mêmes fonctionnalités qu'en POO pour organiser le code. De plus, c'est un langage bas-niveau donc il faut souvent plus de code qu'un langage haut-niveau pour faire la même chose. Perso, j'aime bien faire des fonctions courtes et bien testées mais ça ne me choque pas de voir une fonction C de plus de 100 lignes, du moment qu'elle reste lisible et testable.
5  2 
Avatar de emixam16
Membre chevronné https://www.developpez.com
Le 08/06/2020 à 17:40
Totalement d'accord avec la plupart des interventions ici. La majorité de ces règles sont soit triviales soit inutiles.

Et puis bon en pratique, hein ^^


3  0 
Avatar de bpy1401
Membre éclairé https://www.developpez.com
Le 31/08/2020 à 11:44
Bonjour à tous

Dans un style plus évolué, j'ai connu le Misra https://fr.wikipedia.org/wiki/MISRA_C pour le C embarqué, et la c'était des règles utiles et pas des règles de présentation du code (par contre c'est pas gratuit).
Pour le Misra, il existe un tas d'outil qui test le code, on peut donc facilement vérifier le respect des règles.

Avec cette norme ANSSI, j'attend de voir si des outils sont capables de faire les tests de respect des règles.

A+
3  0