การซ่อนอินพุตของผู้ใช้บนเทอร์มินัลในสคริปต์ Linux


121

ฉันมีสคริปต์ทุบตีดังต่อไปนี้:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

ฉันต้องการให้เมื่อผู้ใช้พิมพ์รหัสผ่านบนเทอร์มินัลไม่ควรแสดง (หรือบางอย่างเช่น *******)) ฉันจะบรรลุเป้าหมายนี้ได้อย่างไร?


2
หมายเหตุทั่วไปเพียงเพื่อป้องกันความสับสน: ชื่อผู้ใช้ / รหัสผ่านนี้ไม่มีส่วนเกี่ยวข้องกับชื่อผู้ใช้ / รหัสผ่านของ linux - ฉันกำลังมองหาวิธีซ่อนข้อมูลที่ผู้ใช้พิมพ์ระหว่าง "อ่านรหัสผ่าน"

ฉันได้เพิ่มการอัปเดตสำหรับหากคุณต้องการจินตนาการโดยการส่งออก*ในขณะที่พวกเขาพิมพ์รหัสผ่าน
SiegeX

ขอบคุณมาก. คำถามหนึ่งหากมีคนรู้ - สิ่งนี้จะป้องกันไม่ให้เข้าสู่. bash_history โดยอัตโนมัติหรือไม่?


2
ฉันไม่คิดว่ามันจะ bash_history จับเฉพาะคำสั่งของคุณสิ่งที่เกิดขึ้นหลังจากเรียกใช้คำสั่งของคุณมันจะไม่จับภาพ
Andreas Wong

คำตอบ:


272

เพียงแค่ใส่ -s ให้กับการโทรเพื่ออ่านของคุณดังนี้:

$ read -s PASSWORD
$ echo $PASSWORD

9
ฉันชอบทางนี้ดีกว่า ฉันจะโหวตให้คุณถ้าฉันไม่ถึงขีด จำกัด รายวันของฉัน
SiegeX

4
เพื่อให้บริบท: ไม่-sแสดงอะไรเลยในขณะที่พิมพ์อินพุต ( -sเป็นส่วนขยายที่ไม่ใช่ POSIX ดังนั้นเชลล์บางตัวจึงไม่รองรับเช่นashเชลล์ที่มาพร้อมกับ BusyBox ใช้ssty -echoวิธีการในเชลล์ดังกล่าว)
mklement0

3
@electron ไม่มีอะไรในmanหน้านี้จริง ๆ ที่แนะนำให้-sตั้งค่าสีข้อความให้เหมือนกับสีพื้นหลัง มันก็แค่ "สะท้อนอย่างเงียบ ๆ " ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Andreas Wong

2
@electron ที่ฉันทดสอบบนกล่องของฉันไม่เพียง แต่มันจะไม่ขยับเคอร์เซอร์เท่านั้นเมื่อฉันพยายามไฮไลต์บรรทัดที่ฉันพิมพ์รหัสผ่านฉันดูเหมือนจะวางไม่ได้ในภายหลัง ทั้งสองอย่างควรเกิดขึ้นหากสิ่งที่หนังสือกล่าวเป็นความจริง ฉันเดาว่าอาจจะเป็นรสชาติที่แตกต่างออกไป (ฉันใช้ bash 4.1.2 (1))
Andreas Wong

1
@DominykasMostauskis ใช่ แต่ฉลากมีไว้สำหรับทุบตีดังนั้นวิธีแก้ปัญหา อาจมีเปลือกหอยมากมายที่มันใช้ไม่ได้ :)
Andreas Wong

25

ปรับปรุง

ในกรณีที่คุณต้องการจินตนาการโดยการแสดงผล*สำหรับตัวละครแต่ละตัวที่พิมพ์คุณสามารถทำสิ่งนี้ได้ (โดยใช้read -sโซลูชันของ Andreas ):

unset password;
while IFS= read -r -s -n1 pass; do
  if [[ -z $pass ]]; then
     echo
     break
  else
     echo -n '*'
     password+=$pass
  fi
done

โดยไม่ต้องแฟนซี

echo "Please enter your username";
read username;

echo "Please enter your password";
stty -echo
read password;
stty echo

มันควรจะเป็นIFS=$'\n'เพื่อให้คุณจริงสามารถเสร็จสิ้นการพิมพ์รหัสผ่าน เป็นอย่างอื่นดี; ฉลาดมาก.
ราศีพิจิก

