wcs/help/fr/api-auth.page

226 lines
6.5 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<page xmlns="http://projectmallard.org/1.0/"
type="topic" id="api-auth" xml:lang="fr">
<info>
<link type="guide" xref="index#api" />
<revision docversion="0.1" date="2013-01-04" status="draft"/>
<revision docversion="0.2" date="2015-12-18" status="draft"/>
<credit type="author">
<name>Frédéric Péters</name>
<email>fpeters@entrouvert.com</email>
</credit>
<desc>Clé d'utilisation, utilisateurs, sessions, signatures, etc.</desc>
</info>
<title>Authentification</title>
<section>
<title>Usager concerné</title>
<p>
Pour les appels concernant un usager particulier (tel que la récupération de la
liste de ses formulaires en cours), l'usager est précisé en ajoutant une query
string avec un paramètre <code>email</code> (pour trouver l'usager selon son
adresse électronique) ou un paramètre <code>NameID</code> (pour trouver
l'usager selon son NameID SAML).
</p>
</section>
<section id="req-security-shared-secret">
<title>Signature des requêtes</title>
<p>
Les appels aux API doivent être signés, cela passe par une clé partagée à
configurer des deux cotés de la liaison, la signature est du type HMAC;
l'algorithme de hash à employer est passé en paramètre.
</p>
<note><p>En ce qui concerne l'algorithme de hash, il est préconisé d'utiliser
SHA-256 par respect du <link
href="http://references.modernisation.gouv.fr/securite">Référentiel Général
de Sécurité (RGS)</link>.</p></note>
<p>
La signature est à calculer sur la query string encodée complète, en
enlevant les paramètres terminaux <code>algo</code>, <code>timestamp</code>,
<code>orig</code> et <code>signature</code>. La formule de calcul de la
signature est la suivante :
</p>
<code>
BASE64(HMAC-HASH(query_string+'algo=HASH&amp;timestamp=' + timestamp + '&amp;orig=" + orig, clé))
</code>
<list>
<item><p><code>timestamp</code> est la date dans la zone GMT au format ISO8601
en se limitant à la précision des secondes (ex : 2012-04-04T12:34:00Z),
</p></item>
<item><p><code>orig</code> est une chaîne précisant l'émetteur de la
requête,</p></item>
<item><p>algo est une chaîne représentant l'algorithme de hachage utilisé, sont
définis : sha1, sha256, sha512 pour les trois algorithmes correspondant.
L'utilisation d'une valeur différente n'est pas définie.</p></item>
</list>
<p>
La query string définitive est ainsi :
</p>
<code>
<var>qs_initial</var>&amp;algo=<var>algo</var>&amp;timestamp=<var>ts</var>&amp;orig=<var>orig</var>&amp;signature=<var>signature</var>
</code>
</section>
<section>
<title>Configuration des clés partagées</title>
<p>
Les clés partagées doivent être définies dans le fichier
<code>site-options.cfg</code>, dans une section <code>[api-secrets]</code>, par
exemple :
</p>
<code>
[api-secrets]
intranet = 12345
</code>
</section>
<section>
<title>Exemples de code de signature</title>
<p>
Voici des exemples de code pour créer des URLs signées selon l'algorithme
expliqué ci-dessus.
</p>
<listing>
<title>Python</title>
<code mime="text/x-python">
#!/usr/bin/env python2
import base64
import hmac
import hashlib
import datetime
import urllib
import urlparse
import random
def sign_url(url, key, algo='sha256', orig=None, timestamp=None, nonce=None):
parsed = urlparse.urlparse(url)
new_query = sign_query(parsed.query, key, algo, orig, timestamp, nonce)
return urlparse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
def sign_query(query, key, algo='sha256', orig=None, timestamp=None, nonce=None):
if timestamp is None:
timestamp = datetime.datetime.utcnow()
timestamp = timestamp.strftime('%Y-%m-%dT%H:%M:%SZ')
if nonce is None:
nonce = hex(random.getrandbits(128))[2:].rstrip('L')
new_query = query
if new_query:
new_query += '&amp;'
new_query += urllib.urlencode((
('algo', algo),
('timestamp', timestamp),
('nonce', nonce)))
if orig is not None:
new_query += '&amp;' + urllib.urlencode({'orig': orig})
signature = base64.b64encode(sign_string(new_query, key, algo=algo))
new_query += '&amp;' + urllib.urlencode({'signature':signature})
return new_query
def sign_string(s, key, algo='sha256', timedelta=30):
digestmod = getattr(hashlib, algo)
hash = hmac.HMAC(key, digestmod=digestmod, msg=s)
return hash.digest()
# usage:
url = sign_url('http://www.example.net/uri/?arg=val&amp;arg2=val2', 'user-key', orig='user')
</code>
</listing>
<listing>
<title>PHP</title>
<code mime="application/x-php">
&lt;?php
function sign_url(string $url, string $orig, string $key) {
$parsed_url = parse_url($url);
$timestamp = gmstrftime("%Y-%m-%dT%H:%M:%SZ");
$nonce = bin2hex(random_bytes(16));
$new_query = '';
if (isset($parsed_url['query'])) {
$new_query .= $parsed_url['query'] . '&amp;';
}
$new_query .= http_build_query(array(
'algo' => 'sha256',
'timestamp' => $timestamp,
'nonce' => $nonce,
'orig' => $orig));
$signature = base64_encode(hash_hmac('sha256', $new_query, $key, $raw_output = true));
$new_query .= '&amp;' . http_build_query(array('signature' => $signature));
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path?$new_query$fragment";
}
# usage:
url = sign_url("http://www.example.net/uri/?arg=val&amp;arg2=val2", "user", "user-key");
?&gt;
</code>
</listing>
<listing>
<title>Shell (bash)</title>
<code mime="application/x-shellscript">
#!/bin/bash
url="http://www.example.net/uri/?arg=val&amp;arg2=val2"
orig="user"
key="user-key"
function rawurlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o
for ((pos=0; pos&lt;strlen; pos++)); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done
echo "${encoded}"
}
now=$(date -u +%FT%TZ);
qs="algo=sha256&amp;timestamp=$now&amp;orig=$orig"
sig=$(rawurlencode $(echo -n "$qs" | openssl dgst -binary -sha256 -hmac "$key" | base64))
signed="${url}?$qs&amp;signature=$sig"
echo "$signed"
</code>
</listing>
</section>
</page>