blog.notmyidea.org/service-de-nuages-garantir-lintegrite-des-donnees-via-des-signatures-fr.html

155 lines
No EOL
10 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<title>
Service de nuages : Garantir l&#8217;intégrité des données via des&nbsp;signatures - Alexis Métaireau </title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="https://blog.notmyidea.org/theme/css/main.css?v2"
type="text/css" />
<link href="https://blog.notmyidea.org/feeds/all.atom.xml"
type="application/atom+xml"
rel="alternate"
title="Alexis Métaireau ATOM Feed" />
</head>
<body>
<div id="content">
<section id="links">
<ul>
<li>
<a class="main" href="/">Alexis Métaireau</a>
</li>
<li>
<a class=""
href="https://blog.notmyidea.org/journal/index.html">Journal</a>
</li>
<li>
<a class="selected"
href="https://blog.notmyidea.org/code/">Code, etc.</a>
</li>
<li>
<a class=""
href="https://blog.notmyidea.org/weeknotes/">Notes hebdo</a>
</li>
<li>
<a class=""
href="https://blog.notmyidea.org/lectures/">Lectures</a>
</li>
<li>
<a class=""
href="https://blog.notmyidea.org/projets.html">Projets</a>
</li>
</ul>
</section>
<header>
<h1 class="post-title">Service de nuages : Garantir l&#8217;intégrité des données via des&nbsp;signatures</h1>
<time datetime="2016-03-01T00:00:00+01:00">01 mars 2016</time>
</header>
<article>
<p><em>Cet article est repris depuis le blog « Service de Nuages » de mon équipe à&nbsp;Mozilla</em></p>
<p>Dans le cadre du projet <a class="reference external" href="https://wiki.mozilla.org/Firefox/Go_Faster">Go Faster</a>, nous souhaitons distribuer des
mises à jour de parties de <em>Firefox</em> de manière séparée des mises à jour majeures
(qui ont lieu toutes les 6&nbsp;semaines).</p>
<p>Les données que nous souhaitons mettre à jour sur les clients sont multiples.
Entre autres, nous souhaitons gérer <a class="reference external" href="https://blog.mozilla.org/security/2015/03/03/revoking-intermediate-certificates-introducing-onecrl/">la mise à jour des listes de révocation
(<span class="caps">CRL</span>) de certificats <span class="caps">SSL</span></a>.</p>
<p>Il est évidemment nécessaire de s&#8217;assurer que les données qui sont téléchargées
sur les client sont légitimes : que personne ne tente d&#8217;invalider des
certificats alors qu&#8217;ils sont valides, et que l&#8217;ensemble des mises à jour sont
bel et bien récupérées sur le&nbsp;client.</p>
<p>La signature garantit qu&#8217;une mise à jour contient tous les enregistrements, mais il
est toujours possible de bloquer l&#8217;accès au service (par exemple avec le <em>china
great firewall</em>).</p>
<p>Ce mécanisme fonctionne pour les listes de certificats à révoquer, mais pas
uniquement. Nous comptons réutiliser ce même fonctionnement dans le futur pour
la mise à jour d&#8217;autres parties de Firefox, et vous pouvez également en tirer
parti pour d&#8217;autres cas&nbsp;d&#8217;utilisation.</p>
<p>Nous souhaitons utiliser <a class="reference external" href="https://kinto.readthedocs.org">Kinto</a> afin
de distribuer ces jeux de données. Un des avantages est que l&#8217;on peut
facilement <em>cacher</em> les collections derrière un <span class="caps">CDN</span>.</p>
<p>Par contre, nous ne souhaitons pas que les clients fassent
confiance aveuglément, ni au serveur Kinto, ni au <span class="caps">CDN</span>.</p>
<p>Effectivement, un attaquant, contrôlant l&#8217;un ou l&#8217;autre, pourrait
alors envoyer les mises à jour qu&#8217;il souhaite à l&#8217;ensemble des clients
ou supprimer des certificats révoqués. Imaginez le carnage&nbsp;!</p>
<p>Afin de résoudre ce problème, considérons les conditions&nbsp;suivantes:</p>
<ul class="simple">
<li>La personne qui a le pouvoir de mettre à jour les <span class="caps">CRL</span> (<em>l&#8217;updater</em>)
a accès à une cle de signature (ou mieux, <a class="reference external" href="https://fr.wikipedia.org/wiki/Hardware_Security_Module">un <span class="caps">HSM</span></a>) qui lui permet de
signer la&nbsp;collection;</li>
<li>Le pendant public de ce certificat est stocké et distribué dans&nbsp;Firefox;</li>
<li>Le <em>hashing</em> et la <em>signature</em> sont faits côté client pour éviter certains
vecteurs d&#8217;attaque (si un attaquant a la main sur le serveur Kinto par&nbsp;exemple).</li>
</ul>
<p>Le chiffrement à sens unique, aussi appellé <em>hashing</em> est un moyen de toujours
obtenir le même résultat à partir de la même&nbsp;entrée.</p>
<div class="section" id="premier-envoi-de-donnees-sur-kinto">
<h2>Premier envoi de données sur&nbsp;Kinto</h2>
<p>L&#8217;ensemble des données est récupéré depuis une source <em>sécurisée</em> puis mis dans
une collection <span class="caps">JSON</span>. Chaque élément contient un identifiant unique généré sur
le&nbsp;client.</p>
<p>Par exemple, un enregistrement peut ressembler à&nbsp;:</p>
<div class="highlight"><pre><span></span><span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="s2">&quot;fingerprint&quot;</span><span class="o">:</span><span class="w"> </span><span class="s2">&quot;11:D5:D2:0A:9A:F8:D9:FC:23:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3&quot;</span><span class="p">}</span>
</pre></div>
<p>Le <em>hash</em> de la collection est ensuite calculé, signé puis envoyé au serveur
(voir plus bas pour les&nbsp;détails).</p>
<p>La signature est déportée sur un service qui ne s&#8217;occupe que de ça, puisque la
sécurité du certificat qui s&#8217;occupe des signatures est extrêmement&nbsp;importante.</p>
</div>
<div class="section" id="comment-verifier-l-integrite-des-donnees">
<h2>Comment vérifier l&#8217;intégrité des données&nbsp;?</h2>
<p>Premièrement, il faut récupérer l&#8217;ensemble des enregistrements présents sur
le serveur, ainsi que le <em>hash</em> et la signature&nbsp;associée.</p>
<p>Ensuite, vérifier la signature du <em>hash</em>, pour s&#8217;assurer que celui-ci provient
bien d&#8217;un tiers de&nbsp;confiance.</p>
<p>Finalement, recalculer le <em>hash</em> localement et valider qu&#8217;il correspond bien à
celui qui a été&nbsp;signé.</p>
</div>
<div class="section" id="ajouter-de-nouvelles-donnees">
<h2>Ajouter de nouvelles&nbsp;données</h2>
<p>Pour l&#8217;ajout de nouvelles données, il est nécessaire de s&#8217;assurer que les
données que l&#8217;on a localement sont valides avant de faire quoi que ce soit&nbsp;d&#8217;autre.</p>
<p>Une fois ces données validées, il suffit de procéder comme la première fois, et
d&#8217;envoyer à nouveau le <em>hash</em> de la collection au&nbsp;serveur.</p>
</div>
<div class="section" id="comment-calculer-ce-hash">
<h2>Comment calculer ce hash&nbsp;?</h2>
<p>Pour calculer le <em>hash</em> de la collection, il est nécessaire&nbsp;:</p>
<ol class="arabic simple">
<li>D&#8217;ordonner l&#8217;ensemble des éléments de la collection (par leur id)&nbsp;;</li>
<li>Pour chaque élément, sérialiser les champs qui nous intéressent (les
concaténer clé +&nbsp;valeur)</li>
<li>Calculer le <em>hash</em> depuis la&nbsp;sérialisation.</li>
</ol>
<p>Nous sommes encore incertains de la manière dont le hash va être calculé. Les <a class="reference external" href="https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41"><span class="caps">JSON</span> Web Signature</a> semblent
une piste intéressante. En attendant, une implementation naïve en python
pourrait ressembler à ceci&nbsp;:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">hashlib</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;b7dded96-8df0-8af8-449a-8bc47f71b4c4&quot;</span><span class="p">,</span>
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="s2">&quot;11:D5:D2:0A:9A:F8:D9:FC:23:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3&quot;</span><span class="p">},</span>
<span class="p">{</span><span class="s2">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;dded96b7-8f0d-8f8a-49a4-7f771b4c4bc4&quot;</span><span class="p">,</span>
<span class="s2">&quot;fingerprint&quot;</span><span class="p">:</span> <span class="s2">&quot;33:6E:5C:5C:30:EC:AF:68:F5:68:FB:A3:11:D5:D2:0A:9A:F8:D9:FC&quot;</span><span class="p">}]</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">()</span>
<span class="n">m</span><span class="o">.</span><span class="n">update</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">sort_keys</span><span class="o">=</span><span class="kc">True</span><span class="p">))</span>
<span class="n">collection_hash</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
</pre></div>
</div>
</article>
<footer>
<a id="feed" href="/feeds/all.atom.xml">
<img alt="RSS Logo" src="/theme/rss.svg" />
</a>
</footer>
</div>
</body>
</html>