วิธีรับรหัสผ่านจากเชลล์สคริปต์โดยไม่ต้องสะท้อน


424

ฉันมีสคริปต์ที่ทำให้กระบวนการที่ต้องการเข้าถึงระบบป้องกันด้วยรหัสผ่านโดยอัตโนมัติ ระบบสามารถเข้าถึงได้ผ่านโปรแกรมบรรทัดคำสั่งที่ยอมรับรหัสผ่านผู้ใช้เป็นอาร์กิวเมนต์

ฉันต้องการให้ผู้ใช้พิมพ์รหัสผ่านกำหนดให้กับตัวแปร shell จากนั้นใช้ตัวแปรนั้นเพื่อสร้างบรรทัดคำสั่งของโปรแกรมที่เข้าถึง (ซึ่งแน่นอนจะสร้างเอาต์พุตสตรีมที่ฉันจะประมวลผล)

ฉันเป็นโปรแกรมเมอร์เชลล์ที่มีความสามารถพอสมควรใน Bourne / Bash แต่ฉันไม่รู้วิธีรับอินพุตของผู้ใช้โดยไม่ต้องสะท้อนไปยังเทอร์มินัล

มีใครช่วยได้บ้าง



PSA : ทำงานเสมอreadกับ-rข้อโต้แย้งเมื่อมีการอ่านรหัสผ่าน มิฉะนั้นแบ็กสแลชอาจหายไปได้ ไม่มีคำตอบพูดถึงเรื่องนี้ คำตอบที่ได้รับการยอมรับสำหรับคำถามที่ซ้ำกันครอบคลุมถึงกรณีนี้และกรณีขอบเพิ่มเติม เช่นการตั้งค่าIFS=เพื่อไม่ให้ช่องว่างต่อท้ายหลวม
bodo

คำตอบ:


617

นี่เป็นอีกวิธีในการทำ:

#!/bin/bash
# Read Password
echo -n Password: 
read -s password
echo
# Run Command
echo $password

read -sจะปิดเสียงก้องสำหรับคุณ เพียงแค่แทนที่echoบรรทัดสุดท้ายด้วยคำสั่งที่คุณต้องการเรียกใช้


85
เชลล์บางตัวอนุญาตให้คุณระบุพรอมต์สำหรับreadคำสั่ง:read -s -p "Password:" password
Gordon Davisson

35
โปรดทราบว่าไม่ได้read -sอยู่ใน POSIX สคริปต์ของคุณขึ้นอยู่กับการทุบตีถ้าคุณใช้มัน หากคุณต้องการที่จะ POSIX สอดคล้องคุณควรใช้stty -echoวิธีการแก้ปัญหาด้านล่างเพราะsttyและechoพารามิเตอร์ที่กำหนดไว้ใน POSIX
scy

8
โอ้และecho -nไม่ได้อยู่ใน POSIX เช่นกัน ใช้printfแทน
scy

4
ตามความพยายามของฉัน: ใช้งานได้เฉพาะกับ/bin/bashและไม่ใช้/bin/shเพื่อให้ชัดเจน
Boris Däppen

1
ฉันติดตามมันด้วยecho "$REPLY" | sed -r 's/./*/g'(หรือแม้แต่จำนวนเครื่องหมายดอกจันโดยพลการ) แทนค่าว่างechoเพื่อให้พวกเขารู้ว่ามีการป้อนรหัสผ่านของพวกเขาแล้ว (ถ้าพวกเขาพิมพ์หนึ่งฉันมีรหัสผ่านเริ่มต้นหากพวกเขาเลือกที่จะข้ามผ่านพรอมต์ของฉันโดยเฉพาะ สคริปต์)
Hashbrown

204

คำตอบที่สอดคล้องกับ POSIX ขอให้สังเกตการใช้แทน/bin/sh /bin/bash(ใช้ได้กับ bash แต่ไม่ต้องการ bash)

#!/bin/sh
stty -echo
printf "Password: "
read PASSWORD
stty echo
printf "\n"