2
ไม่จำเป็นต้องตั้งเพราะคั่นเริ่มต้นการอ่านคือIFS=$'\n' -d $'\n'if-statement ที่จะทำลายสตริง nul ซึ่งเกิดขึ้นในบรรทัดใหม่คือสิ่งที่ช่วยให้พวกเขาตั้งรหัสผ่านให้เสร็จสิ้น
SiegeX

ในการทดสอบบน FreeBSD ด้วย bash 3.x ฉันพบว่าไม่เป็นเช่นนั้น การทดสอบบน Linux ด้วย 3.1.17 ให้ผลลัพธ์ของคุณ ตอนนี้ฉันยังไม่มีกล่อง BSD แต่ฉันจะพยายามยืนยันเรื่องนี้ในวันพรุ่งนี้เพียงเพื่อความอยากรู้อยากเห็นของฉันเอง
sorpigal

@Sorpigal: น่าสนใจแจ้งให้เราทราบสิ่งที่คุณพบ ฉันทุกอย่างเกี่ยวกับการพกพา
SiegeX

2
ฉันได้รับมัน. การทดสอบ FreeBSD ของฉันขึ้นอยู่กับรหัสที่คัดลอกและวางจากการแก้ไขโพสต์ของ lesmana ที่ผิดพลาดซึ่งมีข้อแตกต่างที่สำคัญอย่างหนึ่งนั่นคือคุณได้ผ่านreadไฟล์-d ''. เมื่อฉันลองอีกครั้งในภายหลังบน Linux ฉันใช้เวอร์ชันที่โพสต์ใหม่ ถ้าฉันลองละเว้น-d ''แน่นอนว่ามันใช้งานได้เหมือนกันบน FreeBSD! จริงๆแล้วฉันค่อนข้างโล่งใจที่ไม่มีเวทย์มนตร์แพลตฟอร์มลึกลับที่ทำงานที่นี่
sorpigal

17

สำหรับโซลูชันที่ใช้งานได้โดยไม่ต้องทุบตีหรือคุณสมบัติบางอย่างจากreadคุณสามารถใช้sttyเพื่อปิดใช้งานเสียงสะท้อน

stty_orig=$(stty -g)
stty -echo
read password
stty $stty_orig

9

นี่คือรูปแบบของ*โซลูชันการพิมพ์ที่ยอดเยี่ยมของ @ SiegeX สำหรับbash การรองรับ backspace ที่เพิ่มเข้ามา สิ่งนี้ช่วยให้ผู้ใช้สามารถแก้ไขรายการของตนด้วยbackspaceคีย์ ( deleteคีย์บน Mac)ตามที่ได้รับการสนับสนุนโดยพรอมต์รหัสผ่าน:

#!/usr/bin/env bash

password=''
while IFS= read -r -s -n1 char; do
  [[ -z $char ]] && { printf '\n'; break; } # ENTER pressed; output \n and break.
  if [[ $char == $'\x7f' ]]; then # backspace was pressed
      # Remove last char from output variable.
      [[ -n $password ]] && password=${password%?}
      # Erase '*' to the left.
      printf '\b \b' 
  else
    # Add typed char to output variable.
    password+=$char
    # Print '*' in its stead.
    printf '*'
  fi
done

บันทึก:

  • สาเหตุที่การกดรหัสอักขระ backspace บันทึก0x7f: "ในระบบสมัยใหม่คีย์ backspace มักถูกจับคู่กับอักขระลบ (0x7f ใน ASCII หรือ Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \bจำเป็นต้องให้ลักษณะของการลบอักขระทางด้านซ้าย เพียงแค่ใช้\bการเลื่อนเคอร์เซอร์ไปทางซ้าย แต่ปล่อยให้ตัวละครยังคงอยู่ ( ไม่ทำลายแบ็คสเปซ) เมื่อพิมพ์ช่องว่างและเลื่อนกลับอีกครั้งดูเหมือนว่าอักขระจะถูกลบไปแล้ว (ขอบคุณอักขระหนี "backspace" ใน C มีพฤติกรรมที่ไม่คาดคิด? )

ในเชลล์POSIX เท่านั้น (เช่นshบน Debian และ Ubuntu ที่shอยู่dash) ให้ใช้stty -echoวิธีการ (ซึ่งเป็นอันดับต่ำกว่าเนื่องจากไม่พิมพ์อะไรเลย ) เนื่องจากreadบิวท์อินจะไม่รองรับ-sและ-nอ็อพชัน


ขอบคุณ @Dylan ฉันเพิ่งรู้ว่ารหัสสำหรับลบอักขระตัวสุดท้ายจากรหัสผ่านที่พิมพ์ไปนั้นสามารถทำให้ง่ายขึ้นได้ - ดูการอัปเดต
mklement0

