GNU/Linux >> Tutoriels Linux >  >> Linux

L'utilisation de /dev/urandom est-elle toujours un bon conseil à l'ère des conteneurs et de l'isolement ?

J'ai écrit une réponse qui décrit en détail comment getrandom() blocs en attente d'entropie initiale.

Cependant, je pense qu'il surestime légèrement urandom en disant que "le seul instant où /dev/urandom pourrait impliquer un problème de sécurité dû à une faible entropie est lors des premiers instants d'une nouvelle installation automatisée du système d'exploitation".

Vos inquiétudes sont fondées. J'ai une question ouverte à ce sujet et ses implications. Le problème est que la graine aléatoire persistante prend un certain temps pour passer du pool d'entrée au pool de sortie (le pool de blocage et le CRNG). Ce problème signifie que /dev/urandom affichera des valeurs potentiellement prévisibles pendant quelques minutes après le démarrage. La solution est, comme vous le dites, d'utiliser soit le blocage /dev/random , ou d'utiliser getrandom() défini sur bloquer.

En fait, il n'est pas rare de voir des lignes comme celle-ci dans le journal du noyau au début du démarrage :

random: sn: uninitialized urandom read (4 bytes read, 7 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 15 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 20 bits of entropy available)

Tous ces cas sont des cas où le pool non bloquant a été accédé avant même que suffisamment d'entropie n'ait été collectée. Le problème est que la quantité d'entropie est tout simplement trop faible pour être suffisamment sécurisée cryptographiquement à ce stade. Il devrait y avoir 2 valeurs possibles de 4 octets, mais avec seulement 7 bits d'entropie disponibles, cela signifie qu'il n'y a que 2 ou 128 possibilités différentes.

Halderman semble également dire que le pool d'entropie se remplit à chaque démarrage, et non, comme le dit Pornin dans sa réponse, lors de la toute première installation du système d'exploitation. Bien que ce ne soit pas très important pour mon application, je me demande :lequel est-ce ?

C'est en fait une question de sémantique. Le pool d'entropie réel (la page de mémoire conservée dans le noyau qui contient des valeurs aléatoires) est remplie à chaque démarrage par la graine d'entropie persistante et par le bruit environnemental. Cependant, la graine d'entropie lui-même est un fichier créé au moment de l'installation et mis à jour avec de nouvelles valeurs aléatoires à chaque arrêt du système. J'imagine que Pornin considère que la graine aléatoire fait partie du pool d'entropie (comme dans, une partie du système général de distribution et de collecte d'entropie), alors que Halderman le considère comme séparé (parce que le pool d'entropie est techniquement une page de mémoire, rien de plus). La vérité est que la graine d'entropie est introduite dans le pool d'entropie à chaque démarrage, mais cela peut prendre quelques minutes pour affecter réellement le pool.

Un résumé des trois sources d'aléatoire :

  1. /dev/random - Le dispositif de caractère bloquant décrémente un "compte d'entropie" chaque fois qu'il est lu (bien que l'entropie ne soit pas réellement épuisée). Cependant, il bloque également jusqu'à ce qu'une entropie suffisante ait été collectée au démarrage, ce qui le rend sûr à utiliser dès le début.

  2. /dev/urandom - Le périphérique de caractères non bloquant produira des données aléatoires chaque fois que quelqu'un lira à partir de celui-ci. Une fois qu'une entropie suffisante a été collectée, il produira un flux pratiquement illimité impossible à distinguer des données aléatoires. Malheureusement, pour des raisons de compatibilité, il est lisible même au début du démarrage avant que suffisamment d'entropie ponctuelle n'ait été collectée.

  3. getrandom() - Un appel système qui produira des données aléatoires tant que le pool d'entropie a été correctement initialisé avec la quantité minimale d'entropie requise. Il lit par défaut à partir du pool non bloquant. Si donné le GRND_NONBLOCK flag, il renverra une erreur s'il n'y a pas assez d'entropie. Si donné le GRND_RANDOM flag, il se comportera de la même manière que /dev/random , bloquant simplement jusqu'à ce qu'il y ait de l'entropie disponible.

Je vous suggère d'utiliser la troisième option, le getrandom() appel système. Cela permettra à un processus de lire à haute vitesse des données aléatoires sécurisées par chiffrement et ne se bloquera qu'au début du démarrage lorsqu'une entropie insuffisante a été collectée. Si le os.urandom() de Python fonction agit comme un wrapper à cet appel système comme vous le dites, alors ça devrait être bien à utiliser. Il semble qu'il y ait eu beaucoup de discussions sur la question de savoir si cela devrait être le cas ou non, ce qui finit par bloquer jusqu'à ce que suffisamment d'entropie soit disponible.

