Jai besoin de créer un fichier de configuration pour mon propre script:
Voici un exemple:
script:
#!/bin/bash source /home/myuser/test/config echo "Name=$nam" >&2 echo "Surname=$sur" >&2
Contenu de /home/myuser/test/config
:
nam="Mark" sur="Brown"
ça marche!
Ma question: est-ce la bonne façon de faire ceci ou y a-t-il « dautres moyens?
Commentaires
- Les variables doivent être en haut. I ‘ m surpris que cela fonctionne. Quoi quil en soit, pourquoi avez-vous besoin dun fichier de configuration? Envisagez-vous dutiliser ces variables ailleurs?
- Faheem, jai besoin des variables car mon script a de nombreuses options: utiliser un Le fichier de configuration illustrera le script. Merci
- IMHO cest très bien. Je ferais de cette façon.
-
abcde
le fait aussi de cette façon et cest un programme assez volumineux (pour un script shell). Vous pouvez le consulter ici .
La réponse
source
nest pas sécurisée car elle exécutera du code arbitraire. Cela peut ne pas vous inquiéter, mais si les autorisations de fichier sont incorrectes, il peut être possible pour un attaquant disposant dun accès au système de fichiers dexécuter du code en tant quutilisateur privilégié en injectant du code dans un fichier de configuration chargé par un script autrement sécurisé tel quun init script.
Jusquà présent, la meilleure solution que jai pu identifier est la solution maladroite de réinventer la roue:
myscript.conf
password=bar echo rm -rf / PROMPT_COMMAND="echo "Sending your last command $(history 1) to my email"" hostname=localhost; echo rm -rf /
En utilisant source
, cela exécuterait echo rm -rf /
deux fois, ainsi que modifierait lutilisateur « s $PROMPT_COMMAND
. À la place, faites ceci:
myscript.sh (Bash 4)
#!/bin/bash typeset -A config # init array config=( # set default values in config array [username]="root" [password]="" [hostname]="localhost" ) while read line do if echo $line | grep -F = &>/dev/null then varname=$(echo "$line" | cut -d "=" -f 1) config[$varname]=$(echo "$line" | cut -d "=" -f 2-) fi done < myscript.conf echo ${config[username]} # should be loaded from defaults echo ${config[password]} # should be loaded from config file echo ${config[hostname]} # includes the "injected" code, but it"s fine here echo ${config[PROMPT_COMMAND]} # also respects variables that you may not have # been looking for, but they"re sandboxed inside the $config array
myscript.sh (compatible Mac / Bash 3)
#!/bin/bash config() { val=$(grep -E "^$1=" myscript.conf 2>/dev/null || echo "$1=__DEFAULT__" | head -n 1 | cut -d "=" -f 2-) if [[ $val == __DEFAULT__ ]] then case $1 in username) echo -n "root" ;; password) echo -n "" ;; hostname) echo -n "localhost" ;; esac else echo -n $val fi } echo $(config username) # should be loaded from defaults echo $(config password) # should be loaded from config file echo $(config hostname) # includes the "injected" code, but it"s fine here echo $(config PROMPT_COMMAND) # also respects variables that you may not have # been looking for, but they"re sandboxed inside the $config array
Veuillez répondre si vous trouvez un exploit de sécurité dans mon code.
Commentaires
Réponse
Analyser le fichier de configuration, ne pas lexécuter.
Jécris actuellement une application au travail qui utilise une configuration XML extrêmement simple:
<config> <username>username-or-email</username> <password>the-password</password> </config>
Dans le script shell (l « application »), cest ce que je fais pour obtenir le nom dutilisateur (plus ou moins, je « lai mis dans une fonction shell):
username="$( xml sel -t -v "/config/username" "$config_file" )"
La commande xml
est XMLStarlet , qui est disponible pour la plupart des Unices.
Jutilise XML car dautres parties de lapplication traitent également des données encodées dans des fichiers XML, donc cétait plus simple.
Si vous préférez JSON, il « s jq
qui est un analyseur JSON shell facile à utiliser.
Mon fichier de configuration ressemblerait à quelque chose comme ça dans JS ON:
{ "username": "username-or-email", "password": "the-password" }
Et puis jobtiendrais le nom dutilisateur dans le script:
username="$( jq -r ".username" "$config_file" )"
Commentaires
- Lexécution du script présente un certain nombre davantages et dinconvénients. Les principaux inconvénients sont la sécurité, si quelquun peut modifier le fichier de configuration, il peut exécuter du code, et il est plus difficile de le rendre idiot. Les avantages sont la rapidité, sur un simple test, il est plus de 10000 fois plus rapide de trouver un fichier de configuration que dexécuter pq, et la flexibilité, quiconque aime le patching de singe python appréciera cela.
- @icarus les fichiers de configuration que vous rencontrez habituellement et à quelle fréquence devez-vous les analyser en une seule session? Notez également que plusieurs valeurs peuvent être extraites de XML ou JSON en une seule fois.
- En général, seules quelques valeurs (1 à 3). Si vous utilisez
eval
pour définir plusieurs valeurs, vous exécutez des parties sélectionnées du fichier de configuration :-). - @icarus Je pensais aux tableaux … Pas besoin de
eval
quoi que ce soit. La performance de lutilisation dun format standard avec un analyseur existant (même si ‘ est un utilitaire externe) est négligeable par rapport à la robustesse, la quantité de code, la facilité dutilisation et maintenabilité. - +1 pour » analyser le fichier de configuration, ne ‘ lexécuter »
Réponse
Voici une version propre et portable compatible avec Bash 3 et up, à la fois sur Mac et Linux.
Il spécifie toutes les valeurs par défaut dans un fichier séparé, pour éviter le besoin dune énorme fonction de configuration « par défaut » dupliquée dans tous vos scripts shell. Et il vous permet de choisir entre la lecture avec ou sans solutions de secours par défaut:
config.cfg :
myvar=Hello World
config.cfg.defaults :
myvar=Default Value othervar=Another Variable
config.shlib (il sagit dune bibliothèque, donc il ny a pas de ligne shebang):
config_read_file() { (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d "=" -f 2-; } config_get() { val="$(config_read_file config.cfg "${1}")"; if [ "${val}" = "__UNDEFINED__" ]; then val="$(config_read_file config.cfg.defaults "${1}")"; fi printf -- "%s" "${val}"; }
test.sh (ou tout script où vous voulez lire les valeurs de configuration) :
#!/usr/bin/env bash source config.shlib; # load the config library functions echo "$(config_get myvar)"; # will be found in user-cfg printf -- "%s\n" "$(config_get myvar)"; # safer way of echoing! myvar="$(config_get myvar)"; # how to just read a value without echoing echo "$(config_get othervar)"; # will fall back to defaults echo "$(config_get bleh)"; # "__UNDEFINED__" since it isn"t set anywhere
Explication du script de test:
- Notez que toutes les utilisations de config_get dans test.sh sont placées entre guillemets. En enveloppant chaque config_get entre guillemets, nous nous assurons que le texte de la valeur de la variable ne sera jamais interprété comme des indicateurs. Et cela garantit que nous préservons correctement les espaces blancs, tels que plusieurs espaces dans une ligne dans la valeur de configuration.
- Et quest-ce que cette ligne
printf
? « Cest quelque chose dont vous devez être conscient:echo
est une mauvaise commande pour imprimer du texte sur lequel vous navez aucun contrôle. Même si vous utilisez des guillemets doubles, il interprétera les indicateurs. Essayez de définirmyvar
(dansconfig.cfg
) sur-e
et vous verrez une ligne vide, parce queecho
pensera que cest « un indicateur. Maisprintf
na pas ce problème. Leprintf --
dit « imprimez ceci et ninterprétez rien comme des indicateurs », et le"%s\n"
dit « formatez la sortie sous forme de chaîne avec une nouvelle ligne à la fin, et enfin le paramètre final est la valeur pour printf à formater. - Si vous nallez pas faire écho aux valeurs à lécran, alors vous les attribuez simplement normalement, comme
myvar="$(config_get myvar)";
. Si vous « allez les imprimer à lécran, je suggère dutiliser printf pour être totalement sûr contre toutes les chaînes incompatibles avec lécho qui peuvent être dans la configuration utilisateur. Mais echo convient si la variable fournie par lutilisateur nest pas » t le premier caractère de la chaîne à laquelle vous faites écho, car cest « la seule situation où » flags « pourrait être interprété, donc quelque chose commeecho "foo: $(config_get myvar)";
est sûr, puisque le » toto « ne commence » pas par un tiret et indique donc à echo que le reste de la chaîne nest pas non plus un indicateur pour lui. 🙂
Commentaires
- @ user2993656 Merci davoir remarqué que mon code dorigine contenait toujours mon nom de fichier de configuration privé (environment.cfg) au lieu du nom correct. Quant au » echo -n » que vous avez fait, cela dépend du shell utilisé. Sous Mac / Linux Bash, » echo -n » signifie » écho sans retour à la ligne « , ce que jai fait pour éviter les retours à la ligne. Mais cela semble fonctionner exactement de la même manière sans lui, alors merci pour les modifications!
- En fait, je viens de le réécrire pour utiliser printf au lieu de echo, ce qui garantit que nous ‘ Je vous débarrasserai du risque de mauvaise interprétation des échos » indicateurs » dans les valeurs de configuration.
- Jaime vraiment cette version. Jai laissé tomber les
config.cfg.defaults
au lieu de les définir au moment de lappel$(config_get var_name "default_value")
. tritarget.org/static/… - De même – cest génial.
Réponse
Le moyen le plus courant, le plus efficace et le plus correct est dutiliser source
, ou .
comme forme abrégée. Par exemple:
source /home/myuser/test/config
ou
. /home/myuser/test/config
Cependant, il faut prendre en compte le les problèmes de sécurité que lutilisation dun fichier de configuration externe supplémentaire peut soulever, étant donné que du code supplémentaire peut être inséré. Pour plus dinformations, notamment sur la manière de détecter et de résoudre ce problème, je vous recommande de consulter la section « Sécuriser » de http://wiki.bash-hackers.org/howto/conffile#secure_it
Commentaires
- Javais de grands espoirs pour cet article (également apparu dans mes résultats de recherche), mais lauteur ‘ dessayer dutiliser des regex pour filtrer le code malveillant est un exercice futile.
- La procédure avec un point nécessite un chemin absolu?Avec le relatif, il ne ‘ t fonctionne
Réponse
Jutilise ceci dans mes scripts:
sed_escape() { sed -e "s/[]\/$*.^[]/\\&/g" } cfg_write() { # path, key, value cfg_delete "$1" "$2" echo "$2=$3" >> "$1" } cfg_read() { # path, key -> value test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" | sed "s/^$(echo "$2" | sed_escape)=//" | tail -1 } cfg_delete() { # path, key test -f "$1" && sed -i "/^$(echo $2 | sed_escape).*$/d" "$1" } cfg_haskey() { # path, key test -f "$1" && grep "^$(echo "$2" | sed_escape)=" "$1" > /dev/null }
Doit prendre en charge toutes les combinaisons de caractères, sauf que les clés ne peuvent « t avoir =
en eux, puisque cest le séparateur. Tout le reste fonctionne.
% cfg_write test.conf mykey myvalue % cfg_read test.conf mykey myvalue % cfg_delete test.conf mykey % cfg_haskey test.conf mykey || echo "It"s not here anymore" It"s not here anymore
De plus, cest totalement sûr car il nutilise pas source
ou eval
.
Commentaires
- Isn ‘ t ceci est supposé créer dabord le fichier de configuration sil nexiste pas ‘? Il nexiste ‘ t, mais
touch -a "${path}"
sassurera bien sûr quil existe, même sans mettre à jour frivolement son mtime.
Réponse
Cest succinct et sécurisé:
# Read common vars from common.vars # the incantation here ensures (by env) that only key=value pairs are present # then declare-ing the result puts those vars in our environment declare $(env -i `cat common.vars`)
Le -i
garantit que vous nobtenez que les variables de common.vars
Mise à jour: Une illustration de la sécurité est que
env -i "touch evil1 foo=omg boo=$(touch evil2)"
Ne produira aucun fichier touché. Testé sur mac avec bash, cest-à-dire en utilisant bsd env.
Commentaires
- Voyez simplement comment les fichiers evil1 et evil2 sont créés si vous mettez ceci dans comm on.vars « « touch evil1 foo = omg boo = $ (touch evil2) « «
- @pihentagy Pour moi, ce qui suit ne produit aucun fichier touché
env -i 'touch evil1 foo=omg boo=$(touch evil2)'
. Exécution sur mac. - en effet, mais ne peut pas accéder à foo. Jai ‘ essayé
env -i ... myscript.sh
et à lintérieur de ce script foo nest pas défini. Cependant, si vous supprimez » garbage « , cela fonctionnera. Alors merci pour lexplication. : +1:
Réponse
La plupart des utilisateurs, bien que peu de conteneurs, possèdent déjà le git
binaire. Pourquoi ne pas donc utiliser git config
pour la gestion de la configuration de lapplication en utilisant un fichier de configuration dédié non conflictuel comme dans les exemples ci-dessous?
# Set $ git config -f ~/.myapp core.mykey myval # Get $ git config -f ~/.myapp core.mykey myval # Get invalid $ git config -f ~/.myapp core.mykey $ echo $? 1 # List git config -f ~/.myapp -l core.mykey=myval # View $ cat ~/.myapp [core] mykey = myval
Pour des commandes supplémentaires, voir son man page. Il est préférable de sassurer que le fichier de configuration existe:
touch -a ~/.myapp
Réponse
Celui-ci semble sûr et court. Nhésitez pas à le craquer sans pitié. Je « voudrais connaître un meilleur moyen.
TL; DR;
while read LINE; do declare "$LINE"; done < evil.conf
Je » m utilise bash 4.3.48.
Il est également compatible avec bash --posix
. Voir le bas pour le test.
Mais sh
ne le prend pas en charge à cause de declare
.
Test de base pour ceux qui veulent une preuve
Créer un fichier evil.conf
echo > evil.conf " A=1 B=2 C=$(echo hello) # Could produce side-effect D=`touch evil` C=$((1+2)) E=$(ping 8.8.8.8 -n 3) echo hello # Could produce visible side-effect touch evil2 ping 8.8.8.8 -n 3 F=ok"
Chargez la configuration avec lextrait de code
while read LINE; do declare "$LINE"; done < evil.conf
Sortie (voir le désinfectant en action)
bash: declare: `": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce side-effect": not a valid identifier bash: declare: `echo hello": not a valid identifier bash: declare: `": not a valid identifier bash: declare: `# Could produce visible side-effect": not a valid identifier bash: declare: `touch evil2": not a valid identifier bash: declare: `ping 8.8.8.8 -n 3": not a valid identifier
Vérifions les valeurs maintenant
for V in A B C D E F; do declare -p $V; done
declare -- A="1" declare -- B="2" declare -- C="\$((1+2))" declare -- D="\`touch evil\`" declare -- E="\$(ping 8.8.8.8 -n 3)" declare -- F="ok"
Vérifier les effets secondaires (pas deffets secondaires):
ls evil evil2
ls: cannot access "evil": No such file or directory ls: cannot access "evil2": No such file or directory
Annexe. Test bash --posix
bash -c "while read LINE; do declare "$LINE"; done < evil.conf; for V in A B C D E F; do declare -p $V; done" --posix
Réponse
Pour mon scénario, source
ou .
w aussi bien, mais je voulais prendre en charge les variables denvironnement locales (cest-à-dire FOO=bar myscript.sh
) en prenant le pas sur les variables configurées. Je voulais aussi que le fichier de configuration soit modifiable par lutilisateur et confortable pour quelquun qui a lhabitude de trouver des fichiers de configuration, et quil soit aussi petit / simple que possible, pour ne pas détourner lattention de lorientation principale de mon tout petit script.
Voici ce que jai trouvé:
CONF=${XDG_CONFIG_HOME:-~/config}/myscript.sh if [ ! -f $CONF ]; then cat > $CONF << CONF VAR1="default value" CONF fi . <(sed "s/^\([^=]\+\) *= *\(.*\)$/\1=${\1:-\2}/" < $CONF)
Essentiellement – il vérifie les définitions de variables (sans être très flexible sur les espaces) et réécrit ces lignes afin que le La valeur est convertie en valeur par défaut pour cette variable, et la variable nest pas modifiée si elle est trouvée, comme la variable XDG_CONFIG_HOME
ci-dessus. Il fournit cette version modifiée du fichier de configuration et continue.
Les travaux futurs pourraient rendre le script sed
plus robuste, filtrer les lignes qui semblent étranges ou qui ne sont pas » t définitions, etc., ne sautent pas sur les commentaires de fin de ligne – mais cela me suffit pour le moment.
Réponse
Vous pouvez le faire:
#!/bin/bash name="mohsen" age=35 cat > /home/myuser/test/config << EOF Name=$name Age=$age EOF
*
dans les entrées, mais alors Quest-ce qui dans Bash gère bien ce caractère?my\\password