【问题标题】:Hide/encrypt password in bash file to stop accidentally seeing it在 bash 文件中隐藏/加密密码以防止意外看到它
【发布时间】:2010-07-23 14:04:13
【问题描述】:

对不起,如果之前有人问过这个问题,我确实检查过但找不到任何东西......

Unix 中是否有一个函数可以加密 解密批处理文件中的密码,以便我可以将其通过管道传输到 bash 文件中的其他命令中?

我意识到这样做并不能提供真正的安全性,如果有人在我的肩膀上查看脚本,更能阻止他们意外看到密码:)

我在 Red Hat 5.3 上运行。

我有一个类似这样的脚本:

serverControl.sh -u admin -p myPassword -c shutdown

我想做这样的事情:

password = decrypt("fgsfkageaivgea", "aDecryptionKey")
serverControl.sh -u admin -p $password -c shutdown

这不会以任何方式保护密码,但确实可以防止有人从我的肩膀上意外看到它。

【问题讨论】:

  • 您能解释一下吗?

标签: security bash unix passwords


【解决方案1】:

OpenSSL 提供了一个passwd 命令,它可以加密但不能解密,因为它只做哈希。您还可以下载 aesutil 之类的内容,以便使用功能强大且广为人知的对称加密例程。

例如:

#!/bin/sh    
# using aesutil
SALT=$(mkrand 15) # mkrand generates a 15-character random passwd
MYENCPASS="i/b9pkcpQAPy7BzH2JlqHVoJc2mNTBM=" # echo "passwd" | aes -e -b -B -p $SALT 
MYPASS=$(echo "$MYENCPASS" | aes -d -b -p $SALT)

# and usage
serverControl.sh -u admin -p $MYPASS -c shutdown