En pensant un peu plus loin sur la route :quelles sont les meilleures pratiques pour les environnements aussi frais et naïfs que ceux que j'ai décrits ci-dessus, mais qui fonctionnent sur des appareils aux perspectives assez abyssales pour la génération d'entropie initiale ?

Il s'agit d'une situation courante, et il existe plusieurs façons d'y faire face :

  • Assurez-vous de bloquer au début du démarrage, par exemple en utilisant /dev/random ou getrandom() .

  • Conservez une graine aléatoire persistante, si possible (c'est-à-dire si vous pouvez écrire dans le stockage à chaque démarrage).

  • Plus important encore, utilisez un RNG matériel . C'est la mesure la plus efficace #1.

L'utilisation d'un générateur de nombres aléatoires matériel est très importante. Le noyau Linux initialisera son pool d'entropie avec n'importe quelle interface HWRNG prise en charge s'il en existe une, éliminant complètement le trou d'entropie de démarrage. De nombreux appareils embarqués ont leurs propres générateurs aléatoires.

Ceci est particulièrement important pour de nombreux périphériques embarqués, car ils peuvent ne pas disposer d'une minuterie haute résolution requise pour que le noyau génère en toute sécurité de l'entropie à partir du bruit environnemental. Certaines versions de processeurs MIPS, par exemple, n'ont pas de compteur de cycles.

Comment et pourquoi suggérez-vous d'utiliser urandom pour ensemencer un (je suppose que l'espace utilisateur?) CSPRNG? Comment cela bat-il getrandom ?

Le dispositif aléatoire non bloquant n'est pas conçu pour des performances élevées. Jusqu'à récemment, l'appareil était incroyablement lent en raison de l'utilisation de SHA-1 pour le caractère aléatoire plutôt que d'un chiffrement de flux comme il le fait maintenant. L'utilisation d'une interface noyau pour le caractère aléatoire peut être moins efficace qu'un CSPRNG local en espace utilisateur, car chaque appel au noyau nécessite un changement de contexte coûteux. Le noyau a été conçu pour tenir compte des applications qui souhaitent en tirer une grande partie, mais les commentaires dans le code source indiquent clairement qu'ils ne considèrent pas cela comme la bonne chose à faire :

/*
 * Hack to deal with crazy userspace progams when they are all trying
 * to access /dev/urandom in parallel.  The programs are almost
 * certainly doing something terribly wrong, but we'll work around
 * their brain damage.
 */

Les bibliothèques de chiffrement populaires telles que OpenSSL prennent en charge la génération de données aléatoires. Ils peuvent être amorcés une fois ou réamorcés occasionnellement, et peuvent bénéficier davantage de la parallélisation. Il permet en outre d'écrire du code portable qui ne dépend pas du comportement d'un système d'exploitation ou d'une version de système d'exploitation particulier.

Si vous n'avez pas besoin d'énormes quantités d'aléatoire, il est tout à fait acceptable d'utiliser l'interface du noyau. Si vous développez une application de chiffrement qui aura besoin de beaucoup d'aléatoire tout au long de sa vie, vous pouvez utiliser une bibliothèque comme OpenSSL pour gérer cela pour vous.


Il existe trois états dans lesquels le système peut se trouver :

  1. N'a pas collecté suffisamment d'entropie pour initialiser en toute sécurité un CPRNG.
  2. A collecté suffisamment d'entropie pour initialiser en toute sécurité un CPRNG, et :

    2a. A donné plus d'entropie qu'il n'en a collecté.

    2b. A donné moins d'entropie qu'il n'en a collecté.

Historiquement, les gens pensaient que la distinction entre (2a) et (2b) était importante. Cela a causé deux problèmes. Premièrement, c'est faux - la distinction n'a pas de sens pour un CPRNG bien conçu. Et deuxièmement, l'accent mis sur la distinction (2a)-vs-(2b) a fait que les gens ont manqué la distinction entre (1) et (2), qui est en fait très importante. Les gens se sont en quelque sorte effondrés (1) pour devenir un cas particulier de (2a).

Ce que vous voulez vraiment, c'est quelque chose qui bloque dans l'état (1) et ne bloque pas dans les états (2a) ou (2b).

