Скрытие пользовательского ввода на терминале в сценарии Linux


121

У меня есть сценарий bash, подобный следующему:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

Я хочу, чтобы когда пользователь вводил пароль на терминале, он не отображался (или отображалось что-то вроде *******). Как мне этого добиться?


2
Общее примечание для предотвращения путаницы: это имя пользователя / пароль не имеет ничего общего с именем пользователя / паролем linux - я просто ищу способ скрыть данные, которые пользователь вводит во время «чтения пароля».

Я добавил обновление, если вы хотите *
пофантазировать, выводя

Большое спасибо. Один вопрос, если кто-то знает - это автоматически предотвратит попадание в .bash_history?


2
Я не думаю, что это будет, bash_history захватывает только вашу команду, а то, что происходит после запуска вашей команды, не захватывает.
Андреас Вонг

Ответы:


272

Просто поставьте -s для вашего вызова чтения вот так:

$ read -s PASSWORD
$ echo $PASSWORD

9
Мне так больше нравится. Я бы проголосовал за вас, если бы не достиг своего дневного лимита
SiegeX

4
Для обеспечения контекста: ничего не-s отображает, пока введены данные. (не является расширением POSIX, поэтому не все оболочки поддерживают его, например оболочка, которая поставляется с BusyBox; используйте подход в таких оболочках)-sashssty -echo
mklement0 08

3
@electron Ничто на manстранице фактически не предполагает, что -sцвет текста устанавливается таким же, как цвет фона. Он просто "беззвучно перекликается". ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Андреас Вонг,

2
@electron Я тестировал на своем ящике, не только он не перемещает курсор, когда я пытаюсь выделить строку, где я ввел свой пароль, я не могу вставить его позже. И то, и другое должно произойти, если то, что говорится в книге, правда. Я предполагаю, что это может быть другая оболочка (я использовал bash 4.1.2 (1)).
Андреас Вонг

1
@DominykasMostauskis да, но метка предназначена для bash, поэтому решение. Вероятно, существует масса вариантов оболочки, в которых это не работает :)
Андреас Вонг

25

Обновить

Если вы хотите пофантазировать, выводя *для каждого набранного символа, вы можете сделать что-то вроде этого (используя read -sрешение Андреаса ):

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'так, чтобы вы действительно могли закончить ввод пароля. В остальном приятно; очень умный.
sorpigal

2
Нет необходимости устанавливать, IFS=$'\n'потому что разделитель чтения по умолчанию -d $'\n'. Оператор if для разрыва нулевой строки, который происходит с новой строки, позволяет им завершить ввод пароля.
SiegeX

При тестировании FreeBSD с bash 3.x я обнаружил, что это не так. Тестирование на Linux с 3.1.17 дает свои результаты. На данный момент у меня нет под рукой BSD-бокса, но я постараюсь подтвердить это завтра, просто ради собственного любопытства.
sorpigal

@Sorpigal: интересно, дайте мне знать, что вы узнали. Я все о переносимости
SiegeX

2
Я понял. Мой FreeBSD тест основан на коде скопировать и вставить из исходного ошибочного редактируют пост lesmana, который содержит одно важное отличие: вы были прохождения . Когда я повторил попытку позже в Linux, я использовал репост-версию. Если я попробую пропустить, конечно, он будет работать во FreeBSD точно так же! Я действительно очень рад, что здесь не работает какая-то таинственная магия платформы. read-d ''-d ''
sorpigal 01

17

для решения, которое работает без bash или определенных функций, которые 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перемещает курсор влево, но оставляет символ без изменений ( неразрушающий возврат). При печати пробела и повторном перемещении назад символ кажется удаленным (спасибо, escape-символ «backspace» '\ b' в 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's сохраняет все настройки.
Джефф Пакетт

2

Вот вариант ответа @ SiegeX, который работает с традиционной оболочкой Борна (которая не поддерживает +=назначения).

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при вызове as shпросто изменяет несколько параметров по умолчанию, все еще поддерживая большинство башизмов.)
mklement0 08

2
Спасибо за отзыв, перешел на одиночный, [но, очевидно, я мало что могу сделать, если у вас readнет этих вариантов.
Tripleee

2

Я всегда люблю использовать escape-символы Ansi:

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

8mделает текст невидимым и 0mсбрасывает текст на «нормальный». -E делает возможным побег Анси.

Единственное предостережение: вы все еще можете копировать и вставлять текст, который там есть, поэтому вам, вероятно, не стоит использовать это, если вам действительно нужна безопасность.

Это просто позволяет людям не смотреть на ваши пароли, когда вы их вводите. Просто не оставляйте компьютер включенным после этого. :)


НОТА:

Вышеупомянутое не зависит от платформы, если оно поддерживает escape-последовательности 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.