# Sécuriser une extension qui stocke des identifiants Algolia et Stripe

> wp_options n'est pas un coffre-fort. Voici comment chiffrer une clé API Algolia ou une clé secrète Stripe avant de l'y stocker.

- Auteur : WordPress Développement
- Publié le : 2023-06-13
- Mis à jour le : 2023-06-13
- Catégorie : Sécurité
- URL : https://www.wpmoderne.fr/securite/securiser-extension-identifiants-algolia-stripe/

## L’essentiel

- wp_options reste lisible par quiconque a accès à la base de données
- sodium_crypto_secretbox chiffre une valeur avant son stockage en option
- La clé de chiffrement ne doit jamais résider dans la même base que la donnée chiffrée

Une seule requête SQL, `SELECT option_value FROM wp_options WHERE option_name LIKE '%api_key%'`, suffit à récupérer l'ensemble des clés API stockées en clair par une extension qui utilise l'API d'options de WordPress sans précaution particulière. C'est exactement ce que révèle l'inspection d'une extension maison développée pour connecter un site à Algolia pour la recherche et à Stripe pour les paiements : les deux clés secrètes, l'une et l'autre capables de causer des dégâts significatifs en cas de fuite, dormaient en clair dans `wp_options`.

Ce constat n'a rien d'isolé : l'API d'options est conçue pour la simplicité de stockage de réglages, pas pour la confidentialité de secrets. Toute personne disposant d'un accès à la base de données, y compris via une extension tierce compromise capable d'exécuter des requêtes SQL, ou via une sauvegarde de base mal protégée, peut lire ces valeurs sans effort. Voici le correctif retenu, avec un chiffrement basé sur l'extension `sodium`, disponible nativement en PHP depuis la version 7.2.

## Le problème du stockage en clair

L'extension enregistrait les clés API de cette façon, au moment de la configuration initiale par l'administrateur du site :

```
update_option( 'mon_extension_algolia_api_key', $cle_algolia );
update_option( 'mon_extension_stripe_secret_key', $cle_stripe );
```

Ces deux appels stockent la valeur telle quelle dans la table `wp_options`, sans aucune transformation. N'importe quel accès direct à la base de données, qu'il provienne d'un export de sauvegarde mal sécurisé, d'un accès hébergeur compromis ou d'une injection SQL ailleurs sur le site, expose immédiatement ces deux clés en clair.

## Chiffrer avant le stockage avec sodium_crypto_secretbox

> L'essentiel à retenir : wp_options reste lisible par quiconque a accès à la base de données ; sodium_crypto_secretbox chiffre une valeur avant son stockage en option ; La clé de chiffrement ne doit jamais résider dans la même base que la donnée chiffrée

La bibliothèque `sodium` intégrée à PHP permet de chiffrer une valeur avec une clé symétrique avant de la confier à `update_option()`, et de la déchiffrer uniquement au moment de l'utiliser :

```
function chiffrer_secret( $valeur ) {
    $nonce  = random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
    $chiffre = sodium_crypto_secretbox( $valeur, $nonce, CLE_CHIFFREMENT_EXTENSION );
    return base64_encode( $nonce . $chiffre );
}

function dechiffrer_secret( $valeur_stockee ) {
    $donnees = base64_decode( $valeur_stockee );
    $nonce   = mb_substr( $donnees, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit' );
    $chiffre = mb_substr( $donnees, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit' );
    return sodium_crypto_secretbox_open( $chiffre, $nonce, CLE_CHIFFREMENT_EXTENSION );
}

update_option( 'mon_extension_algolia_api_key', chiffrer_secret( $cle_algolia ) );
$cle_algolia_utilisable = dechiffrer_secret( get_option( 'mon_extension_algolia_api_key' ) );
```

Chaque chiffrement génère un nonce aléatoire différent, stocké aux côtés du contenu chiffré, garantissant qu'une même clé API chiffrée deux fois ne produit jamais deux fois la même valeur stockée. Sans la clé de chiffrement `CLE_CHIFFREMENT_EXTENSION`, le contenu de `wp_options` ne révèle strictement rien d'exploitable.

### Où stocker la clé de chiffrement elle-même

Ce point est le plus souvent négligé : chiffrer une valeur ne sert à rien si la clé de chiffrement se trouve dans la même base de données que la valeur chiffrée. La clé `CLE_CHIFFREMENT_EXTENSION` doit être définie dans le fichier `wp-config.php`, en dehors de la base de données, au même titre que les clés et sels d'authentification de WordPress :

```
define( 'CLE_CHIFFREMENT_EXTENSION', 'valeur-generee-aleatoirement-32-octets' );
```

Une fuite de la base de données seule, sans accès au système de fichiers du serveur, ne permet alors plus de déchiffrer les clés API stockées.

## Ce que ce chiffrement ne remplace pas

- Il ne protège pas contre une compromission complète du serveur, qui donnerait accès à la fois à la base et à `wp-config.php`.
- Il ne dispense pas de restreindre l'accès en écriture à la page de réglages de l'extension aux seuls administrateurs, via `current_user_can( 'manage_options' )`.
- Il ne remplace pas la rotation périodique des clés API elles-mêmes du côté d'Algolia et de Stripe, indépendamment de leur mode de stockage local.

## Vérifier que rien ne fuite ailleurs

Le chiffrement de `wp_options` ne suffit pas si la clé API réapparaît en clair ailleurs dans l'exécution du plugin. L'audit complémentaire a porté sur trois points : les journaux d'erreurs PHP (aucun `error_log()` ne devait afficher la clé déchiffrée), les appels JavaScript côté front-end (aucune clé secrète ne doit jamais transiter vers le navigateur, contrairement à une clé publique Algolia dédiée à la recherche), et les exports de configuration de l'extension proposés aux administrateurs, qui masquaient bien la valeur avant affichage.

> Chiffrer une clé API sans protéger la clé de chiffrement elle-même revient à mettre un cadenas sur une porte tout en laissant la clé sur le montant.

## En résumé

L'API d'options de WordPress reste parfaitement adaptée aux réglages non sensibles, mais toute extension qui stocke un secret capable de causer un dommage réel en cas de fuite — clé de paiement, clé de recherche avec droits d'écriture, jeton d'accès à un service tiers — mérite un chiffrement systématique avant écriture, avec une clé de chiffrement gardée soigneusement hors de portée de la base de données elle-même.