3

แตกต่างจาก (แต่ส่วนใหญ่ชอบ) คำตอบของ @ lesmana

stty -echo
read password
stty echo

เพียง: ซ่อนเสียงสะท้อนทำสิ่งที่คุณแสดงเสียงสะท้อน


อธิบายได้ไหมว่าทำไมถึงดีกว่าคำตอบของ @ lesmana ? ฉันเห็นว่ามันง่ายกว่าโดยมีค่าใช้จ่ายน้อยกว่าสำหรับการsttyโทรอื่นและมีตัวแปรน้อยกว่าหนึ่งตัวในสแต็ก - เพียงแค่กำจัดเสียงสะท้อนและคืนค่าเสียงสะท้อนเท่านั้น มีวิธีใดบ้างที่อาจทำให้sttyการตั้งค่าอื่น ๆเสียหายได้? Lesmana รักษาการตั้งค่าทั้งหมด
Jeff Puckett

2

นี่คือรูปแบบของคำตอบของ @ SiegeX ซึ่งใช้ได้กับ Bourne shell แบบดั้งเดิม (ซึ่งไม่มีการรองรับการ+=มอบหมายงาน)

password=''
while IFS= read -r -s -n1 pass; do
  if [ -z "$pass" ]; then
     echo
     break
  else
     printf '*'
     password="$password$pass"
  fi
done

Bourne เชลล์แบบดั้งเดิม (หรือที่สอดคล้องกับ POSIX) ก็จะไม่รู้จัก[[ ... ]]เช่นกันและในreadตัวจะไม่มีตัวเลือก-sและ -nดังนั้นรหัสของคุณจะใช้ไม่ได้dashเช่น (มันจะทำงานบนแพลตฟอร์มที่shเป็นจริงbashเช่นบน OSX ซึ่งbashเมื่อเรียกใช้เป็นshเพียงแก้ไขตัวเลือกเริ่มต้นบางตัวในขณะที่ยังรองรับ bashisms ส่วนใหญ่)
mklement0

2
ขอบคุณสำหรับคำติชมเปลี่ยนเป็น single [แต่เห็นได้ชัดว่าฉันทำได้ไม่มากถ้าคุณreadไม่มีตัวเลือกเหล่านี้
tripleee

2

ฉันชอบใช้อักขระ Escape Ansi เสมอ:

echo -e "Enter your password: \x1B[8m"
echo -e "\x1B[0m"

8m ทำให้มองไม่เห็นข้อความและ 0mรีเซ็ตข้อความเป็น "ปกติ" The -e ทำให้ Ansi หนีไปได้

ข้อแม้เดียวคือคุณยังสามารถคัดลอกและวางข้อความที่อยู่ในนั้นได้ดังนั้นคุณอาจไม่ควรใช้สิ่งนี้หากคุณต้องการความปลอดภัยจริงๆ

ช่วยให้คนอื่นไม่มองรหัสผ่านของคุณเมื่อคุณพิมพ์รหัสผ่านเพียงแค่อย่าเปิดคอมพิวเตอร์ทิ้งไว้ในภายหลัง :)


บันทึก:

ด้านบนเป็นแพลตฟอร์มที่เป็นอิสระตราบเท่าที่รองรับลำดับการหลบหนีของ Ansi

อย่างไรก็ตามสำหรับโซลูชัน Unix อื่นคุณสามารถบอกได้ว่าreadอย่าสะท้อนตัวละคร ...

printf "password: "
let pass $(read -s)
printf "\nhey everyone, the password the user just entered is $pass\n"

1

รับชื่อผู้ใช้และรหัสผ่าน

ทำให้อ่านได้ชัดเจนยิ่งขึ้น แต่วางไว้ในตำแหน่งที่ดีกว่าบนหน้าจอ

#!/bin/bash
clear
echo 
echo 
echo
counter=0
unset username
prompt="  Enter Username:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]; then
        break
    elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
        prompt=$'\b \b'
        username="${username%?}"
        counter=$((counter-1))
    elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
        prompt=''
        continue
    else
        counter=$((counter+1))
        prompt="$char"
        username+="$char"
    fi
done
echo
unset password
prompt="  Enter Password:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]; then
        break
    elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
        prompt=$'\b \b'
        password="${password%?}"
        counter=$((counter-1))
    elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
        echo
        prompt="  Enter Password:"
        continue
    else
        counter=$((counter+1))
        prompt='*'
        password+="$char"
    fi
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.