Trouvez la faille!
                            Antonio Fontes / Confoo 2012 - Montréal

Notice 1: cette présentation contient des références à Common Weakness Enumeration:
http://cwe.mitre.org/data/index.html
Notice 2: aucun chat n'a été maltraité durant la préparation de cette séance.
Notice 3: cette présentation contient des références au
document "Test your Security IQ", par M. Howard et B. Sullivan
Notice 4: Un grand merci à Sébastien pour ses idées & propositions!
• Règles de jeu:
   – Lire l’exemple de code affiché à l'écran
   – Trouver la ou les éléments pouvant constituer un risque pour la sécurité du S.I.
   – Tenir une comptabilité analytique des points obtenus!
   02.03.2012                Confoo Conference 2012 - Antonio Fontes            2
Antonio Fontes
      Genève (Suisse)
      Consultant indépendant Infosécurité logicielle:
            Sécurité des applications web
            Visibilité et gestion du risque sur Internet
            Formation / accompagnement durant les projets de développement
      Bulletin d'information"cybermenaces et sécurité Internet":
            http://cddb.ch
      OWASP:
            Membre du Comité - OWASP Suisse
            Leader - OWASP Genève



A propos du conférencier…



   02.03.2012                 Confoo Conference 2012 - Antonio Fontes        3
• Le site d’actualité permet la création de comptes personnels, la
  publication de réactions à l’actualité, l’échange de messages entre
  membres.
• 1 point
   02.03.2012            Confoo Conference 2012 - Antonio Fontes    4
Envoi des éléments
                                                 d'authentification en
                                                 clair.




• Le site d’actualité permet la création de comptes personnels, la
  publication de réactions à l’actualité, l’échange de messages entre
  membres.
• 1 point
   02.03.2012            Confoo Conference 2012 - Antonio Fontes         5
• Idem.



   02.03.2012   Confoo Conference 2012 - Antonio Fontes   6
function printFile($username,$filename){
          //read file into string
          $file = file_get_contents($filename);

             if ($file && isOwnerOf($username,$filename)){
               echo $file;
               return true;
             } else {
               echo 'You are not authorized to view this file';
             }
             return false;
         }

• 1 point



   02.03.2012                  Confoo Conference 2012 - Antonio Fontes   7
function printFile($username,$filename){
          //read file into string
          $file = file_get_contents($filename);
                                         Le chargement du
             if ($file && isOwnerOf($username,$filename)){
                                         fichier a lieu avant
               echo $file;
                                         le contrôle d'accès.
               return true;
             } else {
               echo 'You are not authorized to view this file';
             }
             return false;
         }


• 1 point
   – Identification du risque pour la disponibilité du S.I.


   02.03.2012                 Confoo Conference 2012 - Antonio Fontes   8
protected void Page_Load(object sender, EventArgs e)
         {
           string lastLogin = Request["LastLogin"];
           if (String.IsNullOrEmpty(lastLogin)) {
             HttpCookie lastLoginCookie = new HttpCookie("LastLogin",
              DateTime.Now.ToShortDateString());
             lastLoginCookie.Expires = DateTime.Now.AddYears(1);
             Response.Cookies.Add(lastLoginCookie);
           }
           else {
             Response.Write("Welcome back! You last logged in on " +
         lastLogin);
             Response.Cookies["LastLogin"].Value =
              DateTime.Now.ToShortDateString();
           }
         }

• 1 point



   02.03.2012                 Confoo Conference 2012 - Antonio Fontes   9
protected void Page_Load(object sender, EventArgs e)
         {
           string lastLogin = Request["LastLogin"];
           if (String.IsNullOrEmpty(lastLogin)) { Appel vers la
             HttpCookie lastLoginCookie = new HttpCookie("LastLogin",
                                                    collection parente
              DateTime.Now.ToShortDateString());
                                                    "Request"
             lastLoginCookie.Expires = DateTime.Now.AddYears(1);
             Response.Cookies.Add(lastLoginCookie);
           }
           else {
             Response.Write("Welcome back! You last logged in on " +
         lastLogin);
             Response.Cookies["LastLogin"].Value Transfert du contenu
                                                  =
              DateTime.Now.ToShortDateString(); vers le client, sans
           }                                      encodage approprié.
         }

• 1 point
   – Cas de type "XSS" (Cross-site scripting)


   02.03.2012                Confoo Conference 2012 - Antonio Fontes     10
$role = $_COOKIES['role'];
  if (!$role) {
    $role = getRole('user');
    if ($role) {
      // save the cookie to send out in future responses
      setcookie("role", $role, time()+60*60*2);
    } else {
      ShowLoginScreen();
      die("n");
    }
  }
  if ($role == 'Reader') {
    DisplayMedicalHistory($_POST['patient_ID']);
  } else {
    die("You are not Authorized to view this recordn");
  }

• 1 point



   02.03.2012                 Confoo Conference 2012 - Antonio Fontes   11
$role = $_COOKIES['role'];
  if (!$role) {                       Absence de contrôle
    $role = getRole('user');          d'intégrité du
    if ($role) {
                                      cookie.
      // save the cookie to send out in future responses
      setcookie("role", $role, time()+60*60*2);
    } else {
                                                      Contournement du
      ShowLoginScreen();                              mécanisme
      die("n");                                      d'authentification
    }
  }
  if ($role == 'Reader') {                             Contournement du
    DisplayMedicalHistory($_POST['patient_ID']);       contrôle d'accès
  } else {
    die("You are not Authorized to view this recordn");
  }

