web-dev-qa-db-fra.com

Masquage du mot de passe dans les scripts shell

Comment masquer un mot de passe dans les scripts Shell? Il existe un certain nombre de scripts qui accèdent à la base de données. Si nous ouvrons le script, d'autres connaissent également le nom d'utilisateur et le mot de passe. Donc, si quelqu'un sait comment se cacher, faites-le moi savoir.

J'ai un moyen: placer le mot de passe dans un fichier et le rendre caché et personne ne va accéder au fichier (modifier les autorisations et utiliser le fichier dans le script tout en accédant à la base de données).

26
arun

First, comme plusieurs personnes l'ont déjà dit, il est essentiel de conserver les informations d'identification distinctes du script. (En plus d'une sécurité accrue, cela signifie également que vous pouvez réutiliser le même script pour plusieurs systèmes avec des informations d'identification différentes.)

Second, vous devez considérer non seulement la sécurité des informations d'identification, mais également l'impact si/quand ces informations d'identification sont compromises. Vous ne devez pas avoir un seul mot de passe pour tous les accès à la base de données, vous devez avoir des informations d'identification différentes avec différents niveaux d'accès. Vous pouvez, par exemple, avoir un utilisateur de base de données qui a la possibilité d'effectuer une recherche dans la base de données - cet utilisateur doit avoir un accès en lecture seule. Un autre utilisateur peut être autorisé à insérer de nouveaux enregistrements, mais pas à les supprimer. Un troisième peut être autorisé à supprimer des enregistrements.

En plus de restreindre les autorisations pour chaque compte, vous devez également restreindre la provenance de chaque compte. Par exemple, le compte utilisé par votre serveur Web ne doit pas être autorisé à se connecter à partir d'une autre adresse IP que celle du serveur Web. Un compte avec des autorisations root complètes sur la base de données doit en effet être très restreint en termes de provenance et ne doit jamais être utilisé autrement qu'interactivement. Pensez également à utiliser des procédures stockées dans la base de données pour restreindre exactement ce qui peut être fait par chaque compte.

Ces restrictions doivent être implémentées du côté serveur de base de données du système, de sorte que même si le côté client est compromis, les restrictions ne peuvent pas être modifiées. (Et, évidemment, le serveur DB doit être protégé avec des pare-feu, etc. en plus de la configuration DB ...)

Dans le cas d'un compte DB qui n'est autorisé qu'à un accès limité en lecture seule, et uniquement à partir d'une adresse IP particulière, vous n'aurez peut-être pas besoin d'autres informations d'identification que cela, selon la sensibilité des données et la sécurité de l'hôte du script est en cours d'exécution. Un exemple peut être un formulaire de recherche sur votre site Web, qui peut être exécuté avec un utilisateur qui est uniquement autorisé à utiliser une procédure stockée qui extrait uniquement les informations qui seront présentées sur la page Web. Dans ce cas, l'ajout d'un mot de passe ne confère pas vraiment de sécurité supplémentaire, car ces informations sont déjà censées être publiques, et l'utilisateur ne peut accéder à aucune autre donnée qui serait plus sensible.

Assurez-vous également que la connexion à la base de données est établie à l'aide de TLS, ou que quiconque écoute sur le réseau peut obtenir vos informations d'identification.

Troisième, réfléchissez au type d'informations d'identification à utiliser. Les mots de passe ne sont qu'une forme et ne sont pas les plus sécurisés. Vous pouvez à la place utiliser une certaine forme de paire de clés publique/privée, ou AD/PAM ou similaire.

Quatrième, considérez les conditions dans lesquelles le script sera exécuté:

S'il est exécuté de manière interactive, vous devez entrer le mot de passe ou le mot de passe de la clé privée ou de la clé privée, ou être connecté avec un ticket Kerberos valide, lorsque vous l'exécutez - en d'autres termes, le script doit obtenir son informations d'identification directement auprès de vous au moment où vous l'exécutez, au lieu de les lire à partir d'un fichier.

