<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Hooba Studios &#187; Sécurité</title>
	<atom:link href="http://www.hooba.ca/blog/categorie/securite/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.hooba.ca/blog</link>
	<description></description>
	<lastBuildDate>Sun, 11 Jul 2010 13:51:09 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Bon code, Bad code</title>
		<link>http://www.hooba.ca/blog/2009/bon-code-bad-code/</link>
		<comments>http://www.hooba.ca/blog/2009/bon-code-bad-code/#comments</comments>
		<pubDate>Fri, 17 Jul 2009 03:19:15 +0000</pubDate>
		<dc:creator>Antoine Leclair</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Sécurité]]></category>
		<category><![CDATA[Théorie]]></category>

		<guid isPermaLink="false">http://www.hooba.ca/blog/?p=384</guid>
		<description><![CDATA[Il n&#8217;y a pas si longtemps, j&#8217;ai eu à intégrer du code PHP qu&#8217;un autre programmeur avait déjà fait pour un autre site. Le client tenait à ce que j&#8217;utilise son &#171;&#160;module&#160;&#187; de nouvelles en pensant sauver de l&#8217;argent. La première fois que j&#8217;ai eu à intégrer ledit module, ç&#8217;a effectivement été plutôt rapide étant [...]]]></description>
			<content:encoded><![CDATA[<p>Il n&#8217;y a pas si longtemps, j&#8217;ai eu à intégrer du code PHP qu&#8217;un autre programmeur avait déjà fait pour un autre site. Le client tenait à ce que j&#8217;utilise son &laquo;&nbsp;module&nbsp;&raquo; de nouvelles en pensant sauver de l&#8217;argent. La première fois que j&#8217;ai eu à intégrer ledit module, ç&#8217;a effectivement été plutôt rapide étant donné que le site était fait dans le même <em>pattern</em> que le site d&#8217;origine. Par contre, la deuxième fois, c&#8217;était une toute autre chose!<span id="more-384"></span></p>
<h3>Du code non-réutilisable</h3>
<p>J&#8217;espère ne rendre personne triste en publiant cet article, mais je vais vous faire un petit copier-coller d&#8217;une fonction pour illustrer mes points.</p>
<pre>&lt;?php
// _______________________________________________________________________
// Display news list
function fct_display_news_list(){
    // Search
    if(isset($_GET['search']) &amp;&amp; !empty($_GET['search'])){
        $query = " WHERE title LIKE CONVERT(_utf8 '%".$_GET['search']."%' USING latin1) OR content LIKE CONVERT(_utf8 '%".$_GET['search']."%' USING latin1)";
    }else{
        if(isset($_GET['date']) &amp;&amp; ereg("^[0-9]{4}\-[0-9]{2}$", $_GET['date'])){
            $arr = explode("-", $_GET['date']);
            $date_start = mktime(0, 0, 0, $arr[1], 1, $arr[0]);
            $date_end = mktime(0, 0, 0, $arr[1] + 1, 1, $arr[0]);
            $query = " WHERE date_news&gt;=".$date_start." AND date_news&lt;".$date_end;
        }
    }
    if(!isset($query)){
        $query = " ORDER BY date_news DESC LIMIT 3";
    }else{
        $query .= " ORDER BY date_news DESC";
    }

    $query = "SELECT * FROM juris_news".$query;
    $result = mysql_query($query) or die(mysql_error());
    if(mysql_num_rows($result) &gt; 0){
        for($i = 1; $row = mysql_fetch_array($result); $i++){
?&gt;
    &lt;h3 first"); } ?&gt;"&gt;&lt;?= $row['title']; ?&gt;&lt;/h3&gt;
    &lt;p&gt;&lt;?= date("Y-m-d", $row['date_news']); ?&gt;&lt;/p&gt;
    &lt;p&gt;&lt;?= $row['intro']; ?&gt;&lt;/p&gt;
    &lt;p&gt;&lt;a href="actualites.php?id=&lt;?= $row['id']; ?&gt;" onmouseout="MM_swapImgRestore()" onmouseover="MM_swapImage('news&lt;?= $i; ?&gt;','','medias/img_c_btn_savoir_rl.gif',0)"&gt;&lt;img src="medias/img_c_btn_savoir_up.gif" alt="En savoir plus" name="news&lt;?= $i; ?&gt;" id="news&lt;?= $i; ?&gt;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;?php
        }
    }else{
        echo("&lt;p&gt;Aucune actualit&amp;eacute; trouv&amp;eacute;e.&lt;/p&gt;");
    }
}</pre>
<p>Il s&#8217;agit ici de la première fonction d&#8217;une dizaine de fonctions similaires qui constituent le fichier.</p>
<h3>Premier péché: mélanger la logique avec la présentation</h3>
<p>La principale force de PHP est aussi sa principale faiblesse: il est très facile de mélanger du PHP avec du HTML. On peut avoir une page entière en HTML et y insérer très facilement un peu de PHP. On peut par contre aussi avoir du PHP et y insérer du HTML. Le problème avec cette fonction, c&#8217;est qu&#8217;elle génère (et affiche) du HTML.</p>
<p>On peut voir très facilement que le code HTML affiché n&#8217;est pas réutilisable. Il y a des appels à des fonctions JavaScript et des images dont le chemin d&#8217;accès est <em>hard coded</em>. Par exemple, la fonction affiche:</p>
<pre><code>// mauvais
</code>&lt;p&gt;&lt;a href="actualites.php?id=&lt;?= $row['id']; ?&gt;" onmouseout="MM_swapImgRestore()" onmouseover="MM_swapImage('news&lt;?= $i; ?&gt;','','medias/img_c_btn_savoir_rl.gif',0)"&gt;&lt;img src="medias/img_c_btn_savoir_up.gif" alt="En savoir plus" name="news&lt;?= $i; ?&gt;" id="news&lt;?= $i; ?&gt;" /&gt;&lt;/a&gt;&lt;/p&gt;</pre>
<p>Il est fort peu probablement que j&#8217;aie à utiliser les mêmes fonctions JavaScript et que j&#8217;utilise les mêmes images. Aussi, le <em>markup</em> HTML généré ne conviendra générallement pas aux besoins d&#8217;un autre site. Par exemple, je doute que <code>&lt;h3 class="blue"&gt;</code> puissent être réutilisé.</p>
<p>La meilleur façon aurait été de simplement arrêter la fonction à la fin de la requête SQL. La fonction aurait simplement pu retourner un <em>array</em>, un objet ou un itérateur. De cette manière, on sépare la logique de la présentation. Par exemple, si la fonction retournait un <em>array</em> <code>$nouvelles</code>, on pourrait l&#8217;intégrer dans la page directement.</p>
<pre><code>&lt;!-- Mieux --&gt;
&lt;!-- Markup de ce qui vient avant dans la page ici... --&gt;
&lt;div id="nouvelles"&gt;
    &lt;h2&gt;Nouvelles&lt;/h2&gt;
    &lt;?php if (!empty($nouvelles)) : ?&gt;
    &lt;?php foreach ($nouvelles as $nouvelle) : ?&gt;
    &lt;p class="date"&gt;&lt;?= date("Y-m-d", $nouvelle['date_news']) ?&gt;&lt;/p&gt;
    &lt;p&gt;&lt;?= $nouvelle['intro'] ?&gt;&lt;/p&gt;
    &lt;p&gt;&lt;a href="actualites.php?id=&lt;?= $nouvelle['id']; ?&gt;" class="more"&gt;En savoir plus&lt;/a&gt;&lt;/p&gt;
    &lt;?php endforeach ?&gt;
    &lt;?php else : ?&gt;
    &lt;p&gt;Aucune nouvelle&lt;/p&gt;
    &lt;?php endif ?&gt;
&lt;/div&gt;
&lt;!-- Suite du site ici... --&gt;
&lt;div id="sidebar"&gt;</code></pre>
<h3>Deuxième péché: La fonction n&#8217;accepte aucun paramètre</h3>
<p>De la façon dont la fonction est codée, on n&#8217;a aucun moyen externe (lire: aucun paramètre) d&#8217;affecter son déroulement. Si <code>$_GET['search']</code> contient quelque chose, une recherche sera faite, sinon, les trois dernières nouvelles sont affichées.</p>
<p>Il serait bien de pouvoir appeler la fonction en lui demandant un certain nombre de nouvelles. Je ne veux pas avoir toujours les trois dernières nouvelles dans tous les sites que je dois faire.</p>
<p>Aussi, si je veux faire une recherche, je ne veux pas nécessairement utiliser <code>$_GET['search']</code> comme chaine à chercher.</p>
<p>Le prototype de cette fonction aurait plutôt dû ressembler à ceci:</p>
<pre><code>function fct_display_news_list($quantity = 3, $search = NULL)</code></pre>
<p>De cette manière, on aurait pu avoir des comportements par défaut si on ne passe aucun argument tout en ayant un certain contrôle en cas de besoin. Si je voulais à ce moment utiliser la fonction en utilisant la valeur de <code>$_GET['search']</code> comme clé de recherche, j&#8217;aurais pu l&#8217;appeler ainsi:</p>
<pre><code>fct_display_news(3, $_GET['search'])</code></pre>
<h3>Troisième péché: la fonction fait trop de choses</h3>
<p>La fonction &laquo;&nbsp;raboute&nbsp;&raquo; des morceaux de requêtes SQL <strong>en créant la fin de la requête</strong> (<code>"WHERE ..."</code>) <strong>en premier</strong>. Je trouve étrange d&#8217;avoir commencé par la fin sans raison apparente. Ça ne fait que complexifier le code.</p>
<p>En plus, pourquoi ne pas avoir simplement séparé la fonction en deux fonctions? Chaque fonction aurait été beaucoup plus claire et facile à maintenir. Exemple:</p>
<pre><code>// mieux
function get_news($quantity = 3, $search = NULL){
    if ($search == NULL)
        return get_latest_news($quantity);
    else // $search != NULL
        return get_searched_news($quantity, $search);
}

function get_lastest_news($quantity = 3){
    // fonction qui retourne les $quantity dernières nouvelles
    // ...
}

function get_searched_news($quantity = 3, $search = NULL){
    // fonction qui retourne les $quantity dernières nouvelles trouvées selon la clé de recherche
    // ...
}</code></pre>
<p>Si on regarde la liste des fonctions du fichier, on se rend vite compte qu&#8217;une approche orienté objet aurait très bien fait la job. Une classe <code>News</code> aurait grandement simplifié le tout.</p>
<h3>Quatrième péché: des noms qui ne veulent rien dire</h3>
<p>Si on retourne dans le code au début de l&#8217;article, on peut voir qu&#8217;il y a plusieurs noms encombrés d&#8217;abréviations superflues ou qui ne veulent rien dire aux yeux de la prochaine personne qui touche le code.</p>
<p>Est-ce que j&#8217;ai vraiment besoin de spécifier dans le nom d&#8217;une fonction qu&#8217;il s&#8217;agit d&#8217;une fonction en y préfixant <code>fct</code>? Aussi, un fichier nommé <code>img_c_btn_savoir_up.gif</code>: un fichier <code>gif</code> est une image, pas besoin de préfixer <code>img</code> à son nom. Dans le même nom de fichier, que veut dire le &laquo;&nbsp;<code>c</code>&laquo;&nbsp;?</p>
<p>Une fonction devrait avoir un nom qui permet de savoir ce qu&#8217;elle fait tout en étant concis. <code>display_news_list</code> aurait été suffisant.</p>
<h3>Cinquième péché: être vulnérable aux injections SQL</h3>
<p>Étant donné que la fonction n&#8217;accepte aucun paramètre, le programmeur a utilisé la valeur de <code>$_GET['search']</code> directement dans sa fonction. Le problème que je veux souligner ici c&#8217;est la manière dont il l&#8217;a utilisée. Il l&#8217;a mis directement dans une requête sans la filtrer:</p>
<pre><code>// mauvais
$query = " WHERE title LIKE CONVERT(_utf8 '%".$_GET['search']."%' USING latin1) OR content LIKE CONVERT(_utf8 '%".$_GET['search']."%' USING latin1)";
</code></pre>
<p>En insérant directement des valeurs fournies par l&#8217;utilisateur (ici, dans la barre d&#8217;adresse) sans les avoir filtrés au préalable est une mauvaise pratique car elle ouvre très grand les portes aux <a href="http://www.hooba.ca/blog/2009/prevenir-les-injections-sql-avec-php-et-mysql/">attaques par injection SQL</a>.</p>
<h3>En conclusion</h3>
<p>Quand on programme, c&#8217;est important de toujours penser à la réutilisabilité du code. Il faut bien sûr parfois faire du code &laquo;&nbsp;<em>quick and dirty</em>&laquo;&nbsp;, mais les choses importantes que je viens d&#8217;énumérer ne sont pas compliqués à respecter. D&#8217;ailleurs, le programmeur aurait en fait sauvé du temps s&#8217;il avait fait du code réutilisable, car il a eu à reprogrammer un autre ensemble complet de fonctions pour le côté administrateur. S&#8217;il avait dès le départ fait une nouvelle classe ou simplement mieux conçu ses fonctions, il aurait facilement pu sauver beaucoup de temps en réutilisant lui-même son propre code à l&#8217;intérieur du même projet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hooba.ca/blog/2009/bon-code-bad-code/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Prévenir les injections SQL avec PHP et MySQL</title>
		<link>http://www.hooba.ca/blog/2009/prevenir-les-injections-sql-avec-php-et-mysql/</link>
		<comments>http://www.hooba.ca/blog/2009/prevenir-les-injections-sql-avec-php-et-mysql/#comments</comments>
		<pubDate>Tue, 20 Jan 2009 19:12:44 +0000</pubDate>
		<dc:creator>Antoine Leclair</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Sécurité]]></category>

		<guid isPermaLink="false">http://www.hooba.ca/blog/?p=253</guid>
		<description><![CDATA[Tout d&#8217;abord, pour ceux qui ne savent pas ce qu&#8217;est une attaque SQL, je vous fais une brève explication.
Exemple d&#8217;attaque par injection SQL
Une personne mal intentionnée peut tenter d&#8217;abuser votre code PHP en insérant des morceaux de SQL à des endroits que vous vous n&#8217;y attendez pas sur votre site web. Par exemple, une page [...]]]></description>
			<content:encoded><![CDATA[<p>Tout d&#8217;abord, pour ceux qui ne savent pas ce qu&#8217;est une attaque SQL, je vous fais une brève explication.</p>
<h3>Exemple d&#8217;attaque par injection SQL</h3>
<p>Une personne mal intentionnée peut tenter d&#8217;abuser votre code PHP en insérant des morceaux de SQL à des endroits que vous vous n&#8217;y attendez pas sur votre site web. Par exemple, une page qui montre les produits d&#8217;une certaine catégorie à l&#8217;aide d&#8217;un paramètre GET.<span id="more-253"></span> Une version simplifiée de votre code pourrait ressembler à ceci:</p>
<pre><code>&lt;?php
// connection à la base de donnée
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
$query = 'SELECT * FROM products WHERE category="'
        .$_GET['category'].'"';
$mysqli->query($query);
// ... suite du code ...
?&gt;</code></pre>
<p>Avec <code>$_GET['category']=&#39;rasoirs&#39;</code>, la requête ressemblerait à ceci:</p>
<pre><code>SELECT * FROM products WHERE category="rasoirs"</code></pre>
<p>Si l&#8217;utilisateur réussi à passer <code>$_GET['category']=&#39;rasoirs&#34; OR 1=1&#39;</code>, la requête se fait transformer en quelque chose du genre:</p>
<pre><code>SELECT * FROM products WHERE category="rasoirs" OR 1=1</code></pre>
<p>L&#8217;exemple ici n&#8217;est pas si dangereux, mais on peut facilement imaginer des requêtes du genre:</p>
<pre><code>UPDATE products SET en_special="1" WHERE category="rasoirs"</code></pre>
<p>devenir des requêtes du genre:</p>
<pre><code>UPDATE products SET en_special="1" WHERE category="rasoirs" OR 1=1</code></pre>
<p>Regardez cet exemple concret d&#8217;<a href="http://xkcd.com/327/">une attaque par injection SQL</a> <img src='http://www.hooba.ca/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> .</p>
<h3>Comment on faisait pour éviter ce problème</h3>
<p>Une pratique encore valide aujourd&#8217;hui est de ne jamais mettre directement des données entrées par un utilisateurs à des endroits qui pourraient affecter de manière nuisible le programme. Dans l&#8217;exemple précédent des catégories, on aurait pu faire quelque chose qui ressemble à:</p>
<pre><code>switch($_GET['category']){
  case 'rasoirs':
    $query = 'SELECT * FROM products WHERE category="rasoirs"';
    break;
// etc.
}</code></pre>
<p>Bien sûr, on doit souvent utiliser les entrées de l&#8217;utilisateurs pour fabriquer des requêtes. Il faut alors filtrer ce que l&#8217;utilisateur envoie, avec des fonctions du genre <code>mysql_real_escape()</code>. Mais cette fonction peut maintenant être compté comme désuette grâce à la compilation des requêtes.</p>
<h3>Nouvelle façon</h3>
<p>Grâce aux nouveaux (okay, pas si nouveau) <em>compiled statements</em> (requêtes compilées), on peut désormais être certain que l&#8217;utilisateur ne réussira pas à déborder hors de ce qu&#8217;on avait prévu initiallement. Voici un exemple:</p>
<pre><code>$query = 'INSERT INTO users (name, password, date_added) '
        .'VALUES (?, ?, ?);
$stmt = $mysqli->prepare($query);
if ($stmt){
  $stmt->bind_param('ssi', $name, $password, $date_added);
  // s pour string, i pour integer
  $stmt->execute();
}
</code></pre>
<p>La requête SQL est donc compilée avant même de savoir ce qui se tiendra à la place des <code>?</code>. On ne fait qu&#8217;assigner plus tard ce qui les remplacera.</p>
<p>Premier avantage, qui est le sujet de mon article, est que ça prévient les attaques SQL. Dans l&#8217;exemple des catégories de produits ci-haut, même si l&#8217;utilisateur réussissait à assigner une valeur du genre <code>rasoirs" OR 1=1</code> à notre variable qui ira dans notre requête SQL, la requête était déjà compilée. La requête aura donc l&#8217;effet de celle-ci:</p>
<pre><code>SELECT * FROM products WHERE category="rasoirs\" OR 1=1"</code></pre>
<p>Elle cherchera donc RÉELLEMENT les produits appartenant à la catégorie <code>rasoirs" OR 1=1</code> (qui n&#8217;existe pas).</p>
<p>Deuxième avantage des requêtes précompilées, c&#8217;est que si vous avez à exécuter plusieurs requetes dont seulement certain paramètres changent, en ne compilant qu&#8217;une seule fois la requête et en l&#8217;exécutant avec différentes variables, vous sauver du temps-machine. Lorsque vous y allez &laquo;&nbsp;à l&#8217;ancienne&nbsp;&raquo;, le requête est compilée à chaque fois.</p>
<h3>Conclusion et liens</h3>
<p>La plupart des bases de données offrent aujourd&#8217;hui la possibilité de compiler ses requête à l&#8217;avance et je trouve que c&#8217;est une excellente pratique de profiter de cette fonctionnalité.</p>
<p>Les PDO (PHP Data Objects) n&#8217;offrent qu&#8217;une abstraction très mince au-dessus des bases de données. Ils n&#8217;offrent générallement pas de fonctionnalités ajoutées. Pourtant, les concepteurs ont décidé d&#8217;implémenter dans les PDO la possibilité de compiler ses requêtes même lorsque la base de donnée utilisée ne l&#8217;accepte pas! Ils s&#8217;en chargent pour nous. Pour vous dire à quel point c&#8217;est important.</p>
<p>Si vous voulez un peu plus d&#8217;information sur les attaques SQL, vous pouvez lire cette <a href="http://en.wikipedia.org/wiki/SQL_injection">page de Wikipedia</a> et cette <a href="http://www.unixwiz.net/techtips/sql-injection.html">page web</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hooba.ca/blog/2009/prevenir-les-injections-sql-avec-php-et-mysql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