• 1 point



   02.03.2012             Confoo Conference 2012 - Antonio Fontes     12
• 1 + 1 point



   02.03.2012   Confoo Conference 2012 - Antonio Fontes   13
Transfert de
                                                                 confiance à un
                                                                 tiers




• 1 + 1 + 1 points
   – 1 point: identification du risque d'injection de contenu par un tiers
   – 1 point: identification du risque sur la confidentialité (fuite des referrers)
   – 1 point: identification du risque de déni de service sur le tiers
   02.03.2012                  Confoo Conference 2012 - Antonio Fontes                14
• 1 + 1 + 1 points
   – Faire attention aux recommandations sur le web: elles vont souvent à
     l'encontre de la sécurité et visent à faciliter la collecte de données par des tiers.
   – Vérifier qui est l'auteur d'une recommandation de codage.
   02.03.2012                  Confoo Conference 2012 - Antonio Fontes             15
// API flag, output JSON if set
  $json = $_GET['json'];
  $username = $_GET['user'];
  if($json)
  {
    $record = getUserRecord($username);
    echo(json_encode($record));
  } else {

   $record = getUserRecord($username);
   foreach($record as $fieldName => $fieldValue) {
     // never disclose user email addresses to the public (privacy req.)
     if(!($fieldName == "email_address"))
       renderToHtmlTable ($fieldName,$fieldValue);
     }}}


• 1 point



   02.03.2012                 Confoo Conference 2012 - Antonio Fontes      16
// API flag, output JSON if set
  $json = $_GET['json'];                          Dans le cas json,
  $username = $_GET['user'];
                                                  l'adresse email n'est
  if($json)
  {                                               plus protégée contre les
    $record = getUserRecord($username);           fuites.
    echo(json_encode($record));
  } else {

   $record = getUserRecord($username);
   foreach($record as $fieldName => $fieldValue) {
     // never disclose user email addresses to the public (privacy req.)
     if(!($fieldName == "email_address"))
       renderToHtmlTable ($fieldName,$fieldValue);
     }}}


• 1 point
   – Identification de la fuite d'adresses email


   02.03.2012                 Confoo Conference 2012 - Antonio Fontes      17
byte[] GetKey(UInt32 keySize)
         {
           byte[] key = null;
           try
           {
             key = new byte[keySize];
             RNGCryptoServiceProvider.Create().GetBytes(key);
           }
           catch (Exception e)
           {
             Math.Random r = new Math.Random();
             r.NextBytes(key);
           }
           return key;
         }

1 + 1 + 1 point



   02.03.2012                 Confoo Conference 2012 - Antonio Fontes   18
byte[] GetKey(UInt32 keySize)
         {
           byte[] key = null;
           try
           {
             key = new byte[keySize];
             RNGCryptoServiceProvider.Create().GetBytes(key);
           }
           catch (Exception e)
           {                      Exception générique?
             Math.Random r = new Math.Random();
             r.NextBytes(key);
           }                                  Math.Random?
                              Fail-safe?
           return key;
         }

1 + 1 + 1 point
   – Exception générique: privilégier l'exception typée
   – Principe de conception "Fail-safe": le code n'échoue pas en haute sécurité
   – La classe Math.Random ne fournit pas d'entropie de niveau cryptographique
   02.03.2012               Confoo Conference 2012 - Antonio Fontes         19
private decimal? lookupPrice(XmlDocument doc)
 {
     string query = @"//products/product[id/text()='" + Request["itemId"] +
 "']/price"
      XmlNode node = doc.SelectSingleNode(query);
      if (node == null)
         return null;
      else
         return(Convert.ToDecimal(node.InnerText));
 }



• 1 point



   02.03.2012                Confoo Conference 2012 - Antonio Fontes          20
private decimal? lookupPrice(XmlDocument doc)
 {
     string query = @"//products/product[id/text()='" + Request["itemId"] +
 "']/price"
      XmlNode node = doc.SelectSingleNode(query); Validation?
      if (node == null)
         return null;
      else
         return(Convert.ToDecimal(node.InnerText));
 }



