Comment passer des variables en toute sécurité à des scripts root?

13

Cette question est totalement générale et ne s'applique pas seulement à ma situation, mais ... J'ai un petit boîtier occupé où je veux qu'un utilisateur non root puisse exécuter un script particulier avec des privilèges root. Par exemple, quelque chose comme ce petit script pour activer DHCP, où la seule variable ( $1) à envoyer sur la ligne de commande (!!) est le nom d'hôte à envoyer:

#!/bin/bash
udhcpc -b -i eth0 -h $1

exécuter udhcpc comme celui-ci nécessite un accès root, donc pour ce faire, je prévois de modifier /etc/sudoerspour contenir cette ligne:

joe ALL = NOPASSWD: /path/to/enable_dhcp.sh

ce qui devrait permettre à "joe" d'exécuter facilement ce script avec les privilèges root en exécutant simplement:

sudo /path/to/enable_dhcp.sh

et qu'on ne me demande pas de mot de passe (c'est ce que je veux, puisque je veux que joe puisse écrire ceci).

Maintenant .. Je sais (ou du moins pense que je le fais) que l'utilisation $1dans un script qui peut facilement être exécuté avec des privilèges root est une idée HORRIBLE puisque vous pouvez injecter tout ce que vous voulez dans cela.

Alors ... quelle est la meilleure façon de gérer cela? Comment laisser joe faire ce que je veux avec les privilèges root, lui permettre de passer une variable (ou le faire efficacement comme avec une variable d'environnement), tout en n'étant pas ouvert aux attaques par injection?

Russ
la source

Réponses:

14

L'exécution de scripts shell sous sudoest sûre à condition qu'elle sudosoit configurée pour réinitialiser l'environnement . Inversement, si sudone réinitialise pas l'environnement, l'exécution d'un script shell n'est pas sûre, même si votre script n'utilise pas ses paramètres (voir Autoriser setuid sur les scripts shell ). Assurez-vous que vous avez Defaults env_resetdans /etc/sudoersou que cette option est la valeur par défaut à la compilation ( sudo sudo -V | grep envdevrait inclure Reset the environment to a default set of variables).

Il n'y a pas de danger particulier à utiliser les paramètres du script. $1est une chaîne, il vous suffit de vous assurer que vous l'utilisez comme chaîne. (Par exemple, ne le faites pas eval "$1".) De toute évidence, il est particulièrement important ici de ne pas faire d'hypothèses sur le contenu de la variable et de mettre des guillemets autour de toutes les substitutions de variables (c'est-à-dire écrire "$1", pas $1). Notez que mettre des guillemets autour des substitutions de variables n'est pas spécifique aux scripts exécutés avec des privilèges, c'est quelque chose que vous devez faire tout le temps.

Vous voudrez peut-être valider davantage le paramètre, en fonction de ce qui se udhcpcpasse avec quelque chose qui ne ressemble pas à un nom d'hôte. Par exemple, cela effectuera une première vérification syntaxique:

#!/bin/sh
case "$1" in
  *[!:-.0-9A-Za-z]*|-*) echo 1>&2 "Badly formed host name!"; exit 3;;
esac
udhcpc -b -i eth0 -h "$1"
Gilles 'SO- arrête d'être méchant'
la source
D'autres éléments à prendre en compte peuvent avoir un impact sur le comportement de cette commande: le répertoire de travail actuel et les points vers lesquels stdin, stdout, stderr pointent, les gestionnaires de signaux et ulimits. Par exemple, en autorisant les vidages de mémoire (sur <kbd> Ctrl- \ </kbd>), vous pourriez permettre à l'utilisateur de jeter / exécuter / verrouiller qui sur mon système est un tmpfs avec une taille très limitée et pourrait autoriser un DoS.
Stéphane Chazelas
5

Vous devez faire correspondre l'entrée passée avec un bon modèle connu.

Par exemple, il semble qu'une adresse IP puisse être une entrée valide pour vous. Vous pouvez donc utiliser quelque chose comme ceci:

if [[ "$1" =~ ^[0-9]?[0-9]?[0-9].[0-9]?[0-9]?[0-9].[0-9]?[0-9]?[0-9]$ ]]
then
  udhcpc -b -i eth0 -h "$1"
else
  echo "Don't mess with me pork chop."
fi

Notez que l'expression régulière n'a pas été testée, vous êtes responsable de vous assurer que votre expression régulière ne permet rien de dangereux.

bahamat
la source