Malheureusement, autrefois, la confusion entre (1) et (2a) signifiait que ce n'était pas une option. Vos deux seules options étaient /dev/random , qui bloquait dans les cas (1) et (2a), et /dev/urandom , qui n'a jamais bloqué. Mais l'état (1) ne se produit presque jamais - et ne se produit pas du tout dans les systèmes bien configurés, voir ci-dessous - alors /dev/urandom est meilleur pour presque tous les systèmes, presque tout le temps. C'est de là que viennent tous ces articles de blog sur "toujours utiliser urandom" - ils essayaient de convaincre les gens d'arrêter de faire une distinction dénuée de sens et nuisible entre les états (2a) et (2b).

Mais, oui, ni l'un ni l'autre n'est ce que vous voulez réellement. Ainsi, le nouveau getrandom syscall, qui se bloque par défaut dans l'état (1) et ne se bloque pas dans les états (2a) ou (2b). Ainsi, sur Linux moderne, l'orthodoxie doit être mise à jour pour :toujours utiliser getrandom avec les paramètres par défaut .

Rides supplémentaires :

  • getrandom prend également en charge un mode non par défaut où il agit comme /dev/random , qui peut être demandé via le GRND_RANDOM drapeau. AFAIK, ce drapeau n'est jamais réellement utile, pour les mêmes raisons que celles décrites dans les anciens articles de blog. Ne l'utilisez pas.

  • getrandom a également des avantages bonus supplémentaires sur /dev/urandom :cela fonctionne quelle que soit la disposition de votre système de fichiers et ne nécessite pas l'ouverture d'un descripteur de fichier, ce qui est problématique pour les bibliothèques génériques qui souhaitent faire des hypothèses minimales sur l'environnement dans lequel elles seront utilisées. Cela n'affecte pas la sécurité cryptographique , mais c'est bien sur le plan opérationnel.

  • Un système bien configuré aura toujours de l'entropie disponible, même au début du démarrage (c'est-à-dire que vous ne devriez vraiment jamais entrer dans l'état (1), jamais). Il existe de nombreuses façons de gérer cela :économiser de l'entropie du démarrage précédent pour l'utiliser lors du suivant. Installez un RNG matériel. Les conteneurs Docker utilisent le noyau de l'hôte et ont ainsi accès à son pool d'entropie. Les configurations de virtualisation de haute qualité ont des moyens de laisser le système invité récupérer l'entropie du système hôte via des interfaces d'hyperviseur (par exemple, rechercher "virtio rng"). Mais bien sûr, tous les systèmes ne sont pas bien configurés. Si vous avez un système mal configuré, vous devriez voir si vous pouvez le configurer correctement à la place. En principe, cela devrait être bon marché en toute simplicité, mais en réalité, les gens ne donnent pas la priorité à la sécurité, donc... cela peut nécessiter de faire des choses comme changer de fournisseur de cloud ou passer à une autre plate-forme intégrée. Et malheureusement, vous constaterez peut-être que cela coûte plus cher que vous (ou votre patron) êtes prêt à payer, vous êtes donc coincé avec un système mal configuré. Mes sympathies si oui.

  • Comme le note @forest, si vous avez besoin de beaucoup de valeurs CPRNG, alors si vous faites très attention, vous pouvez accélérer cela en exécutant votre propre CPRNG dans l'espace utilisateur, tout en utilisant getrandom pour le (ré)ensemencement. Cependant, il s'agit en grande partie d'une chose "réservée aux experts", comme dans toute situation où vous vous retrouvez à implémenter vos propres primitives cryptographiques. Vous ne devriez le faire que si vous avez mesuré et trouvé cela en utilisant getrandom directement est trop lent pour vos besoins et vous avez une expertise significative en cryptographie. Il est très facile de bousiller une implémentation CPRNG de telle manière que votre sécurité est totalement brisée, mais la sortie "semble" toujours aléatoire, donc vous ne le remarquez pas.


Linux
  1. Comment générer un mot de passe aléatoire sous Linux en utilisant /dev/random

  2. Quelle est la portabilité de /dev/stdin, /dev/stdout et /dev/stderr ?

  3. Comment encoder en base64 /dev/random ou /dev/urandom ?

  4. /dev/random Extrêmement lent ?

  5. Différences entre /dev/sda et /dev/sda1

Linux :Différence entre /dev/console , /dev/tty et /dev/tty0

noyau :désactiver /dev/kmem et /dev/mem

Comment Linux utilise /dev/tty et /dev/tty0

Est-ce une erreur de lier /dev/random à /dev/urandom sous Linux ?

echo ou print /dev/stdin /dev/stdout /dev/stderr

Pourquoi < ou > sont-ils nécessaires pour utiliser /dev/tcp