Difference between revisions of "Secure programming/fr"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(28 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
{{Secure Programming}}
 
{{Secure Programming}}
  
== Vers l'avant  ==
+
= Avant-propos =
Cette page de wiki est une tentative d'enseigner une approche différente dans la façon créer un logiciel. La page utilise des exemples très simples pour montrer que beaucoup de problèmes peuvent être profitables pour prendre l'avantage dans le but de créer une attaque de sécurité sur un ordinateur, un programme ou un système en entier.
+
Cette page de wiki tente d'enseigner une approche différente dans la façon créer un logiciel. La page utilise des exemples très simples pour montrer que beaucoup de problèmes peuvent être profitables pour prendre l'avantage dans le but de créer une attaque de sécurité sur un ordinateur, un programme ou un système en entier.
  
Veuillez noter que le document  est seulement un début sur la technologie sur comment écrire un meilleur et un peu plus sécurisé code, mais il n'essaye pas d'être un guide complet sur la façon de le faire. En fait c'est seulement un résumé de la façon dont nous devons voir notre code et programme , et comment éviter beaucoup de problèmes communs.
+
Veuillez noter que le document  est seulement un début sur la technologie sur comment écrire un meilleur et un code un peu plus sécurisé code, mais il n'essaye pas d'être un guide complet sur la façon de le faire. En fait c'est seulement un résumé de la façon dont nous devons voir notre code et programme , et comment éviter beaucoup de problèmes communs.
  
Veuillez vous rappeler que ce document est au sujet de l'instruction pour un meilleur codage , pas sur la façon de hacker(bidouiller) ou cracker (casser la protection) de programmes.
+
Veuillez vous rappeler que ce document vise un meilleur codage et n'est pas là pour montrer comment hacker(bidouiller) ou craquer (casser la protection) de programmes.
  
== Information générale  ==
+
= Ne faites pas confiance aux entrées =
When developing a program, it is likely that it will interact with the user in some way, even if that means only reading files in the system and presenting the data.
+
En développant un programme, il est probable qu'il interagira avec un utilisateur de différentes façons, même s'il se limite à lire des fichiers dans le système et présenter les données.
  
Usually at schools and at universities when one starts to write programs, that person learns how to receive input, while teachers usually say to that person “assume that the data you receive is valid�?. That's when the problems begin.
+
Généralement à l'école et à l'université, quand quelqu'un commence à écrire des programmes, cette personne apprend comment recevoir des données, les professeurs disent généralement à cette personne "supposez que les données que vous recevez sont valide".
  
From the second that a program receives an input, we can not trust any unknown input that we can not control.  
+
Mais '''Nous ne pouvons pas faire confiance à toute entrée que nous ne pouvons pas contrôler car son contenu est inconnu et pourrait exploiter une vulnérabilité dans notre logiciel.'''
  
Reading from a file is reading an untrusted input, and so is reading users input, or accepting input from a network for example.
+
Lire depuis un fichier est lire depuis une source non sûre, de même que les entrées d'un utilisateur ou les données provenant d'un réseau par exemple.
  
=== Why can't I trust an input ? ===
+
== Pourquoi ne dois-je pas croire aux entrées ? ==
In order to understand why an input is dangerous, we first need to understand what is an input.
 
  
An input can be from a key stroke, and mouse movement or mouse button clicks, or from reading and accepting information from many other ways like a data stream or even system functions.
+
Pour comprendre pourquoi une entrée est dangereuse, nous devons d'abord comprendre ce qu'est une entrée.
  
It does not matter what is the type of input, because the user can give us wrong input, and the reasons can be intentional or a mistake. You can not control this input, and the main reason is that you can't guess what will the input be.
+
Une entrée peut être lue depuis un clavier, le mouvement de la souris ou des clics de souris, ou depuis la lecture ou l'acceptation d'information de différente façons telles que les flux de données ou même les fonctions système. En fait, tout ce qu'obtient votre programme de l'extérieur est une entrée.
  
The result could be an empty (NULL) “data�? that the user provide us, an out of range number or bigger amount of chars we expected, or even an attempt to change the address of the variable that accepts the input from the user. We just can not know what the user is going provide.
+
Peu importe le type d'entrée : par exemple, un utilisateur ou un autre système peut nous donner une mauvaise entrée, et les raisons peuvent être intentionnelles ou une erreur. Vous ne pouvez pas contrôler cette entrée, et la raison principale est que vous ne pouvez pas deviner ce que l'entrée sera.
  
Any “unsafe�? handle of the user input can cause for retrieving vital information that the user must not accept, and could not accept, or modification of data that the user could not do any other way, or even break the program itself.
+
Les résultats peuvent être des "données" vides (NULL) que l'utilisateur nous fournit, un nombre qui est hors de l'étendue attendue, ou une plus grande quantité de caractères que nous attendions, ou même une tentative de changer l'adresse de la variable qui accepte L'entrée de l'utilisateur. Nous ne pouvons tout simplement pas savoir ce que l'utilisateur va fournir.
  
=== What type of problems can we expect ? ===
+
Tout manipulation "non-sûre" de l'entrée peut aboutir à
On every type of bug you probably will find a type of attack, but I wish to give a small list of very common type of attacks, instead of writing a lot of the attack types.
+
*la récupération d'information critique que l'utilisateur n'est pas autorisé à voir,  
 +
* la modification non permise de donnée,
 +
* la corruption de donnée,
 +
* l'interruption (crash, plantage) du programme lui-même.
  
The most common attack types are:
+
== A quel type de problèmes pouvons-nous nous attendre ? ==
====Buffer Overflow====
+
Pour chaque type de bug, vous pouvez sans doute trouver un type d'attaque, mais je vais vous donner une petite liste de types d'attaque communs, au lieu d'écrire beaucoup de types d'attaque.
When a given data overflows the amount of memory that was allocated for it:
 
'''var'''<br>
 
  iNums : '''array''' [0..9]  of''' integer;<br>
 
  ....<br>
 
  FillChar (iNums[-1], 100, #0);
 
  ....<br>
 
  '''for''' i := -10 '''to''' 10 '''do'''<br>
 
      readln (iNums[i]);<br>
 
  ....<br>
 
  
In this example we can see that for the static array of iNums we gave the ability to accept only 10 numbers, while we entered to the variable a content of 21 numbers.
+
Le types d'attaque les plus communs sont :
  
Please note that while the compiler might warn in simple cases, it won't in more elaborate forms.
+
===Débordement de tampon===
 +
C'est quand une donnée dépasse la quantité de mémoire qui lui est allouée :
 +
<syntaxhighlight lang=pascal>var
 +
  iNums : array [0..9] of integer;
 +
  ...
 +
  FillChar (iNums[-1], 100, #0);
 +
  ...
 +
  for i := -10 to 10 do
 +
  readln (iNums[i]);
 +
  ...</syntaxhighlight>
 +
 
 +
Dans cet exemple, nous pouvons voir que le tableau statique iNums n'accepte que 10 nombres, alors que nous entrons dans cette variable 21 nombres.
 +
 
 +
Veuillez noter que le compilateur sait signaler de tels cas, qui ne sont que des formes peu élaborées.
 +
 
 +
Si l'utilisateur peut entrer de la donnée qui est envoyée dans un tampon, il peut entrer certaines valeurs pouvant être interprétées par les instructions en code machine, qui seront écrites en dehors de notre tampon. L'ordinateur pourra alors exécuter ce code au lieu du code qui aurait dû être là.
 +
 
 +
C'est le débordement de tampon.
 +
 
 +
===Attaque par déni de service (''DoS'')===
 +
 
 +
Le déni de service n'est pas seulement un problème de réseau, il peut exister dans d'autres formes :
 +
 
 +
<syntaxhighlight lang=pascal>procedure Recurse;
 +
begin
 +
  while (True) do
 +
    begin
 +
      Recurse;
 +
    end;
 +
end;</syntaxhighlight>
 +
 
 +
Cette procédure s'exécutera jusqu'à ce que le système soit à court de ressources car il alloue plus de mémoire de pile à chaque récursion, cela provoquera l'arrêt de réponse du système, ou même le crash.
 +
Bien que certains systèmes, comme GNU/Linux, tentera de vous donner la possibilité d'arrêter le programme, cela prendra un certain temps pour se faire.
  
If the user will try to execute an arbitrary code in one of our attempts he or she will succeed in doing so, because we went outside the buffer that was given to us. And that's a buffer overflow.
+
Veuillez remarquer que cela n'est qu'un exemple statique, mais nous pouvons faire une attaque DoS sur un système en exécutant ce code.
  
====DoS Attack====
+
Une autre attaque DoS connue est le manque de libération de ressources système telle que la mémoire, les sockets, les descripteurs de fichiers, ...
Denial of Service is not only a network problem, but can exists also in many other ways:
 
  
'''procedure''' Recurse;<br>
+
Par exemple :
'''begin'''<br>
+
<syntaxhighlight lang=pascal>...
  '''while''' (True) '''do'''<br>
+
  begin
    '''begin'''<br>
+
    while True do
      Recurse;<br>
+
      begin
    '''end''';<br>
+
        Getmem(OurPtr, 10);
'''end'''; <br>
+
        OurPtr := Something;
 +
      end;
 +
  end.</syntaxhighlight>
  
This procedure will run until the system will be out of resources to allocate more stack memory to run, and will cause the system to stop responding, or even crash.
+
Cet exemple montre une allocation de mémoire (Getmem est comme malloc en langage C ; il réserve de la mémoire pour utilsiation), mais nous sortons de l'exécution sans avoir libérer la mémoire après emploi.
Although some systems like Linux, will try to give you the ability to stop running the program, it will take a lot of time from you to do it.
 
  
Please note that this is only a static example, but we made a DoS attack on a system that will run the code.
+
=== Injections ===
 +
Quand l'utilisateur nous donne une entrée avec laquelle nous travaillons sans l'avoir assainie, l'utilisateur peut mettre dedans des marque SQL ou du code (comme du script, ou du code machine) par exemple, qui amènera notre programme à réaliser quelque action indésirable (p.ex. supprimer des enregistrements/tables, retourner des données protégées telles que des structures de bases de données/tables, utilisateur et mot de passe, contenu de dossier ou de fichier ou même exécuter un programme sur l'ordinateur).
  
 +
Un exemple d'injection SQL :
  
Another known DoS attack is the lack of freeing system resources such as memory, sockets, file descriptors etc...
+
Entrée d'utilisateur :
 +
  SVP, entrez votre nom : a' OR 1=1
  
For example:
+
Dans le code :
  ...<br>
+
<syntaxhighlight lang=pascal>...
  '''begin'''<br>
+
Write('SVP, entrez votre nom : ');
    '''while''' (True) '''do'''<br>
+
ReadLn(sName);
      '''begin'''<br>
+
Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);
        Getmem (OurPtr, 10);<br>
+
...</syntaxhighlight>
        OurPtr := Something;<br>
 
      '''end''';<br>
 
  '''end'''.<br>
 
  
This example displays a memory allocation (Getmem is like the C malloc), but we exit the execution without freeing the memory at the end of it's use.
+
En soumettant cette instruction SQL avec l'entrée de l'utilisateur, il résultera de l'ajout de OR 1=1 dans la requête passée à la base de donnée : dans ce cas, la condition sera toujours vraie et l'utilisateur aura obtenu un éccès au programme et pourra connaître la liste des mots de passe (NdT : on fait l'hypothèse que ces derniers sont enregistrés en clair, mauvaise idée).
  
====Injections====
+
= Accès à vos données et modifications =
When the user gives us an input, and we are working on the given input directly without sanitizing it, the user can place in some SQL tags or code (like script code, or machine code) for example, that will cause our program to delete some records/tables or send the user some restricted data such as database/table structure, database user and password, content of directory or file, or even execute a program at the computer.
+
Ce n'est pas uniquement votre programme qui aura accès aux données qu'il utilise. Si vous stockez de la donnée dans des fichiers ou des bases de données (distantes), un attaquant peut obtenir l'accès à travers le système d'exploitation (et/ou la base de données et/ou la couche réseau/protocole de base de données).
  
A SQL injection example:
+
== Chiffrement : est-ce suffisant ? ==
 +
Pour contrer la menace décrite ci-dessus, les programmeurs utilise souvent le chiffrement, il peut être utilisé pour fournir :
 +
* la protection de la confidentialité des données
 +
* la non-répudiation (est-ce que la donnée a été créée par la personne qui le prétend) et l'intégrité (la donnée est-elle inchangée), en utilisant des mécanismes supplémentaires de signature numérique/hachage).
 +
ensemble pour
 +
* la communication des données
 +
* stockage/récupération des données
  
User Input:
+
Si vous utilisez ces méthodes, vos données ne sont toutefois pas automatiquement sûres.
  Please enter your name: a' OR 1=1<br>
 
  
Inside the code:
+
Il y a de multiples attaques possibles sur les données chiffrées :  
  ...<br>
+
* attaque sur des algorithmes mal sécurisés ou leur implémentation (p.ex. en utilisant une attaque avec du texte brut connu)
  write ('Please enter your name: ');<br>
+
* attaque sur les clés de chiffrement (p.ex. en faisant une rétro-analyse de votre programme, si vous stocker des clés dans un fichier ou en dur dans le programme, ou en corrigeant le programme pour intercepter les clés/mots de passe entrés par l'utilisateur).
  readln (sName);<br>
 
  Query1.SQL.Add ('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);<br>
 
  ...<br>
 
  
This addition of SQL statement will cause our query to add new “WHERE�? rule that can cause for data traversal or other problems that we are not always able to detect.
+
Si vous ne savez pas exactement ce que vous allez faire (et vous ne saurez pas, à moins d'avoir une formation en cryptographie), svp utilisez (par ordre décroissant de préférence) :
 +
* des bibliothèques réputées qui sont maintenues/corrigées (p.ex. les bibliothèque internes de FPC, et des bibliothèques telles que DCPCrypt ou des bibliothèques externes comme OpenSSL ou cryptlib) qui utilise des protocoles réputés et largement utilisés qui utilisent des algorithmes réputés et largement utilisés.
 +
** Utilisez p.ex. Trusted Authentication/SSPI pour la connectivité SQL Server/Firebird pour éviter l'envoi de mots de passe a travers le câble (dans Firebird >= 2.0 : en texte clair !) et s'appuie sur la sécurité du système d'exploitation pour l'authentification.
 +
** Utilisez Cryptlib ou OpenSSL avedc Synapse pour implémenter SSL/TSL au lieu d'une solution personnelle.
 +
* des protocoles et APIs réputés et largement utilisés au lieu de solutions personnelles. Ces protocoles doivent utiliser des algorithmes de cryptographie/hachage bien compris et largement employés. Exemples :
 +
** GPG/PGP
 +
** TLS (SSL) avec PKI/CAs (de préférence ne faites pas confiance au CAs dont vous avez besoin, et réalisez des certificats d'authenthification client si votre analyse de la menace l'exige).
 +
** SSH (e.g. avec clé d'authentification publique/privée, si nécessaire avec renforcement par des périphrases pour les clés)
 +
** Dans Windows, utilisez l'API pour obtenir l'utilisateur actuellement authentifié. Si vous mettez en application la sécurité adaptée du système (longueur de mot de passe, modification, accès physiques etc), vous n'aurez pas besoin de gérer votre propore mécanisme de nom de connexion/mot de passe au niveau de l'application.
 +
* des algorithmes bien connus de crypto/hachage (tels que AES/Rijndael, 3DES et SHA512). Soyez conservateur dans les algorithmes que vous acceptez (p.ex. MD5 n'est pas sûr dans la signature de message).
  
====Myth and Assumptions====
+
Le chiffrement est une partie de l'ensemble possible des mesures de sécurité ; l'effort/l'argent dépensé doit être évalué comme faisant partie de l'analyse de la sécurité (voir plus bas).
Many of the security issues exists because of ignoring important warnings and information that was given by the compiler, and by thinking that their program does not contain any problem that some one can take advantage.
 
  
Here are some examples for this type of problem:
+
=Mythes et suppositions=
 +
De nombreux problèmes de sécurité existent à cause de l'ignorance d'importants avertissements et d'information donnés par le compilateur et par la conviction que votre programme ne contient pas de problème exploitable.
  
'''Myth'''s:
+
Voici quelques exemples pour ce type de problème :
* Security by Obscurity - When no one knows about a problem no one can take advantage of it.
 
* Secure programming language - There are languages such as Perl that many people think that they are secure from Buffer overflows and other vulnerabilities while that does not make it so.
 
* Hash password is secure - A file that have an hashed password is not secure. Hash can only passed one and you can not retrieve  the original data.
 
* Nothing can break my program.
 
  
'''Assumption'''s:
+
'''Mythes''':
* The QA team will find and fix my bugs.
+
* La sécurité par l'obscurité - Quand personne ne connaît un problème, personne ne peut en profiter ; p.ex. utiliser un nom de colonne obscur pour stocker des mots de passe dans votre base de données.
* The user will not harm my program and its data.
+
* Les langages de programmation sûrs : il y a des langages tels que Perl dont des gens pensent à tort qu'ils sont sûrs face aux débordements de tampon et autres vulnérabilité.
* My program will be used only for its original use.
+
* Un mot de passe hachés est sûr - Un fichier qui a un mot de passe haché n'est pas sûr. (non traduit car incompris : ) Hash can only passed one and you can not retrieve the original data. ''I don't get this. Does the author mean that a hashed password can be retrieved by a brute force or rainbow table attack and that it therefore needs a salt, or multiple hash rounds? --[[User:BigChimp|BigChimp]] 16:19, 24 July 2011 (CEST)''.
* All exceptions can  remain unhandled.
+
* Personne ne peut casser mon programme - Penser que vous êtes le seul programmeur dans le monde qui écrive du code non fautif est sans doute un peu optimiste. Peut-être êtes-vous chanceux et vous avez juste écrit du code qui ne marche bien sans vulnérabilité de sécurité exploitable ...
  
==Explanation==
+
'''Suppositions''':
Now after we know some problems we can encounter when developing programs, we should learn how to fix this problems. All of the problems we saw above manifest into two types of problems, assumptions and the lack of care programming. And for learning how to fix them, we first need to learn to think in different approach, that we have.
+
* L'équipe qualité va trouver et corriger mes bugs de sécurité.
 +
* L'utilisateur (ou quelqu'un d'autre) n'attaquera pas mon programme ni ses données.
 +
* Mon programme ne sera utilisé que pour son utilisation prévue.
 +
* Toutes les exceptions peuvent rester en l'état (''unhandled'')
  
=== Overflow ===
+
= Analyse pour comprendre les menaces et la sécurité =
For fixing overflow of data, like buffers and other type of input, we first of all need to identify the type of data we need to work with.
+
En général, un programmeur (ou son employeur) devrait réaliser une analyse de toutes les menaces (depuis les accès physiques/attaques jusqu'aux attaques logiques et d'ingénierie sociales) qui devrait être faite sur le système entier (y compris l'infrastructure - système d'exploitation, base de données aussi bien que sur les machines physiques/câblage/batiments/connexions externes) pour analyser si vous n'avez pas laisser ouvert une brêche de sécurité qui est inacceptable (dans une perspective risque/bénéfice).
  
==== Buffer overflow ====
+
L'extension de cette analyse devrait dépendre de la valeur des données/processus que le système protège. Il est évident que cela est superflu d'analyser à fond la sécurité de votre maison quand vous développez un programme de loisir pout garder la trace de vos scores de bridge.
If we return to our example of:
+
 
'''var'''<br>
+
Bénéfices de cette sorte d'analyses :
  iNums : '''array''' [0..9]  '''of''' integer;<br>
+
* les risques restants après ces mesures de sécurité deviennent connus et explicites. Souvent, il y a des discussion sur les chances de survenue du risque, ou sur l'impact qui lui associé, mais la fait qu'il subsiste un vecteur d'attaque est au moins clair et les décisions peuvent être rendues basées sur cette information.
  ....<br>
+
* vous pouvez loyalement facilement voir que les mesures sont sur-conçues (''overengineered'') ("too secure", gaspillage d'argent) ou sous-conçues ("pas assez sûres"). Si vous ne pouvez pas utiliser cette information maintenant, vous pouvez au moins apprendre d'elle pour d'autres projets, y compris de future maintenance/modification de votre programme.
  FillChar (iNums[-1], 100, #0);
+
 
  ....<br>
+
= Solutions spécifiques =
  '''for''' i := -10 '''to''' 10 '''do'''<br>
 
      readln (iNums[i]);<br>
 
  ....<br>
 
 
  
We see here a range that was override by our values, without even checking if the index number is correct.
+
Maintenant nous connaissons certains problèmes que nous allons rencontrés en développant des programmes, nous devrions apprendre à corriger ces problèmes. Tous les problèmes que nous avons vu au-dessus se manifestent en deux types : suppositions et manque de programmation sûre. Et pour apprendre à les corriger, nous devons d'abord apprendre à penser d'une manière différente que nous avions jusqu'à maintenant.
  
In dynamic/open arrays in Pascal we can know the limits of the allocated memory. So all we need to do is check if the size is too small or too big for our buffer, and limit the accepting for the size we wish it to be.
+
== Débordement ==
 +
Pour corriger le débordement de donnée, comme les tampons ou autre type d'entrée, nous avons besoin avant tout d'identifier le type de donnée dont nous avons besoin :
  
So the example should be changed into:
+
=== Débordement de tampon ===
'''var'''
+
Si nous revenons à notre exemple de :
 +
<syntaxhighlight lang=pascal>var
 +
  iNums: array [0..9] of Integer;
 +
  ...
 +
  FillChar(iNums[-1], 100, #0);
 +
  ...
 +
  for i := -10 to 10 do
 +
    ReadLn(iNums[i]);
 +
  ...</syntaxhighlight>
 
   
 
   
  iNums : '''array''' [0..9] '''of''' integer;
+
Nous voyons là une étendue qui a été dépassée par nos valeurs, sans même vérifier si l'index est correct.
 +
 
 +
Dans les tableaux dynamiques/ouverts de Pascal, nous pouvons connaître les limites de la mémoire allouée. Ainsi, il suffit juste de contrôler si la taille est trop petite ou trop grande pour notre tampon, et de limiter ce qu'elle doit accepter.
 +
 
 +
Ainsi l'exemple devrait être changé en :
 +
<syntaxhighlight lang=pascal>var
 +
  iNums: array [0..9] of Integer;
 
   
 
   
  ....
+
  ...
 +
  FillChar (iNums[Low(iNums)], High(iNums), #0);
 +
  ...
 
   
 
   
  FillChar (iNums[Low(iNum)], High(iNum), #0);
+
  for i := Low(iNums) to High(iNums) do
  ....
+
    ReadLn(iNums[i]);
+
  ...</syntaxhighlight>
  '''for''' i := Low (iNum) '''to''' High (iNum) '''do'''
 
 
      readln (iNums[i]);
 
 
  ....
 
  
But wait Something is not right yet !
+
Mais attendez, quelque n'est pas encore juste !
  
The readln will accept an unlimited amount of chars, and no one is promise us that it will be an Integer or even in the range we can handle.
+
La routine ReadLn accepte une quantité illimitée de caractères et rien ne nous promet que cela sera un entier ou de l'étendue que nous pouvons gérer.
  
==== Number Overflow ====
+
===Débordement numérique ===
While string in Pascal is pure array (hrmm hrmm.. not really, at least not in FPC, but  lets pretend it is for a second OK ?) so ''readln'' will try to find and see what are it's limits and will not try to overflow the range we gave that type, but Numbers are not the same.
+
Alors qu'une chaîne en Pascal est un pur tableau (humm humm..pas vraiment, du moins pas en FreePascal, mais nous l'admettrons quelques temps, Ok ?) ainsi ''readln'' tentera de trouver et voir ce que sont ces limites et ne tentera pas de dépasser l'étendue que nous avons donné à ce type, mais les nombres ne sont pas pareils.
  
Numbers have limits, a computer have limits of many kinds regarding memory and numbers. It can give only “small�? amount of memory for numbers (floating point and integer numbers). And many times we do not need a large range of numbers to use (like boolean variable that needs only two numbers usually).
+
Les nombres ont des limites, un ordinateur/compilateur a des limites de toutes sortes concernant la mémoire et les nombres. Il peut attribuer une "petite" quantité de mémoire pour les nombres (virgule flottante et entiers). Et souvent, nous n'avons pas besoin d'une grande étendue de nombre à utiliser (tels que les booléens qui ne demandent que deux valeurs).
  
In the above example we may have a buffer overflow that will cause a range check error that will give us the wrong number (Carry Flag reminder issues... I'm not going to explain them in here), and we also have a DoS effect, because our program will halt from that point.
+
Dans l'exemples du dessus, nous pouvons avoir un dépassement de tampon qui provoquera une erreur de contrôle d'étendue qui nous donnera un mauvais nombre (problèmes de rappel(''reminder'') de drapeau de retenue... que je n'expliquerai pas ici), et nous avons un effet DoS, car notre programme s'arrêtera à ce point.
  
So what can we do from that point ?
+
Donc que pouvons-nous faire pour régler cela ?
  
First of all we may wish to work in that point with a string variable that will be in the length of the largest number +1 (for minus sign), or we can create our own readln procedure/function that will specialize with the Integer type.
+
En premier lieu, nous pouvons désirer travailler avec une variable chaîne qui sera de la taille du nombre le plus grand +1 (pour le signe moins), ou nous pouvons créer notre propre procédure/fonction ReadLn qui se spécialisera avec le type entier.
  
For the first offer we can do the following (Copied from the FPC documentation):
+
Pour la première option, nous pouvons faire ce qui suit (copié depuis la documentation FPC) :
'''Program''' Example74;
+
<syntaxhighlight lang=pascal>Program Example74;
 
   
 
   
''{ Program to demonstrate the Val function. }''
+
{ Program to demonstrate the Val function. }
'''Var''' I, Code : Integer;
+
Var  
+
  I, Code: Integer;  
'''begin'''
+
begin
  Val (ParamStr (1),I,Code);
+
  Val(ParamStr(1), I, Code);
  '''If''' Code<>0 '''then'''
+
  If Code <> 0 then
    Writeln ('Error at position ',code,' : ',Paramstr(1)[Code])
+
    Writeln('Error at position ', code, ' : ', Paramstr(1)[Code])
  '''else'''
+
  else
    Writeln ('Value : ',I);   
+
    Writeln('Value : ', I);   
'''end'''.
+
end.</syntaxhighlight>
  
Here we see how to convert a string into a string with a very easy error handeling. The function ''StrToInt'' may also do the trick but it then we need to capture an exception in any error dealing.
+
Nous voyons là comment convertir une chaîne en un entier avec une gestion très simple de l'erreur. La fonction ''StrToInt'' peut aussi faire l'affaire mais il faudra traiter l'exception résultante d'une erreur.
  
 
+
Voici un petit exemple pour un petit ''readln'' comme procédure pour des nombres entiers.
Here is a small example for a small ''readln'' like procedure for integer numbers.
+
<syntaxhighlight lang=pascal>program MyReadln;
 +
uses
 +
  CRT;
 
   
 
   
'''program''' MyReadln;
+
procedure MyIntReadLn(var Param: Integer; ParamLength: Integer);
  '''uses''' CRT;
+
var
 +
  Line: string;
 +
  ch: char;
 +
  Error: Integer;
 +
 
 +
begin
 +
  Line := '';
 
   
 
   
'''procedure''' MyIntReadLn ('''var''' Param : Integer; ParamLength : Integer);
+
  repeat
'''var'''
+
    ch := ReadKey;
  Line  : '''string''';
+
    if (Length (Line) <> ParamLength) then
  ch    : char;
+
       begin
  Error : Integer;
+
       if (ch in ['0'..'9']) then
 
+
         begin
'''begin'''
 
  Line  := '';
 
 
 
  '''repeat'''
 
    ch := readkey;
 
    '''if''' (Length (Line) <> ParamLength) '''then'''
 
       '''begin'''
 
       '''if''' (ch '''in''' ['0'..'9']) '''then'''
 
         '''begin'''
 
 
           Line := Line + ch;
 
           Line := Line + ch;
 
           write (ch);
 
           write (ch);
         '''end'''
+
         end
       '''else'''
+
       else
       '''if''' (ch = '-') '''and''' (Length (Line) = 0) '''then'''
+
       if (ch = '-') and (Length(Line) = 0) then
         '''begin'''
+
         begin
 
           Line := '-';
 
           Line := '-';
 
           write (ch);
 
           write (ch);
         '''end''';
+
         end;
       '''end''';
+
       end;
 
        
 
        
     '''if''' (ch = #8) '''and''' (Length(Line) <> 0) '''then''' // backspace
+
     if (ch = #8) and (Length(Line) <> 0) then // backspace
       '''begin'''
+
       begin
       Line := copy (Line, 1, Length (Line) -1);
+
       Line := Copy(Line, 1, Length(Line) - 1);
       gotoxy (WhereX -1, WhereY);
+
       gotoxy(WhereX - 1, WhereY);
       write (' ');
+
       write(' ');
       gotoxy (WhereX -1, WhereY);
+
       gotoxy(WhereX - 1, WhereY);
       '''end''';
+
       end;
   '''until''' (ch = #13);
+
   until (ch = #13);
 
   
 
   
   val (Line, Param, Error);
+
   val(Line, Param, Error);
 
   
 
   
   '''if''' (Error <> 0) '''then'''
+
   if (Error <> 0) then
 
     Param := 0;
 
     Param := 0;
 
   
 
   
 
   writeln;
 
   writeln;
'''end''';
+
end;
 
   
 
   
'''var'''
+
var
 
   Num : Integer;
 
   Num : Integer;
 
   
 
   
'''begin'''
+
begin
  write ('Number: ');
+
  Write('Number: ');
  MyIntReadLn (Num, 2);
+
  MyIntReadLn(Num, 2);
  writeln ('The number is: ', Num);
+
  WriteLn('The number is: ', Num);
'''end'''.
+
end.</syntaxhighlight>
 
    
 
    
Please note that you can make it even better, and more efficient if you wish. This is only a very small example for how to do it.
+
Veuillez noter que vous pouvez la faire mieux, et plus efficace si vous le voulez. C'est seulement un petit exemple pour montrer comment le faire.
  
==== What is the security risks in Overflows ? ====
+
=== Quels sont les risques de sécurité dans les débordement  ? ===
Overflow of memory can allow arbitrary CPU code to be executed and users may run whatever type of code they wish, and nothing can stop them.
+
Un dépassement de mémoire peut permettre l'exécution d'un code CPU arbitraire et que les utilisateurs peuvent exécuter quelque soit le type de code qu'il désire et rien ne peut les arrêter.
  
===Denial of Service===
+
== Dénis de service ==
Denial of Service is one of the hardest types of attacks to prevent. The reasons are:
+
Le deni de service (DoS) est l'un des types d'attaque les plus difficile à empêcher, les raisons sont :  
* The denial of service can be executed even without any bug that is under exploitation like the “ping�? program.
+
* Le déni de service peut même être exécuté sans bug exploitable, comme en utilisant le programme "ping" sur un paquets de machines pour bloquer une machine connectée à Internet.
* Every system resource can be a possible denial of service, like opening sockets, reading files or just not freeing allocated memory when you "do not need�? the memory anymore.
+
* Toute ressource système peut être la cible d'un possible DoS, comme l'ouverture d'une socket, la lecture de fichiers ou l'allocation de mémoire.
* Removal of files like a kernel module can cause a big problem.
+
* La suppression de fichiers comme un module du noyau peut causer un gros problème. (''I don't get this line --[[User:BigChimp|BigChimp]] 19:32, 24 July 2011 (CEST)'')
* Lack of configuration or wrong configuration can cause a denial of service as well.
+
* Un manque de configuration ou une mauvaise configuration peut provoquer un déni de service s'il permet de mal employer des resources vulnérables.
* Too much permissions or lack of them.
+
* Trop de permissions ou un manque d'elles (''I don't get that not enough permissions can be a problem. Neither too many, either? Is a security risk, obviously but not a DoS problem. --[[User:BigChimp|BigChimp]] 19:32, 24 July 2011 (CEST)''
* Almost any type of exploit can result into a denial of service.
+
* Presque tout type d'exploit peut aboutir à un déni de service.
  
So as you can see, a denial of service can be almost everything that can stop us from making our work as we wish to, because of exploitation or buggy code or just a program that captures the system resources..
+
Donc comme vous pouvez le voir, un déni de service peut presque être n'importe quoi qui arrête le système dans ce qu'il devrait faire à cause de l'exploitation ou d'un code bugué ou juste un programme qui capture des ressources système.
  
In the above example (of the denial of service):
+
Dans l'exemple de déni de service du dessous :
'''procedure''' Recurse;
+
<syntaxhighlight lang=pascal>procedure Recurse;
'''begin'''
+
begin
  '''while''' (True) '''do'''
+
  while (True) do
    '''begin'''
+
    begin
      Recurse;
+
      Recurse;
    '''end''';
+
    end;
'''end''';  
+
end;</syntaxhighlight>
  
I created also a stack overflow (another type of buffer overflow), that caused the computer to arrive to a need for more memory resources to continue executing the code.
+
J'ai créé un débordement de pile (un autre type de débordement de tampon), qui amène l'ordinateur à avoir besoin de plus de mémoire pour continuer à exécuter le code.
  
Any system resource that is available to the program can be abused by not returning it back to the system when the program “does not need it anymore�?. The keeping of system resources like memory, or sockets remove from other programs the ability to perform some of their actions. That way most programs will stop their execution and report an error, and some will hang and keep on looking for the system resources.  
+
Toute ressource système disponible pour le programme peut être détournée en ne la rendant pas au système lorsque le programme «n'en a plus besoin». La conservation de ressources système comme la mémoire, ou des sockets enlève à d'autres programmes la possibilité d'effectuer certaines de leurs actions. De cette façon, la plupart des programmes arrêteront leur exécution et signaleront une erreur, et certains se bloqueront et continueront à chercher les ressources système.
  
Please note that some of the abuse of system resources exist because of a bug in the programming, like waiting for a 150k buffer, while the actual buffer is only 2 bytes, and when the program is still looking for the 150k buffer a new request for a 150k buffer is made etc.. until the system is not able to answer any of the requests anymore (this is a known type of attack).
+
Notez que l'utilisation abusive des ressources système existe à cause d'un bogue dans la programmation, comme l'attente d'un tampon de 150 Ko, alors que le tampon réel n'est que de 2 octets et que le programme cherche toujours le tampon de 150 Ko, une nouvelle demande d'un tampon de 150k est fait, etc. jusqu'à ce que le système ne soit plus en mesure de répondre à aucune des requêtes (c'est un type connu d'attaque).
  
A good workaround for this bug is to limit how many non full buffers can be allocated at one time and if after a “timeout�? the buffer is not full, to free it completely. But also doing that, will cause a Denial of Service, because the communication will stop anyway at some point, or a slow connection can cause a lost of data.
+
Une bonne solution de contournement pour ce bogue est de limiter le nombre de buffers non complets peuvent être alloués en même temps. Si le tampon n'est pas plein après un délai, il devrait être libre.
 +
Toutefois, cette solution entraînera également un déni de service, car la communication s'arrêtera de toute façon à un certain moment, ou une connexion lente peut entraîner la perte de données.
  
===Injection===
+
==Injection==
There are many ways to inject type of code into our programs. As we saw at the above example:
+
Il y a de nombreuses façons d'injecter certain type de code dans vos programmes. Comme nous pouvons le voir dans l'exemple du dessus.
  
User Input:
+
Entrée de l'utilisateur :
  Please enter your name: a' OR 1=1
+
  SVP, veuillez entrer votre nom : a' OR 1=1
  
Inside the code:
+
Inside the code:
  ...  
+
<syntaxhighlight lang=pascal>...  
  write ('Please enter your name: ');  
+
write(' SVP, veuillez entrer votre nom : ');  
  readln (sName);  
+
readln(sName);  
  Query1.SQL.Add ('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);  
+
Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);  
  ...
+
...</syntaxhighlight>
The injection occurred when we do not filter our code (sanitize is the more professional word :)), and we do not check that we received the exact type of input that we are looking for.
 
  
For example, we could check if ''sName'' have spaces, and if so, do not continue checking for the rest of the variable.
+
L'injection se rencontre quand nous ne filtrons pas les données entrées dans notre code (assainir est un mot plus professionnel:)) : ceci signifie que nous recevons seulement ce qui a été exactement frappé de l'entrée que nous cherchons et rien d'autre.
The reason is very simple. The name should be only one word, and for us a word defines by letters, maybe even the tick sign (') and maybe even underscore (_) and then it's over. If we place a number, our word is over (unless we wish to use “hacker�? like language, or allow the use of numbers).
 
  
The best way to check this type of structure can done in many ways. The less effective one, but highly in use is the following:
+
Par exemple, nous pourrions contrôler si ''sName'' contient des espaces. Si c'est ainsi, ne pas contrôler le reste de la variable.
  
'''function''' ValidVar ('''const''' S : AnsiString; AllowChars : TCharset) : Boolean;
+
Ceci aide si le nom d'utilisateur peut être une seul mot contenant des lettres, peut-être une apostrophe et peut-être même un souligné et c'est tout. Si nous entrons un nombre, cela serait illégal.
'''var'''
+
 
   i : Word;
+
Il ya plusieurs manière d'assainir votre donnée. La moins efficace (mais souvent utilisée) est la suivante :
'''begin'''
+
 
   i     := 0;
+
=== Désinfection inefficace ===
 +
<syntaxhighlight lang=pascal>function ValidVar (const S: AnsiString; AllowChars: TCharset): Boolean;
 +
var
 +
   i: Word;
 +
begin
 +
   i := 0;
 
   Result := True;
 
   Result := True;
 
    
 
    
   '''While''' (Result) '''and''' (i <= Length (S)) '''do'''
+
   while (Result) and (i <= Length(S)) do
  '''begin'''
+
  begin
      inc (i);
+
    Inc(i);
      Result := S [i] '''in''' AllowChars;
+
    Result := S[i] in AllowChars;
  '''end''';
+
  end;
'''end''';
+
end;</syntaxhighlight>
  
The function return true if we have a valid structure of content given by the ''AllowChars'' in the ''S'' variable.
+
La fonction retourne true si nous avons un contenu correspondant à celui spécifié par ''AllowChars'' dans la variable ''S''.
Please note that this function is only a proof of concept and may need more work in order to be fully used.
+
Veuillez noter que cette fonction est seulement une preuve du concept et demande plus de travail pour être pleinement utilisable.
  
Another good way to do the same is to use regular expression as the following (This is a Proof of concept only in the Perl language. FPC does not have a fully supported regular engine that allow to modify strings):
+
Une autre solution pour faire la même chose consiste à utiliser une expression régulière comme suit (c'est une preuve de concept seulement pour Pearl. FPC n'a pas un support complet des expressions régulières qui permet de modifier les chaînes) (NdT : alors pourquoi en parler ?) :
 
 
  $sName =~ s/[^a-z0-9\_\']//gi;
 
  $sName =~ s/[^a-z0-9\_\']//gi;
  
The regular expression remove any non valid chars from the string and return to us only purged string.
+
L'expression régulière retire tous les caractères invalides de la chaîne et retourne la chaîne purgée.
Please note that as far as I know, this regular expression will work also in ereg engines, but with minimal adjustments (''g'' flag instruct Perl to replace all the matching patterns found. ''i'' is for ''insensitive case'').
+
Veuillez noter qu'autant que je le sache, cette expression régulière tournera aussi dans le moteur ereg, mais avec des ajustements minimaux (le drapeau ''g'' demande à Pearl de remplacer tous les modèles correspondants trouvés. ''i'' est pour l'insensibilité à la casse).
 +
 
 +
Maintenant nous connaissons que notre entrée est valide, nous devons voir quel est l'utilisation du contenu de la variable. Si la variable va aller dans une base de données, ou un script CGI ou autre chose qui a sa propre syntaxe, nous devons éliminer du contenu les caractères de contrôle ou non permis du langage (p.ex. SQL pour les bases de données).
 +
 
 +
Il y a plusieurs façons d'échapper ce type de contenu. Supposons pour maintenant que ce contenu va aller dans une requête de base de données. Maintenant en premier lieu, nous devons nous assurer que notre échappement n'augmentera pas la taille au dessus de la longueur limite de notre champ de base de données. Parce s'il le font, alors nous passerons d'une injection à une perte de donnée/un déni de service/un débordement de tampon (une base de données respectée généralement tronque la donnée et parfois pas au bon endroit.
 +
 
 +
Généralement, ce que nous faisons pour utiliser une chaîne dans une base de données est seulement d'échapper l'apostrophe (certaines bases de données peuvent avoir des problèmes avec plus de caractères). Ainsi nous devons représenter l'apostrophe d'une façon qu'elle n'ait pas d'effet dans le moteur de la base de données, en le précédant d'une barre de fraction inverse (\') ou en doublant l'apostrophe (<nowiki>''</nowiki>), ou peut-être même en employant un autre caractère en remplacement dans la requête puis en le faisant le remplacement inverse lors de l'affichage à l'utilisateur.
 +
 
 +
=== Restriction des entrées avec les paramètres de requêtes SQL ===
  
Now when we know that our input is valid, we need to see what is the use of the variable content. If the variable content is going into a database, or a cgi script, or anything else that have it's own syntax, we must escape the content.
+
Après nous être assuré du respect des limites, nous pouvons continuer dans nos tentatives. Pour échapper le code, nous pouvons utiliser plusieurs approches. Une approche sympa de moindre débogage, mais une façon sûre d'avoir un échappement correct est d'utiliser la technique des paramètres (NdT : ouf on y est !)
  
There are many ways to escape this type of content. Lets assume for now that this content is going into a query of a database. Now first of all we must make sure that our escaping will not raise above the length limits of our database fields. Because if they will, then we can cause from a data lost to a denial of server/buffer overflow problems (a respected database usually will trunk the data and sometimes not in a good location).  
+
<syntaxhighlight lang=pascal>Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name=?');
 +
Query1.Parameters.Add(sName);
 +
if (Query1.Execute) then
 +
...</syntaxhighlight>
  
After we made sure that we stand in our limits, we can continue in our attempts. To escape the code we can use several approaches. A less debugging friendly way, but a sure way of correct escaping is to use the parameters technique:
+
Cette technique permet au moteur de base de données pour échapper le paramètre d'une manière que nous puissions utiliser le contenu sans problème de caractères illégaux. En outre, certaines bases de données ont augmenté les performances pour les appels répétés à ce code, car il peut préparer une déclaration interne avec des paramètres pour cela.
 +
Le mauvais côté est que nous ne pourrons jamais déboguer le resultat de la requête. Autrement dit, nous ne pouvons pas voir comment le contenu de ''sName'' intégré dans l'instruction SQL, et nous ne pouvons jamais voir si notre requête était correcte à cause de cela.
  
Query1.SQL.Add ('SELECT Password FROM tblUsers WHERE Name=?');
+
De toutes façons, une fois que vous avez testé la requête sans paramètre, l'ajout des paramètres est assez facile, donc en pratique, le problème n'est pas aussi grand qu'il n'y paraît.
Query1.Parameters.Add (sName);
 
'''if''' (Query1.Execute) '''then'''
 
  ...
 
  
This technique allow the database engine to escape the parameter in a way that we could use the content without any problems of illegal characters. The down side is that we can never debug the outcome of the query. That is, we can not see how the content of ''sName'' embedded in the SQL statement, and we can never see if our query was correct because of that.
+
== Code efficace ==
 +
Les mesures de sécurité mentionnées au dessus compliquent le code si vous avez utilisé le code le plus efficace pour obtenir les fonctionnalités voulues. Heureusement, vous pouvez parfois contourner les complications du code avec un framework/une bibliothèque qui traite cela. Par exemple : dans l'exemple DoS SQL, vous pouvez laisser le moteur de base de données traiter les données d'échappement en utilisant des requêtes paramétrées avec un petit coût pour avoir utiliser des paramètres dans votre code.
  
Usually the only escaping we need to do for using a string in a database is to escape only the ticks (') char (although some databases may have problems with more chars then ticks). So all we should do is to represent ticks in a way that will not effect the database engine, like backslash tick (\') or double every single tick to two ticks (<nowiki>''</nowiki>), or maybe even use another char that will be replace the ticks in the query and replace again when we will show it to the user.
+
Toutefois, écrire des programmes efficaces en performance mais vulnérables en sécurité n'aidera personne mis à part des juristes en fiabilité et des experts en forensique.
  
===Myth and Assumption===
+
= Au-delà du document =
One of the biggest problem with myth and assumptions is that we are starting to loose the ability to write efficient code. We all need to remember that there isn't even one program that does not have bugs. But that is also an assumption :) although this assumption was never broken.
 
  
== Beyond The Document ==
+
Alors que j'ai tenté dans ce document quelques exemples courts et l'information sur la façon de créer un meilleur code, il y ad'autres problèmes qui ne sont pas évoqués dans ce document. Une partie d'entre eux sont les privilèges utilisateur pour l'exécution des programmes, les root kits système et autres problèmes que notre code doit prendre en considération (les variables d'environnement en sont un unique exemple).
While in this document I gave a short (yea I know it's an understatement ;)) example and information on how to create better code, there are many issues that I did not touch in this document. Part of them are user privileges for execution of the programs, system root kits and other problems that our code needs to take in consideration (environment variable is only one example).
 
  
Please read more resources out there for security issues like
+
Veuillez lire plus de ressources techniques telles que :
  
Buffer Overflows:  
+
Débordement de tampon :  
 
* http://www.securiteam.com/securityreviews/5QP0L0AFFS.html
 
* http://www.securiteam.com/securityreviews/5QP0L0AFFS.html
 
* http://www.securiteam.com/securityreviews/5OP0B006UQ.html
 
* http://www.securiteam.com/securityreviews/5OP0B006UQ.html
Line 347: Line 400:
 
* http://www.securiteam.com/securityreviews/5UP0B0A60Y.html  
 
* http://www.securiteam.com/securityreviews/5UP0B0A60Y.html  
  
Denial Of Service:
+
Déni de service:
 
* http://www.securiteam.com/securityreviews/5AP0V0AA1W.html
 
* http://www.securiteam.com/securityreviews/5AP0V0AA1W.html
 
* http://www.securiteam.com/securityreviews/6Z00T0K95O.html
 
* http://www.securiteam.com/securityreviews/6Z00T0K95O.html
  
SQL Injection:
+
Injection SQL :
 
* http://www.securiteam.com/securityreviews/5DP0N1P76E.html
 
* http://www.securiteam.com/securityreviews/5DP0N1P76E.html
 
* http://www.securiteam.com/securityreviews/5UP010A6AA.html  
 
* http://www.securiteam.com/securityreviews/5UP010A6AA.html  
 
* http://www.securiteam.com/securityreviews/5GP0E2K7FO.html
 
* http://www.securiteam.com/securityreviews/5GP0E2K7FO.html

Latest revision as of 13:38, 26 February 2020

English (en) français (fr) polski (pl)

Avant-propos

Cette page de wiki tente d'enseigner une approche différente dans la façon créer un logiciel. La page utilise des exemples très simples pour montrer que beaucoup de problèmes peuvent être profitables pour prendre l'avantage dans le but de créer une attaque de sécurité sur un ordinateur, un programme ou un système en entier.

Veuillez noter que le document est seulement un début sur la technologie sur comment écrire un meilleur et un code un peu plus sécurisé code, mais il n'essaye pas d'être un guide complet sur la façon de le faire. En fait c'est seulement un résumé de la façon dont nous devons voir notre code et programme , et comment éviter beaucoup de problèmes communs.

Veuillez vous rappeler que ce document vise un meilleur codage et n'est pas là pour montrer comment hacker(bidouiller) ou craquer (casser la protection) de programmes.

Ne faites pas confiance aux entrées

En développant un programme, il est probable qu'il interagira avec un utilisateur de différentes façons, même s'il se limite à lire des fichiers dans le système et présenter les données.

Généralement à l'école et à l'université, quand quelqu'un commence à écrire des programmes, cette personne apprend comment recevoir des données, les professeurs disent généralement à cette personne "supposez que les données que vous recevez sont valide".

Mais Nous ne pouvons pas faire confiance à toute entrée que nous ne pouvons pas contrôler car son contenu est inconnu et pourrait exploiter une vulnérabilité dans notre logiciel.

Lire depuis un fichier est lire depuis une source non sûre, de même que les entrées d'un utilisateur ou les données provenant d'un réseau par exemple.

Pourquoi ne dois-je pas croire aux entrées ?

Pour comprendre pourquoi une entrée est dangereuse, nous devons d'abord comprendre ce qu'est une entrée.

Une entrée peut être lue depuis un clavier, le mouvement de la souris ou des clics de souris, ou depuis la lecture ou l'acceptation d'information de différente façons telles que les flux de données ou même les fonctions système. En fait, tout ce qu'obtient votre programme de l'extérieur est une entrée.

Peu importe le type d'entrée : par exemple, un utilisateur ou un autre système peut nous donner une mauvaise entrée, et les raisons peuvent être intentionnelles ou une erreur. Vous ne pouvez pas contrôler cette entrée, et la raison principale est que vous ne pouvez pas deviner ce que l'entrée sera.

Les résultats peuvent être des "données" vides (NULL) que l'utilisateur nous fournit, un nombre qui est hors de l'étendue attendue, ou une plus grande quantité de caractères que nous attendions, ou même une tentative de changer l'adresse de la variable qui accepte L'entrée de l'utilisateur. Nous ne pouvons tout simplement pas savoir ce que l'utilisateur va fournir.

Tout manipulation "non-sûre" de l'entrée peut aboutir à

  • la récupération d'information critique que l'utilisateur n'est pas autorisé à voir,
  • la modification non permise de donnée,
  • la corruption de donnée,
  • l'interruption (crash, plantage) du programme lui-même.

A quel type de problèmes pouvons-nous nous attendre ?

Pour chaque type de bug, vous pouvez sans doute trouver un type d'attaque, mais je vais vous donner une petite liste de types d'attaque communs, au lieu d'écrire beaucoup de types d'attaque.

Le types d'attaque les plus communs sont :

Débordement de tampon

C'est quand une donnée dépasse la quantité de mémoire qui lui est allouée :

var
  iNums : array [0..9] of integer;
  ...
  FillChar (iNums[-1], 100, #0);
  ...
  for i := -10 to 10 do
  readln (iNums[i]);
  ...

Dans cet exemple, nous pouvons voir que le tableau statique iNums n'accepte que 10 nombres, alors que nous entrons dans cette variable 21 nombres.

Veuillez noter que le compilateur sait signaler de tels cas, qui ne sont que des formes peu élaborées.

Si l'utilisateur peut entrer de la donnée qui est envoyée dans un tampon, il peut entrer certaines valeurs pouvant être interprétées par les instructions en code machine, qui seront écrites en dehors de notre tampon. L'ordinateur pourra alors exécuter ce code au lieu du code qui aurait dû être là.

C'est le débordement de tampon.

Attaque par déni de service (DoS)

Le déni de service n'est pas seulement un problème de réseau, il peut exister dans d'autres formes :

procedure Recurse;
begin
  while (True) do
    begin
      Recurse;
    end;
end;

Cette procédure s'exécutera jusqu'à ce que le système soit à court de ressources car il alloue plus de mémoire de pile à chaque récursion, cela provoquera l'arrêt de réponse du système, ou même le crash. Bien que certains systèmes, comme GNU/Linux, tentera de vous donner la possibilité d'arrêter le programme, cela prendra un certain temps pour se faire.

Veuillez remarquer que cela n'est qu'un exemple statique, mais nous pouvons faire une attaque DoS sur un système en exécutant ce code.

Une autre attaque DoS connue est le manque de libération de ressources système telle que la mémoire, les sockets, les descripteurs de fichiers, ...

Par exemple :

...
  begin
    while True do
      begin
        Getmem(OurPtr, 10);
        OurPtr := Something;
      end;
  end.

Cet exemple montre une allocation de mémoire (Getmem est comme malloc en langage C ; il réserve de la mémoire pour utilsiation), mais nous sortons de l'exécution sans avoir libérer la mémoire après emploi.

Injections

Quand l'utilisateur nous donne une entrée avec laquelle nous travaillons sans l'avoir assainie, l'utilisateur peut mettre dedans des marque SQL ou du code (comme du script, ou du code machine) par exemple, qui amènera notre programme à réaliser quelque action indésirable (p.ex. supprimer des enregistrements/tables, retourner des données protégées telles que des structures de bases de données/tables, utilisateur et mot de passe, contenu de dossier ou de fichier ou même exécuter un programme sur l'ordinateur).

Un exemple d'injection SQL :

Entrée d'utilisateur :

 SVP, entrez votre nom : a' OR 1=1

Dans le code :

...
Write('SVP, entrez votre nom : ');
ReadLn(sName);
Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);
...

En soumettant cette instruction SQL avec l'entrée de l'utilisateur, il résultera de l'ajout de OR 1=1 dans la requête passée à la base de donnée : dans ce cas, la condition sera toujours vraie et l'utilisateur aura obtenu un éccès au programme et pourra connaître la liste des mots de passe (NdT : on fait l'hypothèse que ces derniers sont enregistrés en clair, mauvaise idée).

Accès à vos données et modifications

Ce n'est pas uniquement votre programme qui aura accès aux données qu'il utilise. Si vous stockez de la donnée dans des fichiers ou des bases de données (distantes), un attaquant peut obtenir l'accès à travers le système d'exploitation (et/ou la base de données et/ou la couche réseau/protocole de base de données).

Chiffrement : est-ce suffisant ?

Pour contrer la menace décrite ci-dessus, les programmeurs utilise souvent le chiffrement, il peut être utilisé pour fournir :

  • la protection de la confidentialité des données
  • la non-répudiation (est-ce que la donnée a été créée par la personne qui le prétend) et l'intégrité (la donnée est-elle inchangée), en utilisant des mécanismes supplémentaires de signature numérique/hachage).

ensemble pour

  • la communication des données
  • stockage/récupération des données

Si vous utilisez ces méthodes, vos données ne sont toutefois pas automatiquement sûres.

Il y a de multiples attaques possibles sur les données chiffrées :

  • attaque sur des algorithmes mal sécurisés ou leur implémentation (p.ex. en utilisant une attaque avec du texte brut connu)
  • attaque sur les clés de chiffrement (p.ex. en faisant une rétro-analyse de votre programme, si vous stocker des clés dans un fichier ou en dur dans le programme, ou en corrigeant le programme pour intercepter les clés/mots de passe entrés par l'utilisateur).

Si vous ne savez pas exactement ce que vous allez faire (et vous ne saurez pas, à moins d'avoir une formation en cryptographie), svp utilisez (par ordre décroissant de préférence) :

  • des bibliothèques réputées qui sont maintenues/corrigées (p.ex. les bibliothèque internes de FPC, et des bibliothèques telles que DCPCrypt ou des bibliothèques externes comme OpenSSL ou cryptlib) qui utilise des protocoles réputés et largement utilisés qui utilisent des algorithmes réputés et largement utilisés.
    • Utilisez p.ex. Trusted Authentication/SSPI pour la connectivité SQL Server/Firebird pour éviter l'envoi de mots de passe a travers le câble (dans Firebird >= 2.0 : en texte clair !) et s'appuie sur la sécurité du système d'exploitation pour l'authentification.
    • Utilisez Cryptlib ou OpenSSL avedc Synapse pour implémenter SSL/TSL au lieu d'une solution personnelle.
  • des protocoles et APIs réputés et largement utilisés au lieu de solutions personnelles. Ces protocoles doivent utiliser des algorithmes de cryptographie/hachage bien compris et largement employés. Exemples :
    • GPG/PGP
    • TLS (SSL) avec PKI/CAs (de préférence ne faites pas confiance au CAs dont vous avez besoin, et réalisez des certificats d'authenthification client si votre analyse de la menace l'exige).
    • SSH (e.g. avec clé d'authentification publique/privée, si nécessaire avec renforcement par des périphrases pour les clés)
    • Dans Windows, utilisez l'API pour obtenir l'utilisateur actuellement authentifié. Si vous mettez en application la sécurité adaptée du système (longueur de mot de passe, modification, accès physiques etc), vous n'aurez pas besoin de gérer votre propore mécanisme de nom de connexion/mot de passe au niveau de l'application.
  • des algorithmes bien connus de crypto/hachage (tels que AES/Rijndael, 3DES et SHA512). Soyez conservateur dans les algorithmes que vous acceptez (p.ex. MD5 n'est pas sûr dans la signature de message).

Le chiffrement est une partie de l'ensemble possible des mesures de sécurité ; l'effort/l'argent dépensé doit être évalué comme faisant partie de l'analyse de la sécurité (voir plus bas).

Mythes et suppositions

De nombreux problèmes de sécurité existent à cause de l'ignorance d'importants avertissements et d'information donnés par le compilateur et par la conviction que votre programme ne contient pas de problème exploitable.

Voici quelques exemples pour ce type de problème :

Mythes:

  • La sécurité par l'obscurité - Quand personne ne connaît un problème, personne ne peut en profiter ; p.ex. utiliser un nom de colonne obscur pour stocker des mots de passe dans votre base de données.
  • Les langages de programmation sûrs : il y a des langages tels que Perl dont des gens pensent à tort qu'ils sont sûrs face aux débordements de tampon et autres vulnérabilité.
  • Un mot de passe hachés est sûr - Un fichier qui a un mot de passe haché n'est pas sûr. (non traduit car incompris : ) Hash can only passed one and you can not retrieve the original data. I don't get this. Does the author mean that a hashed password can be retrieved by a brute force or rainbow table attack and that it therefore needs a salt, or multiple hash rounds? --BigChimp 16:19, 24 July 2011 (CEST).
  • Personne ne peut casser mon programme - Penser que vous êtes le seul programmeur dans le monde qui écrive du code non fautif est sans doute un peu optimiste. Peut-être êtes-vous chanceux et vous avez juste écrit du code qui ne marche bien sans vulnérabilité de sécurité exploitable ...

Suppositions:

  • L'équipe qualité va trouver et corriger mes bugs de sécurité.
  • L'utilisateur (ou quelqu'un d'autre) n'attaquera pas mon programme ni ses données.
  • Mon programme ne sera utilisé que pour son utilisation prévue.
  • Toutes les exceptions peuvent rester en l'état (unhandled)

Analyse pour comprendre les menaces et la sécurité

En général, un programmeur (ou son employeur) devrait réaliser une analyse de toutes les menaces (depuis les accès physiques/attaques jusqu'aux attaques logiques et d'ingénierie sociales) qui devrait être faite sur le système entier (y compris l'infrastructure - système d'exploitation, base de données aussi bien que sur les machines physiques/câblage/batiments/connexions externes) pour analyser si vous n'avez pas laisser ouvert une brêche de sécurité qui est inacceptable (dans une perspective risque/bénéfice).

L'extension de cette analyse devrait dépendre de la valeur des données/processus que le système protège. Il est évident que cela est superflu d'analyser à fond la sécurité de votre maison quand vous développez un programme de loisir pout garder la trace de vos scores de bridge.

Bénéfices de cette sorte d'analyses :

  • les risques restants après ces mesures de sécurité deviennent connus et explicites. Souvent, il y a des discussion sur les chances de survenue du risque, ou sur l'impact qui lui associé, mais la fait qu'il subsiste un vecteur d'attaque est au moins clair et les décisions peuvent être rendues basées sur cette information.
  • vous pouvez loyalement facilement voir que les mesures sont sur-conçues (overengineered) ("too secure", gaspillage d'argent) ou sous-conçues ("pas assez sûres"). Si vous ne pouvez pas utiliser cette information maintenant, vous pouvez au moins apprendre d'elle pour d'autres projets, y compris de future maintenance/modification de votre programme.

Solutions spécifiques

Maintenant nous connaissons certains problèmes que nous allons rencontrés en développant des programmes, nous devrions apprendre à corriger ces problèmes. Tous les problèmes que nous avons vu au-dessus se manifestent en deux types : suppositions et manque de programmation sûre. Et pour apprendre à les corriger, nous devons d'abord apprendre à penser d'une manière différente que nous avions jusqu'à maintenant.

Débordement

Pour corriger le débordement de donnée, comme les tampons ou autre type d'entrée, nous avons besoin avant tout d'identifier le type de donnée dont nous avons besoin :

Débordement de tampon

Si nous revenons à notre exemple de :

var
  iNums: array [0..9] of Integer;
  ...
  FillChar(iNums[-1], 100, #0);
  ...
  for i := -10 to 10 do
    ReadLn(iNums[i]);
  ...

Nous voyons là une étendue qui a été dépassée par nos valeurs, sans même vérifier si l'index est correct.

Dans les tableaux dynamiques/ouverts de Pascal, nous pouvons connaître les limites de la mémoire allouée. Ainsi, il suffit juste de contrôler si la taille est trop petite ou trop grande pour notre tampon, et de limiter ce qu'elle doit accepter.

Ainsi l'exemple devrait être changé en :

var
  iNums: array [0..9] of Integer;
 
  ...
  FillChar (iNums[Low(iNums)], High(iNums), #0);
  ...
 
  for i := Low(iNums) to High(iNums) do
    ReadLn(iNums[i]);
  ...

Mais attendez, quelque n'est pas encore juste !

La routine ReadLn accepte une quantité illimitée de caractères et rien ne nous promet que cela sera un entier ou de l'étendue que nous pouvons gérer.

Débordement numérique

Alors qu'une chaîne en Pascal est un pur tableau (humm humm..pas vraiment, du moins pas en FreePascal, mais nous l'admettrons quelques temps, Ok ?) ainsi readln tentera de trouver et voir ce que sont ces limites et ne tentera pas de dépasser l'étendue que nous avons donné à ce type, mais les nombres ne sont pas pareils.

Les nombres ont des limites, un ordinateur/compilateur a des limites de toutes sortes concernant la mémoire et les nombres. Il peut attribuer une "petite" quantité de mémoire pour les nombres (virgule flottante et entiers). Et souvent, nous n'avons pas besoin d'une grande étendue de nombre à utiliser (tels que les booléens qui ne demandent que deux valeurs).

Dans l'exemples du dessus, nous pouvons avoir un dépassement de tampon qui provoquera une erreur de contrôle d'étendue qui nous donnera un mauvais nombre (problèmes de rappel(reminder) de drapeau de retenue... que je n'expliquerai pas ici), et nous avons un effet DoS, car notre programme s'arrêtera à ce point.

Donc que pouvons-nous faire pour régler cela ?

En premier lieu, nous pouvons désirer travailler avec une variable chaîne qui sera de la taille du nombre le plus grand +1 (pour le signe moins), ou nous pouvons créer notre propre procédure/fonction ReadLn qui se spécialisera avec le type entier.

Pour la première option, nous pouvons faire ce qui suit (copié depuis la documentation FPC) :

Program Example74;
 
{ Program to demonstrate the Val function. }
Var 
  I, Code: Integer; 
begin
  Val(ParamStr(1), I, Code);
  If Code <> 0 then
    Writeln('Error at position ', code, ' : ', Paramstr(1)[Code])
  else
    Writeln('Value : ', I);  
end.

Nous voyons là comment convertir une chaîne en un entier avec une gestion très simple de l'erreur. La fonction StrToInt peut aussi faire l'affaire mais il faudra traiter l'exception résultante d'une erreur.

Voici un petit exemple pour un petit readln comme procédure pour des nombres entiers.

program MyReadln;
uses 
  CRT;
 
procedure MyIntReadLn(var Param: Integer; ParamLength: Integer);
var
  Line: string; 
  ch: char;
  Error: Integer;
   
begin
  Line  := '';
 
  repeat
    ch := ReadKey;
    if (Length (Line) <> ParamLength) then
      begin
       if (ch in ['0'..'9']) then
        begin
          Line := Line + ch;
          write (ch);
        end
       else
       if (ch = '-') and (Length(Line) = 0) then
        begin
          Line := '-';
          write (ch);
        end;
       end;
       
     if (ch = #8) and (Length(Line) <> 0) then // backspace
      begin
       Line := Copy(Line, 1, Length(Line) - 1);
       gotoxy(WhereX - 1, WhereY);
       write(' ');
       gotoxy(WhereX - 1, WhereY);
      end;
   until (ch = #13);
 
   val(Line, Param, Error);
 
   if (Error <> 0) then
     Param := 0;
 
  writeln;
end;
 
var
  Num : Integer;
 
begin
  Write('Number: ');
  MyIntReadLn(Num, 2);
  WriteLn('The number is: ', Num);
end.

Veuillez noter que vous pouvez la faire mieux, et plus efficace si vous le voulez. C'est seulement un petit exemple pour montrer comment le faire.

Quels sont les risques de sécurité dans les débordement ?

Un dépassement de mémoire peut permettre l'exécution d'un code CPU arbitraire et que les utilisateurs peuvent exécuter quelque soit le type de code qu'il désire et rien ne peut les arrêter.

Dénis de service

Le deni de service (DoS) est l'un des types d'attaque les plus difficile à empêcher, les raisons sont :

  • Le déni de service peut même être exécuté sans bug exploitable, comme en utilisant le programme "ping" sur un paquets de machines pour bloquer une machine connectée à Internet.
  • Toute ressource système peut être la cible d'un possible DoS, comme l'ouverture d'une socket, la lecture de fichiers ou l'allocation de mémoire.
  • La suppression de fichiers comme un module du noyau peut causer un gros problème. (I don't get this line --BigChimp 19:32, 24 July 2011 (CEST))
  • Un manque de configuration ou une mauvaise configuration peut provoquer un déni de service s'il permet de mal employer des resources vulnérables.
  • Trop de permissions ou un manque d'elles (I don't get that not enough permissions can be a problem. Neither too many, either? Is a security risk, obviously but not a DoS problem. --BigChimp 19:32, 24 July 2011 (CEST)
  • Presque tout type d'exploit peut aboutir à un déni de service.

Donc comme vous pouvez le voir, un déni de service peut presque être n'importe quoi qui arrête le système dans ce qu'il devrait faire à cause de l'exploitation ou d'un code bugué ou juste un programme qui capture des ressources système.

Dans l'exemple de déni de service du dessous :

procedure Recurse;
begin
  while (True) do
    begin
      Recurse;
    end;
end;

J'ai créé un débordement de pile (un autre type de débordement de tampon), qui amène l'ordinateur à avoir besoin de plus de mémoire pour continuer à exécuter le code.

Toute ressource système disponible pour le programme peut être détournée en ne la rendant pas au système lorsque le programme «n'en a plus besoin». La conservation de ressources système comme la mémoire, ou des sockets enlève à d'autres programmes la possibilité d'effectuer certaines de leurs actions. De cette façon, la plupart des programmes arrêteront leur exécution et signaleront une erreur, et certains se bloqueront et continueront à chercher les ressources système.

Notez que l'utilisation abusive des ressources système existe à cause d'un bogue dans la programmation, comme l'attente d'un tampon de 150 Ko, alors que le tampon réel n'est que de 2 octets et que le programme cherche toujours le tampon de 150 Ko, une nouvelle demande d'un tampon de 150k est fait, etc. jusqu'à ce que le système ne soit plus en mesure de répondre à aucune des requêtes (c'est un type connu d'attaque).

Une bonne solution de contournement pour ce bogue est de limiter le nombre de buffers non complets peuvent être alloués en même temps. Si le tampon n'est pas plein après un délai, il devrait être libre. Toutefois, cette solution entraînera également un déni de service, car la communication s'arrêtera de toute façon à un certain moment, ou une connexion lente peut entraîner la perte de données.

Injection

Il y a de nombreuses façons d'injecter certain type de code dans vos programmes. Comme nous pouvons le voir dans l'exemple du dessus.

Entrée de l'utilisateur :

 SVP, veuillez entrer votre nom : a' OR 1=1

Inside the code:

... 
write('  SVP, veuillez entrer votre nom : '); 
readln(sName); 
Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32); 
...

L'injection se rencontre quand nous ne filtrons pas les données entrées dans notre code (assainir est un mot plus professionnel:)) : ceci signifie que nous recevons seulement ce qui a été exactement frappé de l'entrée que nous cherchons et rien d'autre.

Par exemple, nous pourrions contrôler si sName contient des espaces. Si c'est ainsi, ne pas contrôler le reste de la variable.

Ceci aide si le nom d'utilisateur peut être une seul mot contenant des lettres, peut-être une apostrophe et peut-être même un souligné et c'est tout. Si nous entrons un nombre, cela serait illégal.

Il ya plusieurs manière d'assainir votre donnée. La moins efficace (mais souvent utilisée) est la suivante :

Désinfection inefficace

function ValidVar (const S: AnsiString; AllowChars: TCharset): Boolean;
var
  i: Word;
begin
  i := 0;
  Result := True;
  
  while (Result) and (i <= Length(S)) do
  begin
    Inc(i);
    Result := S[i] in AllowChars;
  end;
end;

La fonction retourne true si nous avons un contenu correspondant à celui spécifié par AllowChars dans la variable S. Veuillez noter que cette fonction est seulement une preuve du concept et demande plus de travail pour être pleinement utilisable.

Une autre solution pour faire la même chose consiste à utiliser une expression régulière comme suit (c'est une preuve de concept seulement pour Pearl. FPC n'a pas un support complet des expressions régulières qui permet de modifier les chaînes) (NdT : alors pourquoi en parler ?) :

$sName =~ s/[^a-z0-9\_\']//gi;

L'expression régulière retire tous les caractères invalides de la chaîne et retourne la chaîne purgée. Veuillez noter qu'autant que je le sache, cette expression régulière tournera aussi dans le moteur ereg, mais avec des ajustements minimaux (le drapeau g demande à Pearl de remplacer tous les modèles correspondants trouvés. i est pour l'insensibilité à la casse).

Maintenant nous connaissons que notre entrée est valide, nous devons voir quel est l'utilisation du contenu de la variable. Si la variable va aller dans une base de données, ou un script CGI ou autre chose qui a sa propre syntaxe, nous devons éliminer du contenu les caractères de contrôle ou non permis du langage (p.ex. SQL pour les bases de données).

Il y a plusieurs façons d'échapper ce type de contenu. Supposons pour maintenant que ce contenu va aller dans une requête de base de données. Maintenant en premier lieu, nous devons nous assurer que notre échappement n'augmentera pas la taille au dessus de la longueur limite de notre champ de base de données. Parce s'il le font, alors nous passerons d'une injection à une perte de donnée/un déni de service/un débordement de tampon (une base de données respectée généralement tronque la donnée et parfois pas au bon endroit.

Généralement, ce que nous faisons pour utiliser une chaîne dans une base de données est seulement d'échapper l'apostrophe (certaines bases de données peuvent avoir des problèmes avec plus de caractères). Ainsi nous devons représenter l'apostrophe d'une façon qu'elle n'ait pas d'effet dans le moteur de la base de données, en le précédant d'une barre de fraction inverse (\') ou en doublant l'apostrophe (''), ou peut-être même en employant un autre caractère en remplacement dans la requête puis en le faisant le remplacement inverse lors de l'affichage à l'utilisateur.

Restriction des entrées avec les paramètres de requêtes SQL

Après nous être assuré du respect des limites, nous pouvons continuer dans nos tentatives. Pour échapper le code, nous pouvons utiliser plusieurs approches. Une approche sympa de moindre débogage, mais une façon sûre d'avoir un échappement correct est d'utiliser la technique des paramètres (NdT : ouf on y est !)

Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name=?');
Query1.Parameters.Add(sName);
if (Query1.Execute) then
...

Cette technique permet au moteur de base de données pour échapper le paramètre d'une manière que nous puissions utiliser le contenu sans problème de caractères illégaux. En outre, certaines bases de données ont augmenté les performances pour les appels répétés à ce code, car il peut préparer une déclaration interne avec des paramètres pour cela. Le mauvais côté est que nous ne pourrons jamais déboguer le resultat de la requête. Autrement dit, nous ne pouvons pas voir comment le contenu de sName intégré dans l'instruction SQL, et nous ne pouvons jamais voir si notre requête était correcte à cause de cela.

De toutes façons, une fois que vous avez testé la requête sans paramètre, l'ajout des paramètres est assez facile, donc en pratique, le problème n'est pas aussi grand qu'il n'y paraît.

Code efficace

Les mesures de sécurité mentionnées au dessus compliquent le code si vous avez utilisé le code le plus efficace pour obtenir les fonctionnalités voulues. Heureusement, vous pouvez parfois contourner les complications du code avec un framework/une bibliothèque qui traite cela. Par exemple : dans l'exemple DoS SQL, vous pouvez laisser le moteur de base de données traiter les données d'échappement en utilisant des requêtes paramétrées avec un petit coût pour avoir utiliser des paramètres dans votre code.

Toutefois, écrire des programmes efficaces en performance mais vulnérables en sécurité n'aidera personne mis à part des juristes en fiabilité et des experts en forensique.

Au-delà du document

Alors que j'ai tenté dans ce document quelques exemples courts et l'information sur la façon de créer un meilleur code, il y ad'autres problèmes qui ne sont pas évoqués dans ce document. Une partie d'entre eux sont les privilèges utilisateur pour l'exécution des programmes, les root kits système et autres problèmes que notre code doit prendre en considération (les variables d'environnement en sont un unique exemple).

Veuillez lire plus de ressources techniques telles que :

Débordement de tampon :

Déni de service:

Injection SQL :