2
แต่เดิมยอมรับว่าเป็นทางออกที่ดีที่สุดและใช้ในสคริปต์ที่ฉันเขียน แต่รหัสผ่าน "read -s -p": "รหัสผ่าน" ดูเหมือนง่ายกว่ามาก
BD ที่ Rivenhill

48
ไม่มีจริงๆทำใช้sttyถ้าคุณต้องการที่จะตาม POSIX รหัสในคำตอบนี้ทำงานได้อย่างสมบูรณ์แบบแม้กระทั่งใน bash แต่จริงๆแล้วในเชลล์ทั้งหมดที่สอดคล้องกับ POSIX
scy

2
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ stty ทำ: stackoverflow.com/questions/22832933/…
yvanscher

5
น่าเสียดายที่การทำลายด้วย CTRL + C หลังจากที่ stty -echo เกิดขึ้นจะทำให้เครื่องของคุณเสีย การจับข้อผิดพลาดที่ดีกว่าอาจจำเป็นที่นี่
PypeBros

3
ดูเหมือนว่าดีกว่าสำหรับการอ่านเพื่อจัดการการซ่อนรหัสผ่านถ้าเป็นไปได้ echo -n "Password: " && read -s password || { stty -echo; read password; stty echo; }
TamusJRoyce

89

หนึ่งในสายการบิน:

read -s -p "Password: " password

ภายใต้ Linux (และ cygwin) แบบฟอร์มนี้ทำงานใน bash และ sh มันอาจจะไม่ใช่ Unix sh มาตรฐาน

สำหรับข้อมูลเพิ่มเติมและตัวเลือกใน bash ให้พิมพ์ "help read"

$ help read
read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
Read a line from the standard input and split it into fields.
  ...
  -p prompt output the string PROMPT without a trailing newline before
            attempting to read
  ...
  -s                do not echo input coming from a terminal

4
หากคำสั่งสร้าง "อ่าน: ตัวเลือกที่ผิดกฎหมาย -s" หมายความว่าสคริปต์ต้องถูกเรียกใช้งานโดยตรง (./script vs sh ./script) ... ดูที่stackoverflow.com/questions/30554353/
shao.lo

@ shao.lo ไม่เพียงหมายความว่าคุณต้องใช้ bash ไม่ใช่ sh ความคิดเห็นของคุณนำไปใช้เฉพาะในกรณีที่ส่วนหัว shebang ในสคริปต์กำลังใช้ bash
Joe Coder

59

-sตัวเลือกของการreadไม่ได้ถูกกำหนดไว้ในมาตรฐาน POSIX ดูhttp://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlฉันต้องการบางอย่างที่ใช้งานได้กับ POSIX เชลล์ดังนั้นฉันจึงเขียนฟังก์ชั่นเล็ก ๆ น้อย ๆ ที่ใช้sttyเพื่อปิดเสียงสะท้อน

#!/bin/sh

# Read secret string
read_secret()
{
    # Disable echo.
    stty -echo

    # Set up trap to ensure echo is enabled before exiting if the script
    # is terminated while echo is disabled.
    trap 'stty echo' EXIT

    # Read secret.
    read "$@"

    # Enable echo.
    stty echo
    trap - EXIT

    # Print a newline because the newline entered by the user after
    # entering the passcode is not echoed. This ensures that the
    # next line of output begins at a new line.
    echo
}

ฟังก์ชั่นนี้ทำงานค่อนข้างคล้ายกับreadคำสั่ง นี่คือการใช้งานที่เรียบง่ายreadตามด้วยการใช้งานที่คล้ายกันของread_secretตามการใช้งานของที่คล้ายกันอินพุตที่จะread_secretปรากฏว่างเปล่าเนื่องจากไม่ได้ถูกสะท้อนไปยังเทอร์มินัล

