|
| 1 | +# Effacement sécurisé |
| 2 | + |
| 3 | +L'effacement sécurisé (mise à zéro) est nécessaire pour les variables sensibles, |
| 4 | +en particulier dans lorsque le code Rust est utilisé *via* des FFI. |
| 5 | + |
| 6 | +> **Règle {{#check MEM-ZERO | Mise à zéro des données sensibles après utilisation}}** |
| 7 | +> |
| 8 | +> Les variables contenant des données sensibles doivent être mises à zéro après |
| 9 | +> utilisation. |
| 10 | +
|
| 11 | +<!-- justification ? --> |
| 12 | + |
| 13 | +<div class=warning> |
| 14 | + |
| 15 | +Les opérations d'effacement de la mémoire son compliquées à réaliser en général, et |
| 16 | +avec Rust en particulier. En effet, comme en C, la sécurité de la mémoire n'est pas |
| 17 | +un comportement *observable*, c'est à dire dont le compilateur se porte garant. |
| 18 | + |
| 19 | +Du fait des nombreuses optimisations[^note] du compilateur, |
| 20 | +les recommendations suivantes ne garantissent donc pas l'effacement complet des secrets |
| 21 | +après leur utilisation, mais sont données en *best-effort*. |
| 22 | + |
| 23 | +</div> |
| 24 | + |
| 25 | +[^note]: ... et éventuellement de celles futures ! |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | +## Forcer l'effacement |
| 30 | + |
| 31 | +Une première difficulté naît des optimisations du compilateur : en général, il considère inutiles des opérations |
| 32 | +d'écriture en mémoire sans lecture subséquente et peut les supprimer. |
| 33 | + |
| 34 | +Par exemple, dans le code suivant, l'écriture en mémoire suivante n'est pas effectuée |
| 35 | + |
| 36 | +```rust |
| 37 | +pub fn main() { |
| 38 | + let mut n: u64 = 0x4953534e41; |
| 39 | + println!("{}", n); |
| 40 | + n = 0; // optimized away! |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +Il existe dans la bibliothèque standard Rust des fonctions `unsafe` forçant l'écriture en mémoire malgré les optimisations |
| 45 | +du compilateur. Par exemple, la fonction `std::ptr::write_volatile` ne sera jamais supprimée |
| 46 | +par le compilateur. Par exemple, la fonction suivante appliquera toujours l'effacement de la mémoire d'un entier. |
| 47 | + |
| 48 | +```rust |
| 49 | +fn erase(mut n: u8) { |
| 50 | + println!("zeroing memory"); |
| 51 | + unsafe { ::std::ptr::write_volatile(&mut n, 0) }; |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +Cependant, cette fonction étant `unsafe`, on lui préférera si possible la crate `zeroize`. |
| 56 | + |
| 57 | +> **Règle {{#check MEM-ZERO-EFFECTIVE | Effectivité de l'effacement de la mémoire}}** |
| 58 | +> |
| 59 | +> Les développements concernant la mise à zéro de variables sensibles |
| 60 | +> s'assureront de l'exécution effective de l'opération. |
| 61 | +
|
| 62 | + <!-- ## Éviter les duplications de secrets |
| 63 | +
|
| 64 | +Plus le nombre de secrets est grand plus il est difficile de s'assurer de leur non divulgation. |
| 65 | +Et même en supposant que leur effacement est automatisé (en utilisant `Drop`, ce qui, |
| 66 | +on le verra dans la suite, ne garantie par un effacement systématique), la multiplication |
| 67 | +des emplacements augmente le risque de divulgation --> |
| 68 | + |
| 69 | +<!-- je n'en suis pas si sûr finalement --> |
| 70 | + |
| 71 | + |
| 72 | +## Déplacements de valeurs sensibles |
| 73 | + |
| 74 | +La [sémantique par déplacement](semantics.md#déplacement-des-valeurs) de Rust induit une contrainte |
| 75 | +supplémentaire dans le contrôle des variables sensibles. Le déplacement est une simple copie |
| 76 | +bit à bit d'un emplacement à un autre, sans action paramétrable par l'utilisateur (excluant par exemple |
| 77 | +d'éventuel recours à `drop`). |
| 78 | + |
| 79 | +De plus, le déplacement étant un détail d'implémentation du compilateur il est difficile de |
| 80 | +savoir à quel moment il interviendra. |
| 81 | + |
| 82 | +> **Règle {{#check MEM-ZERO-NOMOVE | Absence de déplacements de valeurs sensibles}}** |
| 83 | +> |
| 84 | +> Les valeurs sensibles ne devront pas être déplacées après leur création. |
| 85 | +
|
| 86 | +Il s'en suit la recommendation suivante découlant directement de la précédente |
| 87 | + |
| 88 | +> **Règle {{#check MEM-ZERO-BYREF | Absence de transfert de propriété d'une valeur sensible}}** |
| 89 | +> |
| 90 | +> La propriété d'un secret n'est pas transferable. Une fois le secret créé (même vide), les seules façons |
| 91 | +> permettant d'accéder à un secret sont |
| 92 | +> |
| 93 | +> * la copie (trait `Copy`) ou le clonage (trait `Clone`) uniquement si le type du secret implémente `Drop` dans laquelle le secret est effacé, |
| 94 | +> * la référence (`&T` ou `&mut T`) |
| 95 | +
|
| 96 | +<div class="warning"> |
| 97 | + |
| 98 | +Il est à noter que ces précautions ne garantissent pas le bonne effacement d'un secret : en effet, |
| 99 | +il est possible dans certaines situations que le compilateur ajoute des optimisations cassant les invariants |
| 100 | +mis en place pour empêcher le déplacement d'un secret. |
| 101 | + |
| 102 | +Par exemple, il peut choisir de transformer un passage par référence en passage par valeur, ce |
| 103 | +qui met à mal la précaution [MEM-ZERO-BYREF](#MEM-ZERO-BYREF). |
| 104 | + |
| 105 | +</div> |
| 106 | + |
| 107 | +<!-- ### Fixer les valeurs sensibles en mémoire |
| 108 | +
|
| 109 | +Afin de se prémunir d'un déplacement involontaire, on utilisera le système de types pour vérifier à la |
| 110 | +compilation qu'une valeur est *fixée* en mémoire. |
| 111 | +
|
| 112 | +> **Règle {{#check MEM-ZERO-NOMOVE-TYPED | Épinglage des valeurs sensibles}}** |
| 113 | +> |
| 114 | +> Les fonctions manipulant les secrets devront, dans la mesure du possible, prendre en paramètre une version *épinglée* |
| 115 | +> du secret, s'assurant ainsi que le secret ne pourra être déplacé en mémoire. |
| 116 | +> |
| 117 | +> Par exemple, si `T` est le type d'un secret, on privilégiera la fonction |
| 118 | +> |
| 119 | +> ```rust |
| 120 | +> fn plop() |
| 121 | +> ``` --> |
| 122 | + |
| 123 | +> **Règle {{#check MEM-ZERO-WRAPPED-INIT | Création de valeurs sensible dans un environnement sécurisé}}** |
| 124 | +> |
| 125 | +> Les valeurs sensibles seront initialisées dans un environnement s'assurant que ces valeurs seront bien |
| 126 | +> effacées après leur usage. |
| 127 | +
|
| 128 | +L'initialisation suivante est **mauvaise** car la valeur du secret est d'abord créée dans un environnement où l'effacement n'est pas assuré |
| 129 | + |
| 130 | +```rust |
| 131 | +struct Secret {...} |
| 132 | + |
| 133 | +impl Secret { |
| 134 | + /// This initialisation function is no secure since the content |
| 135 | + /// of the secret is first generated outside the secure wrapper [`Secret`] |
| 136 | + fn new(content : [u8; 16]) |
| 137 | +} |
| 138 | + |
| 139 | +impl Drop for Secret { |
| 140 | + fn drop(&self) { |
| 141 | + // securely erase the content of a [`Secret`] |
| 142 | + ... |
| 143 | + } |
| 144 | +} |
| 145 | +``` |
| 146 | +<!-- |
| 147 | +## Exemple d'implementation d'un type de secret |
| 148 | +
|
| 149 | +On pourra s'inspirer de la librairie [secrust](https://github.com/hg-anssi/secrust) pour |
| 150 | +gérer ses secrets en suivant les recommendations de ce guide. --> |
0 commit comments