S'il est exécuté à partir d'un serveur Web, envisagez de configurer les informations d'identification au moment où vous démarrez le serveur Web. Un bon exemple ici est les certificats SSL - ils ont un certificat public et une clé privée, et la clé privée a un mot de passe. Vous pouvez stocker la clé privée sur le serveur Web, mais vous devez toujours y saisir le mot de passe lorsque vous démarrez Apache. Vous pouvez également disposer des informations d'identification sur un type de matériel, comme une carte physique ou un HSM, qui peut être supprimé ou verrouillé une fois le serveur démarré. (Bien sûr, l'inconvénient de cette méthode est que le serveur ne peut pas redémarrer seul si quelque chose se produit. Je préférerais cela au risque de compromettre mon système, mais votre kilométrage peut varier ...)

Si le script est exécuté à partir de cron, c'est la partie difficile. Vous ne voulez pas que les informations d'identification se trouvent n'importe où sur votre système où quelqu'un puisse y accéder - mais vous voulez les faire traîner afin que votre script puisse y accéder, non? Eh bien, pas tout à fait raison. Considérez exactement ce que fait le script. De quelles autorisations a-t-il besoin sur la base de données? Peut-il être restreint afin que cela n'ait pas d'importance si la mauvaise personne se connecte avec ces autorisations? Pouvez-vous plutôt exécuter le script directement sur le serveur de base de données auquel personne d'autre n'a accès, au lieu du serveur qui a d'autres utilisateurs? Si, pour une raison à laquelle je ne peux pas penser, vous devez absolument exécuter le script sur un serveur non sécurisé et le doit être capable de faire quelque chose de dangereux/destructeur ... c'est le bon moment pour repenser votre architecture.

Cinquième, si vous appréciez la sécurité de votre base de données, vous ne devriez pas exécuter ces scripts sur des serveurs auxquels d'autres personnes ont accès. Si quelqu'un est connecté sur votre système, il aura a la possibilité d'obtenir vos informations d'identification. Par exemple, dans le cas d'un serveur Web avec un certificat SSL, il existe au moins une possibilité théorique que quelqu'un puisse accéder à la racine et accéder à la zone de mémoire du processus httpd et extraire les informations d'identification. Il y a eu au moins un exploit ces derniers temps où cela pourrait être fait via SSL, sans même exiger que l'attaquant soit connecté.

Pensez également à utiliser SELinux ou apparmor ou tout ce qui est disponible pour votre système pour restreindre les utilisateurs qui peuvent faire quoi. Ils vous permettront d'interdire aux utilisateurs d'essayer même de se connecter à la base de données, même s'ils parviennent à accéder aux informations d'identification.

Si tout cela vous semble exagéré, et vous ne pouvez pas vous permettre ou n'avez pas le temps de le faire - alors, à mon avis (arrogant et élitiste), vous ne devriez rien stocker important ou sensible dans votre base de données. Et si vous ne stockez rien d'important ou de sensible, alors où vous stockez vos informations d'identification n'est pas important non plus - dans ce cas, pourquoi utiliser un mot de passe?

Enfin, si vous ne pouvez absolument pas éviter de stocker une sorte d'informations d'identification, vous pourriez avoir les informations d'identification en lecture seule et détenues par root et root pourrait accorder la propriété sur une base extrêmement temporaire lorsque demandé par un script (parce que votre script devrait pas être exécuté en tant que root sauf si cela est absolument nécessaire, et la connexion à une base de données ne le rend pas nécessaire). Mais ce n'est toujours pas une bonne idée.

41
Jenny D

Tout d'abord, s'il existe un moyen de changer les choses pour éviter d'avoir à stocker un mot de passe à l'intérieur ou à côté d'un script, vous devriez faire tous les efforts pour le faire. réponse de Jenny D contient beaucoup de bons conseils à cet effet.

Sinon, votre idée de placer le mot de passe dans un fichier séparé avec des autorisations restreintes est à peu près tout. Vous pouvez par exemple source ce fichier à partir du script principal:

. /usr/local/etc/secret-password-here

Vous pouvez également restreindre les autorisations du script principal afin que seules les personnes autorisées puissent l'exécuter, mais il est probablement préférable de faire comme vous le suggérez et de stocker uniquement le mot de passe lui-même dans un fichier restreint. De cette façon, vous pouvez permettre l'inspection du code lui-même (sans secrets sensibles), le contrôle de version et la copie autour du script plus facilement, etc ...

13
Celada

D'autres réponses ont abordé le comment, mais je considérerai le si. Selon le type de base de données auquel vos utilisateurs se connectent, vous disposez peut-être déjà d'un mécanisme approprié déjà utilisé par ces programmes clients, auquel cas assurez-vous de les utiliser (je pense à ~/.mysqlrc ou ~/.pgpass).

Si vous donnez à plusieurs utilisateurs la possibilité d'accéder à la base de données pour effectuer des requêtes spécifiques à l'aide d'un compte partagé, vous ne devriez probablement pas. Au lieu de cela, assurez-vous qu'ils ont des comptes sur la base de données et que ces comptes n'ont pas plus d'autorisations que nécessaire (probablement lire une grande partie de celui-ci et très peu d'accès en écriture). S'ils doivent effectuer des requêtes spécifiques sur certaines tables auxquelles ils ne pourraient pas accéder autrement, fournissez des procédures stockées avec SECURTY DEFINER pour leur permettre de le faire.

Si rien de ce qui précède ne vous évite d'avoir à stocker des informations d'identification, alors lisez les autres réponses ici.

3
Toby Speight

Votre idée de cacher le mot de passe dans un endroit inaccessible pourrait être OK selon les circonstances.

Si les informations sont séparées, cela signifie une simple modification du fichier, par exemple lors d'une révision de code avec un collègue ne va pas le montrer. Mais sachez que toute personne ayant accès à votre compte peut facilement trouver un tel fichier. J'ai utilisé un sous-répertoire de ~/.ssh à de telles fins, pour la simple raison que ssh se plaint si les droits d'accès pour ~/.ssh ne sont pas assez restreints.

Il y a cependant d'autres choses que vous pouvez faire pour empêcher un tel coup d'œil du nom d'utilisateur et/ou des mots de passe.

Obfuscating: Si vous ne voulez pas que quelqu'un lise le nom d'utilisateur ou le mot de passe pendant que vous éditez un script, vous pouvez le mettre dans le texte mais pas en texte brut, mais obscurci. Si la version obscurcie est suffisamment longue pour empêcher sa mémorisation par quelqu'un qui la regarde, elle ne peut pas être connue même si la méthode et la clé de déchiffrement sont bien en vue. Bien sûr, cela serait encore contourné par une personne ayant accès à votre compte.

en utilisant GPG:

Un peu mieux, mais toujours pas une preuve complète contre les attaques de l'utilisateur root sur votre système, consiste à crypter les paires nom d'utilisateur/mot de passe dans un fichier public gpg et à ce que le script récupère le contenu du fichier (possible, vous y invitant pour un mot de passe, s'il n'est pas mis en cache/expiré). J'utilise ggp-card pour que lorsque je laisse mon ordinateur portable autour mais que je retire la carte, aucun accès ne soit possible même si la broche serait toujours mise en cache. Au moins si je lance quelque chose 20 fois en quelques minutes, je ne dois fournir la broche qu'une seule fois.

La sécurité semble toujours inversement liée à la commodité (configuration et utilisation).

1
Anthon

J'ai également rencontré ce problème. La réponse courte est de stocker le mot de passe séparément du script, puis de charger le script dans le mot de passe de l'environnement. Ce qui soulève la question de savoir comment le faire au mieux?

Idéalement, vous pourriez utiliser un service externe comme HashiCorp Vault pour le tirer dans l'environnement Shell; Cependant, Vault est une bête assez compliquée à configurer et dans de nombreux environnements d'entreprise, vous pouvez être limité par ce que vous pouvez installer et même le type de Shell que vous pourriez utiliser.

Pour cette raison, j'ai créé encpass.sh - Solution légère pour utiliser des mots de passe cryptés dans les scripts Shell qui vous permet de stocker des secrets dans un répertoire caché accessible uniquement par votre utilisateur. Il devrait fonctionner dans n'importe quel shell compatible POSIX et sa seule dépendance réelle est d'installer OpenSSL sur la boîte locale pour gérer le cryptage des secrets. Il vous suffit de l'inclure dans votre script et d'appeler la fonction "get_secret". Il a une licence MIT, vous pouvez donc l'utiliser dans un environnement d'entreprise. Mon entreprise, Plyint LLC , gère le script et publie des mises à jour de temps en temps. Je vais copier le code ci-dessous pour une visibilité facile.

#!/bin/sh
################################################################################
# Copyright (c) 2020 Plyint, LLC <[email protected]>. All Rights Reserved.
# This file is licensed under the MIT License (MIT). 
# Please see LICENSE.txt for more information.
# 
# DESCRIPTION: 
# This script allows a user to encrypt a password (or any other secret) at 
# runtime and then use it, decrypted, within a script.  This prevents shoulder 
# surfing passwords and avoids storing the password in plain text, which could 
# inadvertently be sent to or discovered by an individual at a later date.
#
# This script generates an AES 256 bit symmetric key for each script (or user-
# defined bucket) that stores secrets.  This key will then be used to encrypt 
# all secrets for that script or bucket.  encpass.sh sets up a directory 
# (.encpass) under the user's home directory where keys and secrets will be 
# stored.
#
# For further details, see README.md or run "./encpass ?" from the command line.
#
################################################################################

encpass_checks() {
    if [ -n "$ENCPASS_CHECKS" ]; then
        return
    fi

    if [ ! -x "$(command -v openssl)" ]; then
        echo "Error: OpenSSL is not installed or not accessible in the current path." \
            "Please install it and try again." >&2
        exit 1
    fi

    if [ -z "$ENCPASS_HOME_DIR" ]; then
        ENCPASS_HOME_DIR=$(encpass_get_abs_filename ~)/.encpass
    fi

    if [ ! -d "$ENCPASS_HOME_DIR" ]; then
        mkdir -m 700 "$ENCPASS_HOME_DIR"
        mkdir -m 700 "$ENCPASS_HOME_DIR/keys"
        mkdir -m 700 "$ENCPASS_HOME_DIR/secrets"
    fi

    if [ "$(basename "$0")" != "encpass.sh" ]; then
        encpass_include_init "$1" "$2"
    fi

    ENCPASS_CHECKS=1
}

# Initializations performed when the script is included by another script
encpass_include_init() {
    if [ -n "$1" ] && [ -n "$2" ]; then
        ENCPASS_BUCKET=$1
        ENCPASS_SECRET_NAME=$2
    Elif [ -n "$1" ]; then
        ENCPASS_BUCKET=$(basename "$0")
        ENCPASS_SECRET_NAME=$1
    else
        ENCPASS_BUCKET=$(basename "$0")
        ENCPASS_SECRET_NAME="password"
    fi
}

encpass_generate_private_key() {
    ENCPASS_KEY_DIR="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET"

    if [ ! -d "$ENCPASS_KEY_DIR" ]; then
        mkdir -m 700 "$ENCPASS_KEY_DIR"
    fi

    if [ ! -f "$ENCPASS_KEY_DIR/private.key" ]; then
        (umask 0377 && printf "%s" "$(openssl Rand -hex 32)" >"$ENCPASS_KEY_DIR/private.key")
    fi
}

encpass_get_private_key_abs_name() {
    ENCPASS_PRIVATE_KEY_ABS_NAME="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key"

    if [ "$1" != "nogenerate" ]; then 
        if [ ! -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then
            encpass_generate_private_key
        fi
    fi
}

encpass_get_secret_abs_name() {
    ENCPASS_SECRET_ABS_NAME="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET/$ENCPASS_SECRET_NAME.enc"

    if [ "$3" != "nocreate" ]; then 
        if [ ! -f "$ENCPASS_SECRET_ABS_NAME" ]; then
            set_secret "$1" "$2"
        fi
    fi
}

get_secret() {
    encpass_checks "$1" "$2"
    encpass_get_private_key_abs_name
    encpass_get_secret_abs_name "$1" "$2"
    encpass_decrypt_secret
}

set_secret() {
    encpass_checks "$1" "$2"

    if [ "$3" != "reuse" ] || { [ -z "$ENCPASS_SECRET_INPUT" ] && [ -z "$ENCPASS_CSECRET_INPUT" ]; }; then
        echo "Enter $ENCPASS_SECRET_NAME:" >&2
        stty -echo
        read -r ENCPASS_SECRET_INPUT
        stty echo
        echo "Confirm $ENCPASS_SECRET_NAME:" >&2
        stty -echo
        read -r ENCPASS_CSECRET_INPUT
        stty echo
    fi

    if [ "$ENCPASS_SECRET_INPUT" = "$ENCPASS_CSECRET_INPUT" ]; then
        encpass_get_private_key_abs_name
        ENCPASS_SECRET_DIR="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET"

        if [ ! -d "$ENCPASS_SECRET_DIR" ]; then
            mkdir -m 700 "$ENCPASS_SECRET_DIR"
        fi

        printf "%s" "$(openssl Rand -hex 16)" >"$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"

        ENCPASS_OPENSSL_IV="$(cat "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc")"

        echo "$ENCPASS_SECRET_INPUT" | openssl enc -aes-256-cbc -e -a -iv \
            "$ENCPASS_OPENSSL_IV" -K \
            "$(cat "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key")" 1>> \
                    "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"
    else
        echo "Error: secrets do not match.  Please try again." >&2
        exit 1
    fi
}

encpass_get_abs_filename() {
    # $1 : relative filename
    filename="$1"
    parentdir="$(dirname "${filename}")"

    if [ -d "${filename}" ]; then
        cd "${filename}" && pwd
    Elif [ -d "${parentdir}" ]; then
        echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
    fi
}

encpass_decrypt_secret() {
    if [ -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ]; then
        ENCPASS_DECRYPT_RESULT="$(dd if="$ENCPASS_SECRET_ABS_NAME" ibs=1 skip=32 2> /dev/null | openssl enc -aes-256-cbc \
            -d -a -iv "$(head -c 32 "$ENCPASS_SECRET_ABS_NAME")" -K "$(cat "$ENCPASS_PRIVATE_KEY_ABS_NAME")" 2> /dev/null)"
        if [ ! -z "$ENCPASS_DECRYPT_RESULT" ]; then
            echo "$ENCPASS_DECRYPT_RESULT"
        else
            # If a failed unlock command occurred and the user tries to show the secret
            # Present either locked or decrypt command
            if [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then 
            echo "**Locked**"
            else
                # The locked file wasn't present as expected.  Let's display a failure
            echo "Error: Failed to decrypt"
            fi
        fi
    Elif [ -f "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.lock" ]; then
        echo "**Locked**"
    else
        echo "Error: Unable to decrypt. The key file \"$ENCPASS_PRIVATE_KEY_ABS_NAME\" is not present."
    fi
}


##########################################################
# COMMAND LINE MANAGEMENT SUPPORT
# -------------------------------
# If you don't need to manage the secrets for the scripts
# with encpass.sh you can delete all code below this point
# in order to significantly reduce the size of encpass.sh.
# This is useful if you want to bundle encpass.sh with
# your existing scripts and just need the retrieval
# functions.
##########################################################

encpass_show_secret() {
    encpass_checks
    ENCPASS_BUCKET=$1

    encpass_get_private_key_abs_name "nogenerate"

    if [ ! -z "$2" ]; then
        ENCPASS_SECRET_NAME=$2
        encpass_get_secret_abs_name "$1" "$2" "nocreate"
        if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then
            echo "No secret named $2 found for bucket $1."
            exit 1
        fi

        encpass_decrypt_secret
    else
        ENCPASS_FILE_LIST=$(ls -1 "$ENCPASS_HOME_DIR"/secrets/"$1")
        for ENCPASS_F in $ENCPASS_FILE_LIST; do
            ENCPASS_SECRET_NAME=$(basename "$ENCPASS_F" .enc)

            encpass_get_secret_abs_name "$1" "$ENCPASS_SECRET_NAME" "nocreate"
            if [ -z "$ENCPASS_SECRET_ABS_NAME" ]; then
                echo "No secret named $ENCPASS_SECRET_NAME found for bucket $1."
                exit 1
            fi

            echo "$ENCPASS_SECRET_NAME = $(encpass_decrypt_secret)"
        done
    fi
}

encpass_getche() {
        old=$(stty -g)
        stty raw min 1 time 0
        printf '%s' "$(dd bs=1 count=1 2>/dev/null)"
        stty "$old"
}

encpass_remove() {
    if [ ! -n "$ENCPASS_FORCE_REMOVE" ]; then
        if [ ! -z "$ENCPASS_SECRET" ]; then
            printf "Are you sure you want to remove the secret \"%s\" from bucket \"%s\"? [y/N]" "$ENCPASS_SECRET" "$ENCPASS_BUCKET"
        else
            printf "Are you sure you want to remove the bucket \"%s?\" [y/N]" "$ENCPASS_BUCKET"
        fi

        ENCPASS_CONFIRM="$(encpass_getche)"
        printf "\n"
        if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then
            exit 0
        fi
    fi

    if [ ! -z "$ENCPASS_SECRET" ]; then
        rm -f "$1"
        printf "Secret \"%s\" removed from bucket \"%s\".\n" "$ENCPASS_SECRET" "$ENCPASS_BUCKET"
    else
        rm -Rf "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET"
        rm -Rf "$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET"
        printf "Bucket \"%s\" removed.\n" "$ENCPASS_BUCKET"
    fi
}

encpass_save_err() {
    if read -r x; then
        { printf "%s\n" "$x"; cat; } > "$1"
    Elif [ "$x" != "" ]; then
        printf "%s" "$x" > "$1"
    fi
}

encpass_help() {
less << EOF
NAME:
    encpass.sh - Use encrypted passwords in Shell scripts

DESCRIPTION: 
    A lightweight solution for using encrypted passwords in Shell scripts 
    using OpenSSL. It allows a user to encrypt a password (or any other secret)
    at runtime and then use it, decrypted, within a script. This prevents
    shoulder surfing passwords and avoids storing the password in plain text, 
    within a script, which could inadvertently be sent to or discovered by an 
    individual at a later date.

    This script generates an AES 256 bit symmetric key for each script 
    (or user-defined bucket) that stores secrets. This key will then be used 
    to encrypt all secrets for that script or bucket.

    Subsequent calls to retrieve a secret will not Prompt for a secret to be 
    entered as the file with the encrypted value already exists.

    Note: By default, encpass.sh sets up a directory (.encpass) under the 
    user's home directory where keys and secrets will be stored.  This directory
    can be overridden by setting the environment variable ENCPASS_HOME_DIR to a
    directory of your choice.

    ~/.encpass (or the directory specified by ENCPASS_HOME_DIR) will contain 
    the following subdirectories:
      - keys (Holds the private key for each script/bucket)
      - secrets (Holds the secrets stored for each script/bucket)

USAGE:
    To use the encpass.sh script in an existing Shell script, source the script 
    and then call the get_secret function.

    Example:

        #!/bin/sh
        . encpass.sh
        password=\$(get_secret)

    When no arguments are passed to the get_secret function,
    then the bucket name is set to the name of the script and
    the secret name is set to "password".

    There are 2 other ways to call get_secret:

      Specify the secret name:
      Ex: \$(get_secret user)
        - bucket name = <script name>
        - secret name = "user"

      Specify both the secret name and bucket name:
      Ex: \$(get_secret personal user)
        - bucket name = "personal"
        - secret name = "user"

    encpass.sh also provides a command line interface to manage the secrets.
    To invoke a command, pass it as an argument to encpass.sh from the Shell.

        $ encpass.sh [COMMAND]

    See the COMMANDS section below for a list of available commands.  Wildcard
    handling is implemented for secret and bucket names.  This enables
    performing operations like adding/removing a secret to/from multiple buckets
        at once.

COMMANDS:
    add [-f] <bucket> <secret>
        Add a secret to the specified bucket.  The bucket will be created
        if it does not already exist. If a secret with the same name already
        exists for the specified bucket, then the user will be prompted to
        confirm overwriting the value.  If the -f option is passed, then the
        add operation will perform a forceful overwrite of the value. (i.e. no
        Prompt)

    list|ls [<bucket>]
        Display the names of the secrets held in the bucket.  If no bucket
        is specified, then the names of all existing buckets will be
        displayed.

    lock
        Locks all keys used by encpass.sh using a password.  The user
        will be prompted to enter a password and confirm it.  A user
        should take care to securely store the password.  If the password
        is lost then keys can not be unlocked.  When keys are locked,
        secrets can not be retrieved. (e.g. the output of the values
        in the "show" command will be encrypted/garbage)

    remove|rm [-f] <bucket> [<secret>]
        Remove a secret from the specified bucket.  If only a bucket is
        specified then the entire bucket (i.e. all secrets and keys) will
        be removed.  By default the user is asked to confirm the removal of
        the secret or the bucket.  If the -f option is passed then a 
        forceful removal will be performed.  (i.e. no Prompt)

    show [<bucket>] [<secret>]
        Show the unencrypted value of the secret from the specified bucket.
        If no secret is specified then all secrets for the bucket are displayed.

    update <bucket> <secret>
        Updates a secret in the specified bucket.  This command is similar
        to using an "add -f" command, but it has a safety check to only 
        proceed if the specified secret exists.  If the secret, does not
        already exist, then an error will be reported. There is no forceable
        update implemented.  Use "add -f" for any required forceable update
        scenarios.

    unlock
        Unlocks all the keys for encpass.sh.  The user will be prompted to 
        enter the password and confirm it.

    dir
        Prints out the current value of the ENCPASS_HOME_DIR environment variable.

    help|--help|usage|--usage|?
        Display this help message.
EOF
}

# Subcommands for cli support
case "$1" in
    add )
        shift
        while getopts ":f" ENCPASS_OPTS; do
            case "$ENCPASS_OPTS" in
                f ) ENCPASS_FORCE_ADD=1;;
            esac
        done

        encpass_checks

        if [ -n "$ENCPASS_FORCE_ADD" ]; then
            shift $((OPTIND-1))
        fi

        if [ ! -z "$1" ] && [ ! -z "$2" ]; then
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_ADD_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
            if [ -z "$ENCPASS_ADD_LIST" ]; then
                ENCPASS_ADD_LIST="$1"
            fi

            for ENCPASS_ADD_F in $ENCPASS_ADD_LIST; do
                ENCPASS_ADD_DIR="$(basename "$ENCPASS_ADD_F")"
                ENCPASS_BUCKET="$ENCPASS_ADD_DIR"
                if [ ! -n "$ENCPASS_FORCE_ADD" ] && [ -f "$ENCPASS_ADD_F/$2.enc" ]; then
                    echo "Warning: A secret with the name \"$2\" already exists for bucket $ENCPASS_BUCKET."
                    echo "Would you like to overwrite the value? [y/N]"

                    ENCPASS_CONFIRM="$(encpass_getche)"
                    if [ "$ENCPASS_CONFIRM" != "Y" ] && [ "$ENCPASS_CONFIRM" != "y" ]; then
                        continue
                    fi
                fi

                ENCPASS_SECRET_NAME="$2"
                echo "Adding secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..."
                set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse"
            done
        else
            echo "Error: A bucket name and secret name must be provided when adding a secret."
            exit 1
        fi
        ;;
    update )
        shift

        encpass_checks
        if [ ! -z "$1" ] && [ ! -z "$2" ]; then

            ENCPASS_SECRET_NAME="$2"
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_UPDATE_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"

            for ENCPASS_UPDATE_F in $ENCPASS_UPDATE_LIST; do
                # Allow globbing
                # shellcheck disable=SC2027,SC2086
                if [ -f "$ENCPASS_UPDATE_F/"$2".enc" ]; then
                        ENCPASS_UPDATE_DIR="$(basename "$ENCPASS_UPDATE_F")"
                        ENCPASS_BUCKET="$ENCPASS_UPDATE_DIR"
                        echo "Updating secret \"$ENCPASS_SECRET_NAME\" to bucket \"$ENCPASS_BUCKET\"..."
                        set_secret "$ENCPASS_BUCKET" "$ENCPASS_SECRET_NAME" "reuse"
                else
                    echo "Error: A secret with the name \"$2\" does not exist for bucket $1."
                    exit 1
                fi
            done
        else
            echo "Error: A bucket name and secret name must be provided when updating a secret."
            exit 1
        fi
        ;;
    rm|remove )
        shift
        encpass_checks

        while getopts ":f" ENCPASS_OPTS; do
            case "$ENCPASS_OPTS" in
                f ) ENCPASS_FORCE_REMOVE=1;;
            esac
        done

        if [ -n "$ENCPASS_FORCE_REMOVE" ]; then
            shift $((OPTIND-1))
        fi

        if [ -z "$1" ]; then 
            echo "Error: A bucket must be specified for removal."
        fi

        # Allow globbing
        # shellcheck disable=SC2027,SC2086
        ENCPASS_REMOVE_BKT_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
        if [ ! -z "$ENCPASS_REMOVE_BKT_LIST" ]; then
            for ENCPASS_REMOVE_B in $ENCPASS_REMOVE_BKT_LIST; do

                ENCPASS_BUCKET="$(basename "$ENCPASS_REMOVE_B")"
                if [ ! -z "$2" ]; then
                    # Removing secrets for a specified bucket
                    # Allow globbing
                    # shellcheck disable=SC2027,SC2086
                    ENCPASS_REMOVE_LIST="$(ls -1p "$ENCPASS_REMOVE_B/"$2".enc" 2>/dev/null)"

                    if [ -z "$ENCPASS_REMOVE_LIST" ]; then
                        echo "Error: No secrets found for $2 in bucket $ENCPASS_BUCKET."
                        exit 1
                    fi

                    for ENCPASS_REMOVE_F in $ENCPASS_REMOVE_LIST; do
                        ENCPASS_SECRET="$2"
                        encpass_remove "$ENCPASS_REMOVE_F"
                    done
                else
                    # Removing a specified bucket
                    encpass_remove
                fi

            done
        else
            echo "Error: The bucket named $1 does not exist."
            exit 1
        fi
        ;;
    show )
        shift
        encpass_checks
        if [ -z "$1" ]; then
            ENCPASS_SHOW_DIR="*"
        else
            ENCPASS_SHOW_DIR=$1
        fi

        if [ ! -z "$2" ]; then
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            if [ -f "$(encpass_get_abs_filename "$ENCPASS_HOME_DIR/secrets/$ENCPASS_SHOW_DIR/"$2".enc")" ]; then
                encpass_show_secret "$ENCPASS_SHOW_DIR" "$2"
            fi
        else
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_SHOW_LIST="$(ls -1d "$ENCPASS_HOME_DIR/secrets/"$ENCPASS_SHOW_DIR"" 2>/dev/null)"

            if [ -z "$ENCPASS_SHOW_LIST" ]; then
                if [ "$ENCPASS_SHOW_DIR" = "*" ]; then
                    echo "Error: No buckets exist."
                else
                    echo "Error: Bucket $1 does not exist."
                fi
                exit 1
            fi

            for ENCPASS_SHOW_F in $ENCPASS_SHOW_LIST; do
                ENCPASS_SHOW_DIR="$(basename "$ENCPASS_SHOW_F")"
                echo "$ENCPASS_SHOW_DIR:"
                encpass_show_secret "$ENCPASS_SHOW_DIR"
                echo " "
            done
        fi
        ;;
    ls|list )
        shift
        encpass_checks
        if [ ! -z "$1" ]; then
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_FILE_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"

            if [ -z "$ENCPASS_FILE_LIST" ]; then
                # Allow globbing
                # shellcheck disable=SC2027,SC2086
                ENCPASS_DIR_EXISTS="$(ls -d "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
                if [ ! -z "$ENCPASS_DIR_EXISTS" ]; then
                    echo "Bucket $1 is empty."
                else
                    echo "Error: Bucket $1 does not exist."
                fi
                exit 1
            fi

            ENCPASS_NL=""
            for ENCPASS_F in $ENCPASS_FILE_LIST; do
                if [ -d "${ENCPASS_F%:}" ]; then
                    printf "$ENCPASS_NL%s\n" "$(basename "$ENCPASS_F")"
                    ENCPASS_NL="\n"
                else
                    printf "%s\n" "$(basename "$ENCPASS_F" .enc)"
                fi
            done
        else
            # Allow globbing
            # shellcheck disable=SC2027,SC2086
            ENCPASS_BUCKET_LIST="$(ls -1p "$ENCPASS_HOME_DIR/secrets/"$1"" 2>/dev/null)"
            for ENCPASS_C in $ENCPASS_BUCKET_LIST; do
                if [ -d "${ENCPASS_C%:}" ]; then
                    printf "\n%s" "\n$(basename "$ENCPASS_C")"
                else
                    basename "$ENCPASS_C" .enc
                fi
            done
        fi
        ;;
    lock )
        shift
        encpass_checks

        echo "************************!!!WARNING!!!*************************" >&2
        echo "* You are about to lock your keys with a password.           *" >&2
        echo "* You will not be able to use your secrets again until you   *" >&2
        echo "* unlock the keys with the same password. It is important    *" >&2
        echo "* that you securely store the password, so you can recall it *" >&2
        echo "* in the future.  If you forget your password you will no    *" >&2
        echo "* longer be able to access your secrets.                     *" >&2
        echo "************************!!!WARNING!!!*************************" >&2

        printf "\n%s\n" "About to lock keys held in directory $ENCPASS_HOME_DIR/keys/"

        printf "\nEnter Password to lock keys:" >&2
        stty -echo
        read -r ENCPASS_KEY_PASS
        printf "\nConfirm Password:" >&2
        read -r ENCPASS_CKEY_PASS
        printf "\n"
        stty echo

        if [ -z "$ENCPASS_KEY_PASS" ]; then
            echo "Error: You must supply a password value."
            exit 1
        fi

        if [ "$ENCPASS_KEY_PASS" = "$ENCPASS_CKEY_PASS" ]; then
            ENCPASS_NUM_KEYS_LOCKED=0
            ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)"
            for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do

                if [ -d "${ENCPASS_KEY_F%:}" ]; then
                    ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")"
                    ENCPASS_KEY_VALUE=""
                    if [ -f "$ENCPASS_KEY_F/private.key" ]; then
                        ENCPASS_KEY_VALUE="$(cat "$ENCPASS_KEY_F/private.key")"
                        if [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then
                        echo "Locking key $ENCPASS_KEY_NAME..."
                        else
                          echo "Error: The key $ENCPASS_KEY_NAME appears to have been previously locked."
                            echo "       The current key file may hold a bad value. Exiting to avoid encrypting"
                            echo "       a bad value and overwriting the lock file."
                            exit 1
                        fi
                    else
                        echo "Error: Private key file ${ENCPASS_KEY_F}private.key missing for bucket $ENCPASS_KEY_NAME."
                        exit 1
                    fi
                    if [ ! -z "$ENCPASS_KEY_VALUE" ]; then
                        openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -salt -in "$ENCPASS_KEY_F/private.key" -out "$ENCPASS_KEY_F/private.lock" -k "$ENCPASS_KEY_PASS"
                        if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then
                            # Both the key and lock file exist.  We can remove the key file now
                            rm -f "$ENCPASS_KEY_F/private.key"
                            echo "Locked key $ENCPASS_KEY_NAME."
                            ENCPASS_NUM_KEYS_LOCKED=$(( ENCPASS_NUM_KEYS_LOCKED + 1 ))
                        else
                            echo "Error: The key fle and/or lock file were not found as expected for key $ENCPASS_KEY_NAME."
                        fi
                    else
                        echo "Error: No key value found for the $ENCPASS_KEY_NAME key."
                        exit 1
                    fi
                fi
            done
            echo "Locked $ENCPASS_NUM_KEYS_LOCKED keys."
        else
            echo "Error: Passwords do not match."
        fi
        ;;
    unlock )
        shift
        encpass_checks

        printf "%s\n" "About to unlock keys held in the $ENCPASS_HOME_DIR/keys/ directory."

        printf "\nEnter Password to unlock keys: " >&2
        stty -echo
        read -r ENCPASS_KEY_PASS
        printf "\n"
        stty echo

        if [ ! -z "$ENCPASS_KEY_PASS" ]; then
            ENCPASS_NUM_KEYS_UNLOCKED=0
            ENCPASS_KEYS_LIST="$(ls -1d "$ENCPASS_HOME_DIR/keys/"*"/" 2>/dev/null)"
            for ENCPASS_KEY_F in $ENCPASS_KEYS_LIST; do

                if [ -d "${ENCPASS_KEY_F%:}" ]; then
                    ENCPASS_KEY_NAME="$(basename "$ENCPASS_KEY_F")"
                    echo "Unlocking key $ENCPASS_KEY_NAME..."
                    if [ -f "$ENCPASS_KEY_F/private.key" ] && [ ! -f "$ENCPASS_KEY_F/private.lock" ]; then
                        echo "Error: Key $ENCPASS_KEY_NAME appears to be unlocked already."
                        exit 1
                    fi

                    if [ -f "$ENCPASS_KEY_F/private.lock" ]; then
                        # Remove the failed file in case previous decryption attempts were unsuccessful
                        rm -f "$ENCPASS_KEY_F/failed" 2>/dev/null

                        # Decrypt key. Log any failure to the "failed" file.
                        openssl enc -aes-256-cbc -d -pbkdf2 -iter 10000 -salt \
                            -in "$ENCPASS_KEY_F/private.lock" -out "$ENCPASS_KEY_F/private.key" \
                            -k "$ENCPASS_KEY_PASS" 2>&1 | encpass_save_err "$ENCPASS_KEY_F/failed"

                        if [ ! -f "$ENCPASS_KEY_F/failed" ]; then
                            # No failure has occurred.
                          if [ -f "$ENCPASS_KEY_F/private.key" ] && [ -f "$ENCPASS_KEY_F/private.lock" ]; then
                              # Both the key and lock file exist.  We can remove the lock file now.
                              rm -f "$ENCPASS_KEY_F/private.lock"
                              echo "Unlocked key $ENCPASS_KEY_NAME."
                              ENCPASS_NUM_KEYS_UNLOCKED=$(( ENCPASS_NUM_KEYS_UNLOCKED + 1 ))
                          else
                              echo "Error: The key file and/or lock file were not found as expected for key $ENCPASS_KEY_NAME."
                          fi
                        else
                          printf "Error: Failed to unlock key %s.\n" "$ENCPASS_KEY_NAME"
                            printf "       Please view %sfailed for details.\n" "$ENCPASS_KEY_F"
                        fi
                    else
                        echo "Error: No lock file found for the $ENCPASS_KEY_NAME key."
                    fi
                fi
            done
            echo "Unlocked $ENCPASS_NUM_KEYS_UNLOCKED keys."
        else
            echo "No password entered."
        fi
        ;;
    dir )
        shift
        encpass_checks
        echo "ENCPASS_HOME_DIR=$ENCPASS_HOME_DIR"
        ;;
    help|--help|usage|--usage|\? )
        encpass_checks
        encpass_help
        ;;
    * )
        if [ ! -z "$1" ]; then
            echo "Command not recognized. See \"encpass.sh help\" for a list commands."
            exit 1
        fi
        ;;