[susam@cube ~]$ read a b c
foo \bar baz \qux
[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=bar c=baz qux
[susam@cube ~]$ unset a b c
[susam@cube ~]$ read_secret a b c

[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=bar c=baz qux
[susam@cube ~]$ unset a b c

นี่คืออีกอย่างหนึ่งที่ใช้-rตัวเลือกในการรักษาแบ็กสแลชในอินพุต ใช้งานได้เนื่องจากread_secretฟังก์ชั่นที่กำหนดไว้ด้านบนผ่านการขัดแย้งทั้งหมดที่ได้รับไปยังreadคำสั่ง

[susam@cube ~]$ read -r a b c
foo \bar baz \qux
[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=\bar c=baz \qux
[susam@cube ~]$ unset a b c
[susam@cube ~]$ read_secret -r a b c

[susam@cube ~]$ echo a=$a b=$b c=$c
a=foo b=\bar c=baz \qux
[susam@cube ~]$ unset a b c

ในที่สุดนี่คือตัวอย่างที่แสดงวิธีการใช้read_secretฟังก์ชั่นเพื่ออ่านรหัสผ่านในลักษณะที่สอดคล้องกับ POSIX

printf "Password: "
read_secret password
# Do something with $password here ...

9
ควรตรวจสอบให้แน่ใจว่าเสียงก้องไม่ได้ถูกปิดใช้งานในเปลือกและไม่เปิดใช้งานหากไม่ได้มาก่อน ... สามารถใช้งานได้oldtty = stty -gแล้วในตอนท้ายstty $oldttyเพื่อเรียกคืนการตั้งค่าก่อนหน้า ไม่อย่างนั้นมันเยี่ยมมาก
Perkins

12

ฉันพบว่าเป็นaskpassคำสั่งที่มีประโยชน์

password=$(/lib/cryptsetup/askpass "Give a password")

อักขระอินพุตทุกตัวจะถูกแทนที่ด้วย * ดู: ให้รหัสผ่าน ****


3
ไหนนี้เป็น/lib/cryptsetup/askpassมาจากไหน? แน่นอนว่าไม่ใช่เครื่องมือมาตรฐาน * nix (หรือ GNU / Linux)
Daniel Serodio

มันเป็นทางออกที่ดีที่สุดที่ฉันเคยเห็น มาจาก cryptsetup ซึ่งเป็นมาตรฐานสำหรับการเข้ารหัสฮาร์ดไดรฟ์ sudo apt-get install cryptsetup.
con-f-use

ฉันพยายามใช้สิ่งนี้และดวงดาวก็ไม่ปรากฏขึ้นรวมทั้งทำให้เวลาของฉันใหญ่เทอะทะจนกว่าฉันจะปิดมัน อินพุตเชลล์ใด ๆ เพิ่มเติมจะถูกซ่อนไว้!
Jeff

1
มันทำงานได้ถ้าฉันผ่านรหัสผ่าน แต่ถ้าฉันพยายามที่จะยกเลิกและฆ่ามันด้วยความCtrl+Cยุ่งเหยิง terminal
Jakub Bochenski

ฉันใช้วิธีนี้เป็น oneliner เพื่อขอรหัสผ่านและเขียนลงในไฟล์ (ใช่ฉันรู้ว่าฉันทำอะไร ^^): /lib/cryptsetup/askpass "Give a password" > pass.txtมีประโยชน์มากขอบคุณ!
Seboudry


1

คุณยังสามารถขอรหัสผ่านโดยไม่ต้องตั้งค่าตัวแปรในเชลล์ปัจจุบันโดยทำสิ่งนี้:

$(read -s;echo $REPLY)

ตัวอย่างเช่น

my-command --set password=$(read -sp "Password: ";echo $REPLY)

คุณสามารถเพิ่มค่าที่ได้รับการแจ้งเตือนเหล่านี้ด้วยการขึ้นบรรทัดใหม่โดยทำดังนี้

my-command --set user=$(read -sp "`echo $'\n '`User: ";echo $REPLY) --set password=$(read -sp "`echo $'\n '`Password: ";echo $REPLY)

1

สำหรับทุกคนที่ต้องการให้ใส่รหัสผ่านคุณอาจสนใจใช้encpass.sh encpass.shนี่เป็นสคริปต์ที่ฉันเขียนขึ้นเพื่อจุดประสงค์คล้ายกันในการเก็บข้อมูลความลับขณะใช้งานจริงและจากนั้นทำการเข้ารหัสเพื่อใช้ในภายหลัง การเรียกใช้ครั้งต่อไปจะไม่พร้อมท์รหัสผ่านเนื่องจากจะใช้ค่าที่เข้ารหัสจากดิสก์

มันเก็บรหัสผ่านที่เข้ารหัสไว้ในโฟลเดอร์ที่ซ่อนอยู่ภายใต้ไดเรกทอรีบ้านของผู้ใช้หรือในโฟลเดอร์ที่กำหนดเองที่คุณสามารถกำหนดผ่านตัวแปรสภาพแวดล้อม ENCPASS_HOME_DIR มันถูกออกแบบมาให้สอดคล้องกับ POSIX และมีใบอนุญาต MIT ดังนั้นจึงสามารถใช้ได้แม้ในสภาพแวดล้อมขององค์กร Plyint LLCบริษัท ของฉันดูแลสคริปต์และเผยแพร่การปรับปรุงเป็นครั้งคราว ยินดีต้อนรับคำขอดึงเช่นกันหากคุณพบปัญหา :)

หากต้องการใช้งานในสคริปต์ของคุณเพียงแค่ source encpass.sh ในสคริปต์ของคุณและเรียกใช้ฟังก์ชัน get_secret ฉันรวมถึงสำเนาของสคริปต์ด้านล่างเพื่อให้ง่ายต่อการมองเห็น

#!/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() {
    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

ลิงค์นี้ช่วยในการกำหนด * วิธีอ่านรหัสผ่านจากการใช้งานโดยไม่ต้องสะท้อนกลับไปที่เทอร์มินัล * วิธีการแทนที่อักขระแต่ละตัวด้วย * -character

https://www.tutorialkart.com/bash-shell-scripting/bash-read-username-and-password/


-3

ก่อนอื่นถ้าใครจะเก็บรหัสผ่านในไฟล์ฉันจะตรวจสอบให้แน่ใจว่ามันถูกแฮช ไม่ใช่ความปลอดภัยที่ดีที่สุด แต่อย่างน้อยมันจะไม่เป็นข้อความธรรมดา

  1. ก่อนอื่นให้สร้างรหัสผ่านและแฮช:

    echo "password123" | md5sum  | cut -d '-' -f 1 > /tmp/secret
  2. ตอนนี้สร้างโปรแกรมของคุณเพื่อใช้แฮช ในกรณีนี้โปรแกรมเล็ก ๆ นี้จะรับรหัสผ่านของผู้ใช้โดยไม่มีการสะท้อนจากนั้นแปลงเป็นแฮชเพื่อเปรียบเทียบกับแฮชที่เก็บไว้ หากตรงกับแฮชที่เก็บไว้การเข้าถึงจะได้รับ:

    #!/bin/bash
    
    PASSWORD_FILE="/tmp/secret"
    MD5_HASH=$(cat /tmp/secret)
    PASSWORD_WRONG=1
    
    
    while [ $PASSWORD_WRONG -eq 1 ]
     do
        echo "Enter your password:"
        read -s ENTERED_PASSWORD
        if [ "$MD5_HASH" != "$(echo $ENTERED_PASSWORD | md5sum | cut -d '-' -f 1)" ]; then
            echo "Access Deniend: Incorrenct password!. Try again"
        else
            echo "Access Granted"
            PASSWORD_WRONG=0
        fi
    done

1
ฉันขอแนะนำให้รักษาอักขระขึ้นบรรทัดใหม่ซึ่งเป็น\nผลมาจากechoคำสั่งออกจากการแฮชโดยใช้echo -nแทนเนื่องจาก\nไม่ได้เป็นส่วนหนึ่งของรหัสผ่านที่ให้มาแน่นอนว่าทั้งการสร้าง/tmp/secretและการเปรียบเทียบที่ตามมาในตัวอย่างของคุณ
Nico Rittner

คำถามไม่ได้พูดอะไรเกี่ยวกับการบันทึกไฟล์
สิทธิ์ฟอสเตอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.