【讨论】:

  • 我认为推荐使用$(echo $FOO) 样式而不是`echo $FOO` 样式。
  • 我做了一些搜索,似乎链接仍然有效,但目标站点当前配置错误:(。
  • 这正是我所需要的!除了......人们运行 decode 命令来获取明文密码是微不足道的。这可能是一个愚蠢的问题,但有没有办法阻止用户获取密码?
【解决方案2】:

我使用 base64 来解决同样的问题,即人们可以看到我的密码。

这就是我所做的 - 我创建了一个新的“db_auth.cfg”文件并创建了参数,其中一个是我的数据库密码。我将文件的权限设置为 750。

DB_PASSWORD=Z29vZ2xl

在我的 shell 脚本中,我使用“source”命令获取文件,然后将其解码回用于我的脚本中。

source path_to_the_file/db_auth.cfg
DB_PASSWORD=$(eval echo ${DB_PASSWORD} | base64 --decode)

我希望这会有所帮助。

【讨论】:

  • 如上所述,编码与加密不同。虽然您可能混淆了密码并阻止了随意的肩膀冲浪,但很容易恢复:您的 DB_PASSWORD 是“google”。
【解决方案3】:

虽然这不是一个内置的 Unix 解决方案,但我已经使用一个 shell 脚本实现了一个解决方案,该脚本可以包含在您正在使用的任何 shell 脚本中。这可用于符合 POSIX 的设置。 (sh, bash, ksh, zsh) 完整的描述在 github repo -> https://github.com/plyint/encpass.sh。此解决方案将为您的脚本自动生成密钥,并将密钥和密码(或其他机密)存储在您用户下的隐藏目录中(即 ~/.encpass)。

在您的脚本中,您只需获取 encpass.sh 的源代码,然后调用 get_secret 方法。例如:

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

下面粘贴的是 encpass.sh 的精简版代码(您可以在 github 上获得完整版本)以便于查看:

 #!/bin/sh
 ################################################################################
 # Copyright (c) 2020 Plyint, LLC <contact@plyint.com>. 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() {
    [ -n "$ENCPASS_CHECKS" ] && return

    if [ -z "$ENCPASS_HOME_DIR" ]; then
        ENCPASS_HOME_DIR="$HOME/.encpass"
    fi
    [ ! -d "$ENCPASS_HOME_DIR" ] && mkdir -m 700 "$ENCPASS_HOME_DIR"

    if [ -f "$ENCPASS_HOME_DIR/.extension" ]; then
        # Extension enabled, load it...
        ENCPASS_EXTENSION="$(cat "$ENCPASS_HOME_DIR/.extension")"
        ENCPASS_EXT_FILE="encpass-$ENCPASS_EXTENSION.sh"
        if [ -f "./extensions/$ENCPASS_EXTENSION/$ENCPASS_EXT_FILE" ]; then
            # shellcheck source=/dev/null
          . "./extensions/$ENCPASS_EXTENSION/$ENCPASS_EXT_FILE"
        elif [ ! -z "$(command -v encpass-"$ENCPASS_EXTENSION".sh)" ]; then 
            # shellcheck source=/dev/null
            . "$(command -v encpass-$ENCPASS_EXTENSION.sh)"
        else
            encpass_die "Error: Extension $ENCPASS_EXTENSION could not be found."
        fi

        # Extension specific checks, mandatory function for extensions
        encpass_"${ENCPASS_EXTENSION}"_checks
    else
        # Use default OpenSSL implementation
        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

        [ ! -d "$ENCPASS_HOME_DIR/keys" ] && mkdir -m 700 "$ENCPASS_HOME_DIR/keys"
        [ ! -d "$ENCPASS_HOME_DIR/secrets" ] && mkdir -m 700 "$ENCPASS_HOME_DIR/secrets"
        [ ! -d "$ENCPASS_HOME_DIR/exports" ] && mkdir -m 700 "$ENCPASS_HOME_DIR/exports"

    fi

   ENCPASS_CHECKS=1
 }

 # Checks if the enabled extension has implented the passed function and if so calls it
 encpass_ext_func() {
   [ ! -z "$ENCPASS_EXTENSION" ] && ENCPASS_EXT_FUNC="$(command -v "encpass_${ENCPASS_EXTENSION}_$1")" || return
    [ ! -z "$ENCPASS_EXT_FUNC" ] && shift && $ENCPASS_EXT_FUNC "$@" 
 }

 # Initializations performed when the script is included by another script
 encpass_include_init() {
    encpass_ext_func "include_init" "$@"
    [ ! -z "$ENCPASS_EXT_FUNC" ] && return

    if [ -n "$1" ] && [ -n "$2" ]; then
        ENCPASS_BUCKET=$1
        ENCPASS_SECRET_NAME=$2
    elif [ -n "$1" ]; then
        if [ -z "$ENCPASS_BUCKET" ]; then
          ENCPASS_BUCKET=$(basename "$0")
        fi
        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"

    [ ! -d "$ENCPASS_KEY_DIR" ] && mkdir -m 700 "$ENCPASS_KEY_DIR"

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

 encpass_set_private_key_abs_name() {
    ENCPASS_PRIVATE_KEY_ABS_NAME="$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key"
    [ ! -n "$1" ] && [ ! -f "$ENCPASS_PRIVATE_KEY_ABS_NAME" ] && encpass_generate_private_key
 }

 encpass_set_secret_abs_name() {
    ENCPASS_SECRET_ABS_NAME="$ENCPASS_HOME_DIR/secrets/$ENCPASS_BUCKET/$ENCPASS_SECRET_NAME.enc"
    [ ! -n "$1" ] && [ ! -f "$ENCPASS_SECRET_ABS_NAME" ] && set_secret
 }

 encpass_rmfifo() {
    trap - EXIT
    kill "$1" 2>/dev/null
    rm -f "$2"
 }

 encpass_mkfifo() {
    fifo="$ENCPASS_HOME_DIR/$1.$$"
    mkfifo -m 600 "$fifo" || encpass_die "Error: unable to create named pipe"
    printf '%s\n' "$fifo"
 }

 get_secret() {
    encpass_checks
    encpass_ext_func "get_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return

    [ "$(basename "$0")" != "encpass.sh" ] && encpass_include_init "$1" "$2"

    encpass_set_private_key_abs_name
    encpass_set_secret_abs_name
    encpass_decrypt_secret "$@"
 }

 set_secret() {
    encpass_checks

    encpass_ext_func "set_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return

    if [ "$1" != "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

        # Use named pipe to securely pass secret to openssl
        fifo="$(encpass_mkfifo set_secret_fifo)"
    fi

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

        [ ! -d "$ENCPASS_SECRET_DIR" ] && mkdir -m 700 "$ENCPASS_SECRET_DIR"

        # Generate IV and create secret file
        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" > "$fifo" &
        # Allow expansion now so PID is set
        # shellcheck disable=SC2064
        trap "encpass_rmfifo $! $fifo" EXIT HUP TERM INT TSTP

        # Append encrypted secret to IV in the secret file
        openssl enc -aes-256-cbc -e -a -iv "$ENCPASS_OPENSSL_IV" \
            -K "$(cat "$ENCPASS_HOME_DIR/keys/$ENCPASS_BUCKET/private.key")" \
            -in "$fifo" 1>> "$ENCPASS_SECRET_DIR/$ENCPASS_SECRET_NAME.enc"
    else
        encpass_die "Error: secrets do not match.  Please try again."
    fi
 }

 encpass_decrypt_secret() {
    encpass_ext_func "decrypt_secret" "$@"; [ ! -z "$ENCPASS_EXT_FUNC" ] && return

    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 a locked or failed decrypt error.
            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
 }

 encpass_die() {
   echo "$@" >&2
   exit 1
 }
 #LITE

【讨论】:

    【解决方案4】:

    【讨论】:

    • 遗憾的是,我的系统上都没有(Red Hat 5.3 - 抱歉,我应该说)
    • Rich:你有root权限吗?如果是这样,您应该能够安装这些软件包,这些软件包应该适用于所有 Linux 发行版。
    【解决方案5】:

    另一种不考虑安全性的解决方案(我也认为最好将凭据保存在另一个文件或数据库中)是使用 gpg 加密密码并将其插入脚本中。

    我使用保存在 USB 中的无密码 gpg 密钥对。 (注意:导出此密钥对时不要使用--armor,以二进制格式导出)。

    首先加密你的密码:

    EDIT:在此命令前加一个空格,因此不会被 bash 历史记录。

    echo -n "pAssw0rd" | gpg --armor --no-default-keyring --keyring /media/usb/key.pub --recipient someone@mail.com --encrypt
    

    这将在标准输出中打印出 gpg 加密密码。复制整个消息并将其添加到脚本中:

    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)
    

    这样只有在系统中挂载了usb才能解密密码。当然你也可以将密钥导入系统(不太安全,或者根本没有安全性)或者你可以用密码保护私钥(所以它不能自动化)。

    【讨论】:

    • 永远不要echo你的密码,它将被写入你的.bash_history文件,所有加密它的努力都被丢弃了。
    • 我同意,这不是一个好主意。我认为一个可能的解决方案是在命令前放一个空格,这样命令不会被 bash 历史记录。
    【解决方案6】:
    • 从屏幕边缘缩进(假设您不使用换行并且编辑器宽度一致)

    • 将其存储在单独的文件中并读入。

    【讨论】:

    • 在文件中存储未加密的密码?这是一个很大的禁忌。
    • 加密或未加密在这里没有实际区别,问题是关于防止密码的过度发现。在问题的上下文中,加密密码无非是安全剧场,除非理解这里的加密不是一种有效的安全措施,否则表明它可能会提供一种危险和虚假的安全感。
    • @Ashton 对密码进行加密,使其保留在加密的静态文件中,这是一件安全的事情。如果加密算法使用了未包含在备份中的密钥,或者除了登录用户之外的任何人都无法访问。最近对密码文件和数据库的许多黑客攻击是加密文件和/或数据存储行中的敏感数据的直接结果。
    • 无用的解决方案。
    • +1 用于存储在另一个文件中(但缩进建议不好)。将密码存储在文件中比将经过混淆的密码放入脚本中要好得多。如果凭据在文件中,则可以传递脚本而无需考虑泄漏该信息。如果您愿意,即使 ssh 也允许您将私钥存储在未加密的文件中 - 未加密的私钥只是一个非常长的密码。
    【解决方案7】:

    有一种更方便的方法可以将密码存储在脚本中,但您必须对脚本进行加密和混淆处理,使其无法被读取。为了成功加密和混淆一个 shell 脚本并让该脚本真正可执行,请尝试在此处复制并粘贴它:

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

    在上面的页面上,您所要做的就是提交您的脚本并给脚本一个正确的名称,然后点击下载按钮。将为您生成一个 zip 文件。右键单击下载链接并复制您提供的 URL。然后,转到您的 UNIX 机器并执行以下步骤。

    安装:

    1. wget link-to-the-zip-file
    2. unzip the-newly-downloaded-zip-file
    3. cd /tmp/KingLazySHIELD
    4. ./install.sh /var/tmp/KINGLAZY/SHIELDX-(your-script-name) /home/(your-username) -force
    

    上面的安装命令将为您做的是:

    1. 在目录 /var/tmp/KINGLAZY/SHIELDX-(your-script-name) 中安装脚本的加密版本。
    2. 它会在您指定的任何目录中放置一个指向该加密脚本的链接,以替换 /home/(your-username) - 这样,您就可以轻松访问该脚本,而无需输入绝对路径。
    3. 确保没有人可以修改脚本 - 任何修改加密脚本的尝试都会使其无法操作...直到这些尝试被停止或删除。它甚至可以配置为在有人尝试对脚本执行任何操作而不是运行它时通知您...即黑客或修改尝试。
    4. 确保绝对没有人可以复制它。没有人可以将您的脚本复制到一个僻静的位置并尝试使用它来查看它是如何工作的。脚本的所有副本都必须链接到您在安装期间指定的原始位置(第 4 步)。

    注意:

    这不适用于提示并等待用户响应的交互式脚本。用户期望的值应该硬编码到脚本中。加密确保没有人可以实际看到这些值,因此您不必担心。

    关系:

    这篇文章中提供的解决方案回答了您的问题,因为它加密了包含您想要加密的密码的实际脚本。您可以按原样(未加密)保留密码,但密码所在的脚本被深度混淆和加密,您可以放心,没有人能够看到它。如果有人试图窥探脚本,您将收到有关它们的电子邮件通知。

    【讨论】:

    • 为什么会有人将他们的脚本可能包含他们的密码发送到您的网页?为什么不发布你的“KingLazySHIELD”(不幸的名字,因为听起来你只是想收集密码并且非常懒惰)源代码给大家看?这样就不会向可疑网站发送密码。 PS 任何有权访问的人都可以复制文件,这到底有什么作用?这个答案看起来就像一面巨大的红旗
    • 同意 Xen2050。危险就在这里!
    【解决方案8】:

    上述代码中的以下行不起作用

    DB_PASSWORD=$(eval echo ${DB_PASSWORD} | base64 --decode)
    

    正确的行是:

    DB_PASSWORD=`echo $PASSWORD|base64 -d`
    

    并将密码另存为PASSWORD。

    【讨论】:

    • 这在一般情况下是不正确的。 非常 sh 的旧实现可能不支持现代 $(command substitution) 语法,但它已经成为 POSIX 的一部分,好几十年了。 (但顺便说一句,上面的两个例子都是不正确的,因为没有正确引用你传递给echo的字符串。)
    • 不要说简单行不通,你应该指出在sh的一些旧版本中不支持$(cmd)语法。
    猜你喜欢
    • 2017-12-17
    • 2019-07-30
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多