• 1 point
   – Injection de type Xpath (il n'y a pas que des injections SQL!!)
   – Marche aussi sur: commandes système, LDAP, APIs ORM, etc.

   02.03.2012                Confoo Conference 2012 - Antonio Fontes          21
public class MySessionIDManager : System.Web.Session State.SessionIDManager
 {
   private static object lockObject = new object();

   public override string CreateSessionID(HttpContext context)
   {
     lock (lockObject)
     {
       Int32? lastSessionId = (int?)context.Application ["LastSessionId"];
       if (lastSessionId == null)
          lastSessionId = 1;
       else
          lastSessionId++;
       context.Application["LastSessionId"] = lastSessionId;
       return lastSessionId.ToString();
     }}}
• 1 + 1 point



   02.03.2012                 Confoo Conference 2012 - Antonio Fontes        22
public class MySessionIDManager : System.Web.Session State.SessionIDManager
 {
   private static object lockObject = new object();

   public override string CreateSessionID(HttpContext context)
   {
     lock (lockObject)       Multi-serveur?
     {
       Int32? lastSessionId = (int?)context.Application ["LastSessionId"];
       if (lastSessionId == null)
          lastSessionId = 1;          ID de session prédictibles
       else
          lastSessionId++;
       context.Application["LastSessionId"] = lastSessionId;
       return lastSessionId.ToString();
     }}}
• 1 + 1 point
   – Identification de l'identifiant de session prédictible
   – Collision des identifiants de session si le serveur est répliqué!

   02.03.2012                 Confoo Conference 2012 - Antonio Fontes        23
bool login(SqlConnection connection, out string errorMessage)
 {
   string uname = Request.Form["username"];
   string pword = Request.Form["password"];
   SqlCommand selectUserAndPassword = new SqlCommand( "SELECT pwd FROM Users WHERE uname =
 @username", connection);
   selectUserAndPassword.Parameters.Add( new SqlParameter("@username", uname));

      string validPassword = (string)selectUserAndPassword.ExecuteScalar();
      if (validPassword == null)
      {
          // the user doesn't exist in the database
         errorMessage = "The username is invalid.";
         return false;
      }
      else if (validPassword != pword)
      {
          // the given password doesn't match
         errorMessage = "The password is incorrect.";
         return false;
     }
     else
     {
          // success
Bug #9   errorMessage = String.Empty;
         return true;
      }
 }                                                                            1 + 1 + 1 points
       – 1 point + bonus point
       02.03.2012                           Confoo Conference 2012 - Antonio Fontes              24
bool login(SqlConnection connection, out string errorMessage)
 {                                  Requête paramétrée, ça, c'est juste!
   string uname = Request.Form["username"];
   string pword = Request.Form["password"];
   SqlCommand selectUserAndPassword = new SqlCommand( "SELECT pwd FROM Users WHERE uname =
 @username", connection);
   selectUserAndPassword.Parameters.Add( new SqlParameter("@username", uname));

      string validPassword = (string)selectUserAndPassword.ExecuteScalar();
      if (validPassword == null)
      {
          // the user doesn't exist in the database    Rapatriement inutile du
         errorMessage = "The username is invalid.";
         return false;
                                                       mot de passe!
      }
      else if (validPassword != pword)
      {                                        Stockage du mot de passe en
          // the given password doesn't match
                                               clair.
         errorMessage = "The password is incorrect.";
         return false;
     }
     else                         Message d'erreur variable lorsque le
     {
          // success
                                  login ou le mdp est faux (fuite)
Bug #9   errorMessage = String.Empty;
         return true;
      }
 }                                                                            1 + 1 + 1 points
       – 1 point + bonus point
       02.03.2012                           Confoo Conference 2012 - Antonio Fontes              25
// SilverLight code module review
bool verifyCode(string discountCode)
{
   // We store the hash of the secret code instead of the plaintext of the secret code for security.
   // We hash the incoming value and compare it against the stored hash.
   byte[] codeHash = SecurityUtils.ComputeHash(discountCode, "MD5");
   byte[] secretCode = new byte[] { 116, 46, 130, 122, 36, 234, 158, 125, 163, 122, 157, 186, 64, 142, 51,
                        153, 113, 79, 1, 42 };

    // This should never happen, but we check it anyway
    if (codeHash.Length != secretCode.Length)
        return false;

    // perform an element-by-element comparison of the arrays
    for (int i = 0; i < codeHash.Length; i++)
    {
       if (codeHash[i] != secretCode[i])
          return false; // the hashes don't match
    } // all the elements match, so the strings match

    // the discount code is valid, inform the server
    WebServiceSoapClient client = new WebServiceSoapClient();
    client.ApplyDiscountCode();
    return true;                                                               1 + 1 + 2 points
}

     02.03.2012                      Confoo Conference 2012 - Antonio Fontes                         26
// SilverLight code module review
bool verifyCode(string discountCode)      Algorithme déconseillé (+1)
{
   // We store the hash of the secret code instead of the plaintext of the secret code for security.
   // We hash the incoming value and compare it against the stored hash.
   byte[] codeHash = SecurityUtils.ComputeHash(discountCode, "MD5");
   byte[] secretCode = new byte[] { 116, 46, 130, 122, 36, 234, 158, 125, 163, 122, 157, 186, 64, 142, 51,
                        153, 113, 79, 1, 42 };

    // This should never happen, but we check it anyway
                                                               A-t-on besoin d'un
    if (codeHash.Length != secretCode.Length)                  sel? (seed) +1
        return false;

    // perform an element-by-element comparison of the arrays
    for (int i = 0; i < codeHash.Length; i++)
    {
       if (codeHash[i] != secretCode[i])              Défense côté client                          
          return false; // the hashes don't match
    } // all the elements match, so the strings match totalement inutile!                          (+2
                                                             points)
    // the discount code is accepted, inform the server
    WebServiceSoapClient client = new WebServiceSoapClient();
    client.ApplyDiscountCode();
    return true;                                                               1 + 1 + 2 points
}

     02.03.2012                      Confoo Conference 2012 - Antonio Fontes                         27
• 1 point
   02.03.2012   Confoo Conference 2012 - Antonio Fontes   28
Injection SQL
                                 (absence de
                                 validation)




• 1 point
   02.03.2012   Confoo Conference 2012 - Antonio Fontes   29
$MessageFile = "messages/messages.out";

 if ($_GET["action"] == "NewMessage") {
    $name = $_GET["name"];
    $message = $_GET["message"];
    $handle = fopen($MessageFile, "a+");
    fwrite($handle, "<b>$name</b> says '$message'<hr>n");
    fclose($handle);
    echo "Message Saved!<p>n";
 } else if ($_GET["action"] == "ViewMessages") {
    include($MessageFile);
 }


• 1 + 1 point



   02.03.2012           Confoo Conference 2012 - Antonio Fontes   30
$MessageFile = "messages/messages.out";

 if ($_GET["action"] == "NewMessage") {
    $name = $_GET["name"];
    $message = $_GET["message"];
    $handle = fopen($MessageFile, "a+");
    fwrite($handle, "<b>$name</b> says '$message'<hr>n");
                                Et s'il y a du script
    fclose($handle);
                                client?
    echo "Message Saved!<p>n";
 } else if ($_GET["action"] == "ViewMessages") {
    include($MessageFile);     include == eval()
 }                             ?


• 1 + 1 point
   – Identification de l'injection de code côté-serveur (via la fonction "include")
   – Identification de l'injection de code côté-client (via l'affichage du fichier)

   02.03.2012                 Confoo Conference 2012 - Antonio Fontes             31
// anti SQL-injection filter for user input
      string SQliProtect(string formValue)
      {
        string tmp = formValue.ToUpperCase();

      return(tmp.Replace("SELECT", "").Replace("INSERT
      ", "").Replace("UPDATE", "").Replace("UNION","")
      .Replace("BENCHMARK, "").Replace("--
      ", "").Replace("OR
      1=1", "").Replace("DROP", "").Replace("@@version
      ", "").Replace("WAITFOR", "").Replace("OUTFILE",
       "")
      ...
        return(tmp)
      }
• 1 point



   02.03.2012         Confoo Conference 2012 - Antonio Fontes   32
// anti SQL-injection filter for user input
      string SQliProtect(string formValue)
      {
        string tmp = formValue.ToUpperCase();

      return(tmp.Replace("SELECT", table" ?
                        "DRDROPOP "").Replace("INSERT
      ", "").Replace("UPDATE", "").Replace("UNION","")
      .Replace("BENCHMARK, "").Replace("--
      ", "").Replace("OR
      1=1", "").Replace("DROP", "").Replace("@@version
      ", "").Replace("WAITFOR", "").Replace("OUTFILE",
       "")
      ...
        return(tmp)
      }
• 1 point
   – Identification de la technique de contournement du filtre


   02.03.2012                Confoo Conference 2012 - Antonio Fontes   33
<?
      $reqId = 0;
      if(isset($_GET[“account_id"]))
        $reqId = (int)(htmlentities($_GET[“account_id"]));

      if($reqId == 0)
      {
        // no account selected, show the list of authorized accounts
        $sql = " SELECT * FROM accounts a "
         ." INNER JOIN account_managers am "
         ." ON a.id = am.account_id "
         ." WHERE am.manager_id = ".$currentUserID;
        echo(RenderHTMLTable($sql));
      } else {
        // docucment is clicked -> show statement
        $sql = " SELECT * FROM accounts a WHERE a.id = ".$reqId;
        RenderHTMLAccount($sql);
      }
• 2 points



   02.03.2012            Confoo Conference 2012 - Antonio Fontes   34
<?
      $reqId = 0;
      if(isset($_GET[“account_id"]))
        $reqId = (int)(htmlentities($_GET[“account_id"]));

      if($reqId == 0)
                                  Références internes?
      {
        // no account selected, show the list of authorized accounts
        $sql = " SELECT * FROM accounts a "
                                          Contrôle d’accès. Bien!
         ." INNER JOIN account_managers am "
         ." ON a.id = am.account_id "
         ." WHERE am.manager_id = ".$currentUserID;
        echo(RenderHTMLTable($sql));
      } else {
        // docucment is clicked -> show statement
        $sql = " SELECT * FROM accounts a WHERE a.id = ".$reqId;
        RenderHTMLAccount($sql);
      }                                 Mais ici?
• 2 points
   – Identification de l'exposition de références internes
   – Identification de l'absence de contrôle d'accès lors de l'affichage du document

   02.03.2012                Confoo Conference 2012 - Antonio Fontes           35
bool verifyPassword(string formPwd, int userId)
       {
         byte[] formHash = Tools.ComputeSHA1Hash(formPwd);
         byte[] dbHash = B64.Decode(User.GetPasswordHash(userId));

           if (formHash.Length != dbHash.Length)
                 return false;

           for (int i = 0; i < formHash.Length; i++)
           {
             if (formHash[i] != dbHash[i])
               return false; // the hashes don't match
           }
           // we are still here, so the passwords matched
           return true;
       }



• 1+1+1+1+1 points



   02.03.2012               Confoo Conference 2012 - Antonio Fontes   36
bool verifyPassword(string formPwd, int userId)
  {                                                 Sel?
    byte[] formHash = Tools.ComputeSHA1Hash(formPwd);
    byte[] dbHash = B64.Decode(User.GetPasswordHash(userId));
Rappatriement du mode
de passe?
     if (formHash.Length != dbHash.Length)             Algorithme fort?
             return false;

      for (int i = 0; i < formHash.Length; i++)
      {
        if (formHash[i] != dbHash[i])    Longueurs variables
          return false; // the hashes don't match
                                         possibles?
      }
      // we are still here, so the passwords matched
      return true;
  }
                      Stratégie Fail safe?

– Absence probable de sel +1
– S'interroger sur la nature de l'algorithme choisi +1
– Rapatriement inutile du mot de passe +1
– Identification de l'absence de mécanisme fail-safe +1
– Présence de signes indiquant une méconnaissance des fonctions de hachage +1
02.03.2012                    Confoo Conference 2012 - Antonio Fontes           37
Quel a été votre score?

                20 points et plus: changez de carrière, ça embauche!

                De 13 à 19 points: Très bien! Vous vous y intéressez et ça
                se voit. Vous devriez songer à appliquer vos
                connaissances aussi au code de vos collègues si ce n'est
                déjà fait, pensez aussi à joindre une association ou
                communauté traitant du sujet. Sensibilisez les gens
                autour de vous!

                De 8 à 12 points: Vous avez clairement identifié la notion
                de risque dans le code mais vous ne savez probablement
                pas encore où regarder. Il faut à présent consolider les
                bases simplement en…pratiquant!

                De 4 à 7 points: Demandez à vos chefs de vous faire
                suivre un cours! 

                Moins de 4 points: Si vous êtes développeur(ou
                développeuse), votre code est probablement dangereux
                pour la survie de l'organisation. Assurez-vous qu'il soit
                relu par une personne expérimentée dans l'attente
                d'avoir un peu plus d'expérience!
02.03.2012   Confoo Conference 2012 - Antonio Fontes                   38
Common Weakness Enumeration database:
  http://cwe.mitre.org/data/index.html


  OWASP Secure Coding Checklist:
  https://www.owasp.org/index.php/OWASP_Secure_Coding_Practices_-
  _Quick_Reference_Guide


  OWASP ASVS:
  https://www.owasp.org/index.php/Category:OWASP_Application_Security_Ve
  rification_Standard_Project



Merci de votre attention!
Si vous souhaitez me contacter:
    – antonio.fontes@L7securite.ch ou @starbuck3000
    – Newsletter: http://cddb.ch
    02.03.2012                Confoo Conference 2012 - Antonio Fontes      39

Trouvez la faille! - Confoo 2012

  • 1.
    Trouvez la faille! Antonio Fontes / Confoo 2012 - Montréal Notice 1: cette présentation contient des références à Common Weakness Enumeration: http://cwe.mitre.org/data/index.html Notice 2: aucun chat n'a été maltraité durant la préparation de cette séance. Notice 3: cette présentation contient des références au document "Test your Security IQ", par M. Howard et B. Sullivan Notice 4: Un grand merci à Sébastien pour ses idées & propositions!
  • 2.
    • Règles dejeu: – Lire l’exemple de code affiché à l'écran – Trouver la ou les éléments pouvant constituer un risque pour la sécurité du S.I. – Tenir une comptabilité analytique des points obtenus! 02.03.2012 Confoo Conference 2012 - Antonio Fontes 2
  • 3.
    Antonio Fontes Genève (Suisse) Consultant indépendant Infosécurité logicielle: Sécurité des applications web Visibilité et gestion du risque sur Internet Formation / accompagnement durant les projets de développement Bulletin d'information"cybermenaces et sécurité Internet": http://cddb.ch OWASP: Membre du Comité - OWASP Suisse Leader - OWASP Genève A propos du conférencier… 02.03.2012 Confoo Conference 2012 - Antonio Fontes 3
  • 4.
    • Le sited’actualité permet la création de comptes personnels, la publication de réactions à l’actualité, l’échange de messages entre membres. • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 4
  • 5.
    Envoi des éléments d'authentification en clair. • Le site d’actualité permet la création de comptes personnels, la publication de réactions à l’actualité, l’échange de messages entre membres. • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 5
  • 6.
    • Idem. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 6
  • 7.
    function printFile($username,$filename){ //read file into string $file = file_get_contents($filename); if ($file && isOwnerOf($username,$filename)){ echo $file; return true; } else { echo 'You are not authorized to view this file'; } return false; } • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 7
  • 8.
    function printFile($username,$filename){ //read file into string $file = file_get_contents($filename); Le chargement du if ($file && isOwnerOf($username,$filename)){ fichier a lieu avant echo $file; le contrôle d'accès. return true; } else { echo 'You are not authorized to view this file'; } return false; } • 1 point – Identification du risque pour la disponibilité du S.I. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 8
  • 9.
    protected void Page_Load(objectsender, EventArgs e) { string lastLogin = Request["LastLogin"]; if (String.IsNullOrEmpty(lastLogin)) { HttpCookie lastLoginCookie = new HttpCookie("LastLogin", DateTime.Now.ToShortDateString()); lastLoginCookie.Expires = DateTime.Now.AddYears(1); Response.Cookies.Add(lastLoginCookie); } else { Response.Write("Welcome back! You last logged in on " + lastLogin); Response.Cookies["LastLogin"].Value = DateTime.Now.ToShortDateString(); } } • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 9
  • 10.
    protected void Page_Load(objectsender, EventArgs e) { string lastLogin = Request["LastLogin"]; if (String.IsNullOrEmpty(lastLogin)) { Appel vers la HttpCookie lastLoginCookie = new HttpCookie("LastLogin", collection parente DateTime.Now.ToShortDateString()); "Request" lastLoginCookie.Expires = DateTime.Now.AddYears(1); Response.Cookies.Add(lastLoginCookie); } else { Response.Write("Welcome back! You last logged in on " + lastLogin); Response.Cookies["LastLogin"].Value Transfert du contenu = DateTime.Now.ToShortDateString(); vers le client, sans } encodage approprié. } • 1 point – Cas de type "XSS" (Cross-site scripting) 02.03.2012 Confoo Conference 2012 - Antonio Fontes 10
  • 11.
    $role = $_COOKIES['role']; if (!$role) { $role = getRole('user'); if ($role) { // save the cookie to send out in future responses setcookie("role", $role, time()+60*60*2); } else { ShowLoginScreen(); die("n"); } } if ($role == 'Reader') { DisplayMedicalHistory($_POST['patient_ID']); } else { die("You are not Authorized to view this recordn"); } • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 11
  • 12.
    $role = $_COOKIES['role']; if (!$role) { Absence de contrôle $role = getRole('user'); d'intégrité du if ($role) { cookie. // save the cookie to send out in future responses setcookie("role", $role, time()+60*60*2); } else { Contournement du ShowLoginScreen(); mécanisme die("n"); d'authentification } } if ($role == 'Reader') { Contournement du DisplayMedicalHistory($_POST['patient_ID']); contrôle d'accès } else { die("You are not Authorized to view this recordn"); } • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 12
  • 13.
    • 1 +1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 13
  • 14.
    Transfert de confiance à un tiers • 1 + 1 + 1 points – 1 point: identification du risque d'injection de contenu par un tiers – 1 point: identification du risque sur la confidentialité (fuite des referrers) – 1 point: identification du risque de déni de service sur le tiers 02.03.2012 Confoo Conference 2012 - Antonio Fontes 14
  • 15.
    • 1 +1 + 1 points – Faire attention aux recommandations sur le web: elles vont souvent à l'encontre de la sécurité et visent à faciliter la collecte de données par des tiers. – Vérifier qui est l'auteur d'une recommandation de codage. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 15
  • 16.
    // API flag,output JSON if set $json = $_GET['json']; $username = $_GET['user']; if($json) { $record = getUserRecord($username); echo(json_encode($record)); } else { $record = getUserRecord($username); foreach($record as $fieldName => $fieldValue) { // never disclose user email addresses to the public (privacy req.) if(!($fieldName == "email_address")) renderToHtmlTable ($fieldName,$fieldValue); }}} • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 16
  • 17.
    // API flag,output JSON if set $json = $_GET['json']; Dans le cas json, $username = $_GET['user']; l'adresse email n'est if($json) { plus protégée contre les $record = getUserRecord($username); fuites. echo(json_encode($record)); } else { $record = getUserRecord($username); foreach($record as $fieldName => $fieldValue) { // never disclose user email addresses to the public (privacy req.) if(!($fieldName == "email_address")) renderToHtmlTable ($fieldName,$fieldValue); }}} • 1 point – Identification de la fuite d'adresses email 02.03.2012 Confoo Conference 2012 - Antonio Fontes 17
  • 18.
    byte[] GetKey(UInt32 keySize) { byte[] key = null; try { key = new byte[keySize]; RNGCryptoServiceProvider.Create().GetBytes(key); } catch (Exception e) { Math.Random r = new Math.Random(); r.NextBytes(key); } return key; } 1 + 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 18
  • 19.
    byte[] GetKey(UInt32 keySize) { byte[] key = null; try { key = new byte[keySize]; RNGCryptoServiceProvider.Create().GetBytes(key); } catch (Exception e) { Exception générique? Math.Random r = new Math.Random(); r.NextBytes(key); } Math.Random? Fail-safe? return key; } 1 + 1 + 1 point – Exception générique: privilégier l'exception typée – Principe de conception "Fail-safe": le code n'échoue pas en haute sécurité – La classe Math.Random ne fournit pas d'entropie de niveau cryptographique 02.03.2012 Confoo Conference 2012 - Antonio Fontes 19
  • 20.
    private decimal? lookupPrice(XmlDocumentdoc) { string query = @"//products/product[id/text()='" + Request["itemId"] + "']/price" XmlNode node = doc.SelectSingleNode(query); if (node == null) return null; else return(Convert.ToDecimal(node.InnerText)); } • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 20
  • 21.
    private decimal? lookupPrice(XmlDocumentdoc) { string query = @"//products/product[id/text()='" + Request["itemId"] + "']/price" XmlNode node = doc.SelectSingleNode(query); Validation? if (node == null) return null; else return(Convert.ToDecimal(node.InnerText)); } • 1 point – Injection de type Xpath (il n'y a pas que des injections SQL!!) – Marche aussi sur: commandes système, LDAP, APIs ORM, etc. 02.03.2012 Confoo Conference 2012 - Antonio Fontes 21
  • 22.
    public class MySessionIDManager: System.Web.Session State.SessionIDManager { private static object lockObject = new object(); public override string CreateSessionID(HttpContext context) { lock (lockObject) { Int32? lastSessionId = (int?)context.Application ["LastSessionId"]; if (lastSessionId == null) lastSessionId = 1; else lastSessionId++; context.Application["LastSessionId"] = lastSessionId; return lastSessionId.ToString(); }}} • 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 22
  • 23.
    public class MySessionIDManager: System.Web.Session State.SessionIDManager { private static object lockObject = new object(); public override string CreateSessionID(HttpContext context) { lock (lockObject) Multi-serveur? { Int32? lastSessionId = (int?)context.Application ["LastSessionId"]; if (lastSessionId == null) lastSessionId = 1; ID de session prédictibles else lastSessionId++; context.Application["LastSessionId"] = lastSessionId; return lastSessionId.ToString(); }}} • 1 + 1 point – Identification de l'identifiant de session prédictible – Collision des identifiants de session si le serveur est répliqué! 02.03.2012 Confoo Conference 2012 - Antonio Fontes 23
  • 24.
    bool login(SqlConnection connection,out string errorMessage) { string uname = Request.Form["username"]; string pword = Request.Form["password"]; SqlCommand selectUserAndPassword = new SqlCommand( "SELECT pwd FROM Users WHERE uname = @username", connection); selectUserAndPassword.Parameters.Add( new SqlParameter("@username", uname)); string validPassword = (string)selectUserAndPassword.ExecuteScalar(); if (validPassword == null) { // the user doesn't exist in the database errorMessage = "The username is invalid."; return false; } else if (validPassword != pword) { // the given password doesn't match errorMessage = "The password is incorrect."; return false; } else { // success Bug #9 errorMessage = String.Empty; return true; } } 1 + 1 + 1 points – 1 point + bonus point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 24
  • 25.
    bool login(SqlConnection connection,out string errorMessage) { Requête paramétrée, ça, c'est juste! string uname = Request.Form["username"]; string pword = Request.Form["password"]; SqlCommand selectUserAndPassword = new SqlCommand( "SELECT pwd FROM Users WHERE uname = @username", connection); selectUserAndPassword.Parameters.Add( new SqlParameter("@username", uname)); string validPassword = (string)selectUserAndPassword.ExecuteScalar(); if (validPassword == null) { // the user doesn't exist in the database Rapatriement inutile du errorMessage = "The username is invalid."; return false; mot de passe! } else if (validPassword != pword) { Stockage du mot de passe en // the given password doesn't match clair. errorMessage = "The password is incorrect."; return false; } else Message d'erreur variable lorsque le { // success login ou le mdp est faux (fuite) Bug #9 errorMessage = String.Empty; return true; } } 1 + 1 + 1 points – 1 point + bonus point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 25
  • 26.
    // SilverLight codemodule review bool verifyCode(string discountCode) { // We store the hash of the secret code instead of the plaintext of the secret code for security. // We hash the incoming value and compare it against the stored hash. byte[] codeHash = SecurityUtils.ComputeHash(discountCode, "MD5"); byte[] secretCode = new byte[] { 116, 46, 130, 122, 36, 234, 158, 125, 163, 122, 157, 186, 64, 142, 51, 153, 113, 79, 1, 42 }; // This should never happen, but we check it anyway if (codeHash.Length != secretCode.Length) return false; // perform an element-by-element comparison of the arrays for (int i = 0; i < codeHash.Length; i++) { if (codeHash[i] != secretCode[i]) return false; // the hashes don't match } // all the elements match, so the strings match // the discount code is valid, inform the server WebServiceSoapClient client = new WebServiceSoapClient(); client.ApplyDiscountCode(); return true; 1 + 1 + 2 points } 02.03.2012 Confoo Conference 2012 - Antonio Fontes 26
  • 27.
    // SilverLight codemodule review bool verifyCode(string discountCode) Algorithme déconseillé (+1) { // We store the hash of the secret code instead of the plaintext of the secret code for security. // We hash the incoming value and compare it against the stored hash. byte[] codeHash = SecurityUtils.ComputeHash(discountCode, "MD5"); byte[] secretCode = new byte[] { 116, 46, 130, 122, 36, 234, 158, 125, 163, 122, 157, 186, 64, 142, 51, 153, 113, 79, 1, 42 }; // This should never happen, but we check it anyway A-t-on besoin d'un if (codeHash.Length != secretCode.Length) sel? (seed) +1 return false; // perform an element-by-element comparison of the arrays for (int i = 0; i < codeHash.Length; i++) { if (codeHash[i] != secretCode[i]) Défense côté client  return false; // the hashes don't match } // all the elements match, so the strings match totalement inutile! (+2 points) // the discount code is accepted, inform the server WebServiceSoapClient client = new WebServiceSoapClient(); client.ApplyDiscountCode(); return true; 1 + 1 + 2 points } 02.03.2012 Confoo Conference 2012 - Antonio Fontes 27
  • 28.
    • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 28
  • 29.
    Injection SQL (absence de validation) • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 29
  • 30.
    $MessageFile = "messages/messages.out"; if ($_GET["action"] == "NewMessage") { $name = $_GET["name"]; $message = $_GET["message"]; $handle = fopen($MessageFile, "a+"); fwrite($handle, "<b>$name</b> says '$message'<hr>n"); fclose($handle); echo "Message Saved!<p>n"; } else if ($_GET["action"] == "ViewMessages") { include($MessageFile); } • 1 + 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 30
  • 31.
    $MessageFile = "messages/messages.out"; if ($_GET["action"] == "NewMessage") { $name = $_GET["name"]; $message = $_GET["message"]; $handle = fopen($MessageFile, "a+"); fwrite($handle, "<b>$name</b> says '$message'<hr>n"); Et s'il y a du script fclose($handle); client? echo "Message Saved!<p>n"; } else if ($_GET["action"] == "ViewMessages") { include($MessageFile); include == eval() } ? • 1 + 1 point – Identification de l'injection de code côté-serveur (via la fonction "include") – Identification de l'injection de code côté-client (via l'affichage du fichier) 02.03.2012 Confoo Conference 2012 - Antonio Fontes 31
  • 32.
    // anti SQL-injectionfilter for user input string SQliProtect(string formValue) { string tmp = formValue.ToUpperCase(); return(tmp.Replace("SELECT", "").Replace("INSERT ", "").Replace("UPDATE", "").Replace("UNION","") .Replace("BENCHMARK, "").Replace("-- ", "").Replace("OR 1=1", "").Replace("DROP", "").Replace("@@version ", "").Replace("WAITFOR", "").Replace("OUTFILE", "") ... return(tmp) } • 1 point 02.03.2012 Confoo Conference 2012 - Antonio Fontes 32
  • 33.
    // anti SQL-injectionfilter for user input string SQliProtect(string formValue) { string tmp = formValue.ToUpperCase(); return(tmp.Replace("SELECT", table" ? "DRDROPOP "").Replace("INSERT ", "").Replace("UPDATE", "").Replace("UNION","") .Replace("BENCHMARK, "").Replace("-- ", "").Replace("OR 1=1", "").Replace("DROP", "").Replace("@@version ", "").Replace("WAITFOR", "").Replace("OUTFILE", "") ... return(tmp) } • 1 point – Identification de la technique de contournement du filtre 02.03.2012 Confoo Conference 2012 - Antonio Fontes 33
  • 34.
    <? $reqId = 0; if(isset($_GET[“account_id"])) $reqId = (int)(htmlentities($_GET[“account_id"])); if($reqId == 0) { // no account selected, show the list of authorized accounts $sql = " SELECT * FROM accounts a " ." INNER JOIN account_managers am " ." ON a.id = am.account_id " ." WHERE am.manager_id = ".$currentUserID; echo(RenderHTMLTable($sql)); } else { // docucment is clicked -> show statement $sql = " SELECT * FROM accounts a WHERE a.id = ".$reqId; RenderHTMLAccount($sql); } • 2 points 02.03.2012 Confoo Conference 2012 - Antonio Fontes 34
  • 35.
    <? $reqId = 0; if(isset($_GET[“account_id"])) $reqId = (int)(htmlentities($_GET[“account_id"])); if($reqId == 0) Références internes? { // no account selected, show the list of authorized accounts $sql = " SELECT * FROM accounts a " Contrôle d’accès. Bien! ." INNER JOIN account_managers am " ." ON a.id = am.account_id " ." WHERE am.manager_id = ".$currentUserID; echo(RenderHTMLTable($sql)); } else { // docucment is clicked -> show statement $sql = " SELECT * FROM accounts a WHERE a.id = ".$reqId; RenderHTMLAccount($sql); } Mais ici? • 2 points – Identification de l'exposition de références internes – Identification de l'absence de contrôle d'accès lors de l'affichage du document 02.03.2012 Confoo Conference 2012 - Antonio Fontes 35
  • 36.
    bool verifyPassword(string formPwd,int userId) { byte[] formHash = Tools.ComputeSHA1Hash(formPwd); byte[] dbHash = B64.Decode(User.GetPasswordHash(userId)); if (formHash.Length != dbHash.Length) return false; for (int i = 0; i < formHash.Length; i++) { if (formHash[i] != dbHash[i]) return false; // the hashes don't match } // we are still here, so the passwords matched return true; } • 1+1+1+1+1 points 02.03.2012 Confoo Conference 2012 - Antonio Fontes 36
  • 37.
    bool verifyPassword(string formPwd,int userId) { Sel? byte[] formHash = Tools.ComputeSHA1Hash(formPwd); byte[] dbHash = B64.Decode(User.GetPasswordHash(userId)); Rappatriement du mode de passe? if (formHash.Length != dbHash.Length) Algorithme fort? return false; for (int i = 0; i < formHash.Length; i++) { if (formHash[i] != dbHash[i]) Longueurs variables return false; // the hashes don't match possibles? } // we are still here, so the passwords matched return true; } Stratégie Fail safe? – Absence probable de sel +1 – S'interroger sur la nature de l'algorithme choisi +1 – Rapatriement inutile du mot de passe +1 – Identification de l'absence de mécanisme fail-safe +1 – Présence de signes indiquant une méconnaissance des fonctions de hachage +1 02.03.2012 Confoo Conference 2012 - Antonio Fontes 37
  • 38.
    Quel a étévotre score? 20 points et plus: changez de carrière, ça embauche! De 13 à 19 points: Très bien! Vous vous y intéressez et ça se voit. Vous devriez songer à appliquer vos connaissances aussi au code de vos collègues si ce n'est déjà fait, pensez aussi à joindre une association ou communauté traitant du sujet. Sensibilisez les gens autour de vous! De 8 à 12 points: Vous avez clairement identifié la notion de risque dans le code mais vous ne savez probablement pas encore où regarder. Il faut à présent consolider les bases simplement en…pratiquant! De 4 à 7 points: Demandez à vos chefs de vous faire suivre un cours!  Moins de 4 points: Si vous êtes développeur(ou développeuse), votre code est probablement dangereux pour la survie de l'organisation. Assurez-vous qu'il soit relu par une personne expérimentée dans l'attente d'avoir un peu plus d'expérience! 02.03.2012 Confoo Conference 2012 - Antonio Fontes 38
  • 39.
    Common Weakness Enumerationdatabase: http://cwe.mitre.org/data/index.html OWASP Secure Coding Checklist: https://www.owasp.org/index.php/OWASP_Secure_Coding_Practices_- _Quick_Reference_Guide OWASP ASVS: https://www.owasp.org/index.php/Category:OWASP_Application_Security_Ve rification_Standard_Project Merci de votre attention! Si vous souhaitez me contacter: – antonio.fontes@L7securite.ch ou @starbuck3000 – Newsletter: http://cddb.ch 02.03.2012 Confoo Conference 2012 - Antonio Fontes 39

Notes de l'éditeur

  • #23  En cas de distribution? Identifiant prévisible
  • #24  En cas de distribution? Identifiant prévisible
  • #25 Password a été rapatrié en local
  • #26 Password a été rapatrié en local
  • #31  En cas de distribution? Identifiant prévisible
  • #32  En cas de distribution? Identifiant prévisible