esac
0
Alexander Nick

Il existe un moyen de stocker les mots de passe dans un script bash mais vous devez crypter et obscurcir le script afin que personne ne puisse le lire ou exécuter n'importe quel type de débogueur dessus pour voir exactement ce qu'il fait. Pour chiffrer et obscurcir un script bash/Shell et qu'il soit réellement exécutable, essayez de le copier et de le coller ici:

http://www.kinglazy.com/Shell-script-encryption-kinglazy-shieldx.htm

Sur la page ci-dessus, tout ce que vous avez à faire est de soumettre votre script (vous pouvez d'abord soumettre un exemple de script pour votre tranquillité d'esprit). Un fichier Zip sera généré pour vous. Faites un clic droit sur le lien de téléchargement et copiez l'URL qui vous est fournie. Ensuite, accédez à votre boîte UNIX et effectuez les étapes suivantes.

Installation:

  1. wget lien vers le fichier Zip

  2. décompressez le fichier Zip nouvellement téléchargé

  3. cd/tmp/KingLazySHIELD

  4. ./install.sh/var/tmp/KINGLAZY/SHIELDX- (votre-nom-de-script)/home/(votre-nom-d'utilisateur) -force

Ce que la commande d'installation ci-dessus fera pour vous:

  1. Il installera la version chiffrée de votre script dans le répertoire/var/tmp/KINGLAZY/SHIELDX- (votre-nom-script).

  2. Il placera un lien vers ce script crypté dans le répertoire que vous spécifiez en remplacement de/home/(votre-nom d'utilisateur) - de cette façon, il vous permet d'accéder facilement au script sans avoir à taper le chemin absolu.

  3. Garantit que PERSONNE ne peut modifier le script - Toute tentative de modification du script crypté le rendra inutilisable ... jusqu'à ce que ces tentatives soient arrêtées ou supprimées. Il peut même être configuré pour vous avertir chaque fois que quelqu'un essaie de faire autre chose avec le script que de l'exécuter ... c'est-à-dire. tentatives de piratage ou de modification.

  4. Garantit que personne ne peut en faire de copie. Personne ne peut copier votre script dans un endroit isolé et essayer de le contourner pour voir comment cela fonctionne. Toutes les copies du script doivent être des liens vers l'emplacement d'origine que vous avez spécifié lors de l'installation (étape 4).

REMARQUE:

Je ne pense pas que cela fonctionne pour les scripts interactifs qui invitent l'utilisateur à répondre. Les valeurs doivent être codées en dur dans le script. Le chiffrement garantit que personne ne peut réellement voir ces valeurs, vous n'avez donc pas à vous en soucier.

0
AncientMinds

Une autre solution, sans égard à la sécurité (je pense aussi qu'il vaut mieux conserver les informations d'identification dans un autre fichier ou dans une base de données) est de crypter le mot de passe avec gpg et de l'insérer dans le script.

J'utilise une paire de clés gpg sans mot de passe que je garde dans une clé USB. (Remarque: lorsque vous exportez cette paire de clés, n'utilisez pas --armor, exportez-les au format binaire).

Cryptez d'abord votre mot de passe:

echo -n "pAssw0rd" | gpg --armor --no-default-keyring --keyring /media/usb/key.pub --recipient [email protected] --encrypt

Ce sera imprimé le mot de passe crypté gpg dans la sortie standard. Copiez l'intégralité du message et ajoutez-le au script:

password=$(gpg --batch --quiet --no-default-keyring --secret-keyring /media/usb/key.priv --decrypt <<EOF 
-----BEGIN PGP MESSAGE-----

hQEMA0CjbyauRLJ8AQgAkZT5gK8TrdH6cZEy+Ufl0PObGZJ1YEbshacZb88RlRB9
h2z+s/Bso5HQxNd5tzkwulvhmoGu6K6hpMXM3mbYl07jHF4qr+oWijDkdjHBVcn5
0mkpYO1riUf0HXIYnvCZq/4k/ajGZRm8EdDy2JIWuwiidQ18irp07UUNO+AB9mq8
5VXUjUN3tLTexg4sLZDKFYGRi4fyVrYKGsi0i5AEHKwn5SmTb3f1pa5yXbv68eYE
lCVfy51rBbG87UTycZ3gFQjf1UkNVbp0WV+RPEM9JR7dgR+9I8bKCuKLFLnGaqvc
beA3A6eMpzXQqsAg6GGo3PW6fMHqe1ZCvidi6e4a/dJDAbHq0XWp93qcwygnWeQW
Ozr1hr5mCa+QkUSymxiUrRncRhyqSP0ok5j4rjwSJu9vmHTEUapiyQMQaEIF2e2S
/NIWGg==
=uriR
-----END PGP MESSAGE-----
EOF)

De cette façon, uniquement si l'USB est monté dans le système, le mot de passe peut être déchiffré. Bien sûr, vous pouvez également importer les clés dans le système (moins sécurisées ou pas de sécurité du tout) ou vous pouvez protéger la clé privée avec un mot de passe (de sorte qu'elle ne peut pas être automatisée).

0
Juanu