시나리오 :
bash 스크립트에서 사용자가 제공 한 비밀번호가 유효한 사용자 비밀번호인지 확인해야합니다.
즉, 암호 PA가있는 사용자 A가 있다고 가정합니다. 스크립트에서 사용자 A에게 암호를 입력하도록 요청 했으므로 입력 한 문자열이 실제로 암호인지 확인하는 방법은 무엇입니까? ...
expect에 stackoverflow.com/a/1503831/320594 .
시나리오 :
bash 스크립트에서 사용자가 제공 한 비밀번호가 유효한 사용자 비밀번호인지 확인해야합니다.
즉, 암호 PA가있는 사용자 A가 있다고 가정합니다. 스크립트에서 사용자 A에게 암호를 입력하도록 요청 했으므로 입력 한 문자열이 실제로 암호인지 확인하는 방법은 무엇입니까? ...
expect에 stackoverflow.com/a/1503831/320594 .
답변:
쉘 스크립트에서이 작업을 수행하려고하므로 Linux에서 비밀번호를 확인하는 방법에 몇 가지 기여가 있습니까? ( AB 에서 제안한 Unix.SE )는 특히 관련이 있습니다.
/etc/shadow 은 솔루션의 일부를 제공합니다.mkpasswd 은 데비안 (및 우분투)에 존재 하는 명령 의 다른 구문을 설명합니다 .문자열이 실제로 사용자의 암호인지 수동으로 확인하려면 사용자의 섀도 항목과 동일한 해시 알고리즘으로 사용자의 섀도 항목 과 동일한 소금 으로 문자열을 해시해야합니다 . 그런 다음 저장된 암호 해시와 비교할 수 있습니다.
이 작업을 수행하는 방법을 보여주는 완전하고 작동하는 스크립트를 작성했습니다.
chkpass실행할 수 있으며 표준 입력에서 한 줄을 읽고 암호 인지 확인 합니다.chkpass useruser
패키지를 설치하십시오 mkpasswd.#!/usr/bin/env bash
xcorrect=0 xwrong=1 enouser=2 enodata=3 esyntax=4 ehash=5 IFS=$
die() {
printf '%s: %s\n' "$0" "$2" >&2
exit $1
}
report() {
if (($1 == xcorrect))
then echo 'Correct password.'
else echo 'Wrong password.'
fi
exit $1
}
(($# == 1)) || die $esyntax "Usage: $(basename "$0") <username>"
case "$(getent passwd "$1" | awk -F: '{print $2}')" in
x) ;;
'') die $enouser "error: user '$1' not found";;
*) die $enodata "error: $1's password appears unshadowed!";;
esac
if [ -t 0 ]; then
IFS= read -rsp "[$(basename "$0")] password for $1: " pass
printf '\n'
else
IFS= read -r pass
fi
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
case "${ent[1]}" in
1) hashtype=md5;; 5) hashtype=sha-256;; 6) hashtype=sha-512;;
'') case "${ent[0]}" in
\*|!) report $xwrong;;
'') die $enodata "error: no shadow entry (are you root?)";;
*) die $enodata 'error: failure parsing shadow entry';;
esac;;
*) die $ehash "error: password hash type is unsupported";;
esac
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
then report $xcorrect
else report $xwrong
fi
이와 같은 접근 방식이 안전하고 적절한 것으로 간주되어야하는지 여부는 제공하지 않은 사용 사례에 대한 세부 정보에 따라 다릅니다 (이 문서 작성 시점 기준).
이 스크립트를 작성하는 동안주의를 기울이려고했지만 보안 취약점에 대해 제대로 감사하지 않았습니다 . 데모 용으로 제작되었으며 프로젝트의 일부로 출시 된 경우 "알파"소프트웨어가됩니다. 더욱이...
mkpasswd솔트 데이터를 받아들이 는 방식의 한계로 인해이 스크립트 에는 알려진 보안 결함 이 포함되어 있으며 , 사용 사례에 따라 허용 가능한 것으로 간주 될 수도 있고 그렇지 않을 수도 있습니다. 기본적으로 Ubuntu 및 대부분의 다른 GNU / Linux 시스템 사용자는 명령 줄 인수를 포함하여 다른 사용자 (루트 포함)가 실행하는 프로세스에 대한 정보를 볼 수 있습니다. 사용자의 입력 또는 저장된 암호 해시는 명령 줄 인수로 외부 유틸리티에 전달되지 않습니다. 그러나 데이터베이스 에서 추출 된 salt 는 에 대한 명령 행 인수 로 제공됩니다. 유틸리티가 salt를 입력으로 받아들이는 유일한 방법이기 때문입니다.shadowmkpasswd
만약
www-data:)이 코드를 실행 하거나/proc)mkpasswd이 스크립트에 의해 실행될 때 명령 행 인수를 확인할 수 있으며, shadow데이터베이스 에서 사용자의 소금 사본을 얻을 수 있습니다 . 그들은 수 추측 할 수있을 때 그 명령을 실행하지만 때때로 달성 할 수있다.
salt를 가진 공격자는 salt 와 hash를 가진 공격자만큼 나쁘지는 않지만 이상적이지는 않습니다. 소금은 누군가가 귀하의 비밀번호를 발견 할 수있는 충분한 정보를 제공하지 않습니다. 그러나 누군가가 해당 시스템에서 해당 사용자에 대한 레인보우 테이블 또는 사전 계산 된 사전 해시 를 생성 할 수 있습니다. 처음에는 가치가 없지만 나중에 보안이 손상 되어 전체 해시 를 얻는 경우 사용자 암호를 변경하기 전에 사용자 암호를 얻기 위해 더 빨리 해독 될 수 있습니다.
따라서이 보안 결함은 완전히 악용 가능한 취약점이 아니라보다 복잡한 공격 시나리오의 악화 요인입니다. 그리고 당신은 위의 상황을 멀리 가져온 것으로 생각할 수 있습니다. 그러나 비공개 데이터를 루트가 아닌 사용자 에게 유출 하는 일반적인 실제 사용 방법을 권장하지 않습니다 /etc/shadow.
다음을 통해이 문제점을 완전히 피할 수 있습니다.
신뢰할 수없는 사용자가이 스크립트를 루트로 실행하거나이 스크립트를 호출하는 프로세스를 루트로 실행하도록 허용하는 경우 주의하십시오 . 환경을 변경하면이 스크립트 또는 루트로 실행되는 모든 스크립트가 무엇이든 수행 할 수 있습니다. 이를 방지 할 수 없다면 쉘 스크립트를 실행할 수있는 권한을 사용자에게 허용해서는 안됩니다.
10.4를 참조하십시오 . 이에 대한 자세한 내용 은 David A. Wheeler의 Linux 및 Unix HOWTO 용 보안 프로그래밍 의 셸 스크립팅 언어 (sh 및 csh 파생) 입니다 . 그의 프레젠테이션은 setuid 스크립트에 중점을 두지 만, 다른 메커니즘이 환경을 올바르게 위생 처리하지 않으면 동일한 문제 중 일부에 노출 될 수 있습니다.
shadow데이터베이스 에서 해시 읽기만 지원합니다 .이 스크립트가 작동하려면 비밀번호를 음영 처리해야합니다 (즉, 해시는 /etc/shadow루트가 아닌 읽을 수 있는 별도의 파일 에 있어야 함 /etc/passwd).
우분투에서는 항상 그렇습니다. 어쨌든, 필요한 경우 스크립트를 간단하게 확장하여 passwd에서뿐만 아니라 암호 해시를 읽을 수 있습니다 shadow.
IFS이 스크립트를 수정할 때 명심 하십시오 .IFS=$섀도 항목의 해시 필드에있는 세 개의 데이터가로 구분되므로 처음에 설정 했습니다 $.
$해시 유형과 소금이 이유입니다, "${ent[1]}"그리고 "${ent[2]}"보다는 "${ent[0]}"와 "${ent[1]}"각각.이 스크립트에서 $IFS쉘 이 단어를 나누 거나 결합 하는 방법을 결정하는 유일한 위치
이러한 데이터가 인용되지 않은 $( )명령 대체에서 초기화되어 배열로 분할되는 경우 :
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f에서 전체 필드와 비교하기 위해 배열을 문자열로 재구성 shadow하면 "${ent[*]}"식은 다음과 같습니다.
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]다른 상황에서 스크립트를 수정하고 단어 분리 (또는 단어 결합)를 수행하는 경우 IFS다른 명령이나 스크립트의 다른 부분에 대해 다른 값 으로 설정해야합니다 .
이것을 명심하지 않고 $IFS일반적인 공백 ( $' \t\n')으로 설정되어 있다고 가정 하면 스크립트가 이상한 모양으로 보일 수 있습니다.
sudo이것을 오용 할 수 있습니다 . sudo이 -l사용자가 가지고있는 sudo는 권한을 테스트하고, 옵션, -S표준 입력에서 암호에서 읽을 수 있습니다. 그러나 사용자의 권한 수준에 관계없이 성공적으로 인증되면 sudo종료 상태 0으로 돌아갑니다. 따라서 인증이 작동하지 않았다는 표시로 다른 종료 상태를 취할 수 있습니다 ( sudo자체에 아무런 문제가 없다고 가정). 권한 오류 또는 잘못된 sudoers구성).
다음과 같은 것 :
#! /bin/bash
IFS= read -rs PASSWD
sudo -k
if sudo -lS &> /dev/null << EOF
$PASSWD
EOF
then
echo 'Correct password.'
else
echo 'Wrong password.'
fi
이 스크립트는 sudoers구성 에 따라 약간 다릅니다 . 기본 설정을 가정했습니다. 실패 할 수있는 것들 :
targetpw또는 runaspw설정listpw 이다 never다른 문제는 다음과 같습니다 (엘리아에게 감사합니다).
/var/log/auth.logsudo인증하는 사용자로 실행해야합니다. 을 sudo실행할 수 있는 권한이 없으면 sudo -u foo sudo -lS스크립트를 대상 사용자로 실행해야합니다.자, 내가 여기 문서를 사용한 이유는 도청을 방해하기 위해서입니다. 커맨드 라인의 일부로서 사용되는 변수는 더 쉽게하여 노출 top또는 ps프로세스를 검사하는 다른 도구 또는.
sudo구성 (당신이 말한 것처럼)이 있고 /var/log/auth.log각 시도의 기록이있는 시스템에서는 작동하지 않지만 이것은 빠르고 간단하며 바퀴를 재발 명하지 않고 기존 유틸리티를 사용합니다 (발언). 공백으로 채워진 사용자 입력 및 채워지지 않은 암호의 잘못된 성공과 공백으로 채워진 사용자 입력 및 채워진 암호의 잘못된 실패를 방지하기 위해에 IFS=대한 설정 을 제안 read합니다. (내 솔루션에는 비슷한 버그가 있습니다.) 암호를 확인하는 사용자로 어떻게 실행해야하는지 설명하고 싶을 수도 있습니다.
sudo -l" COMMAND=list" 로 항목을 씁니다 . Btw (관련되지 않음)는 객관적으로 낫지는 않지만 here 문서 대신 here 문자열을 사용하면 동일한 보안 목표를 달성하고보다 컴팩트합니다. 스크립트가 이미 bashisms을 사용하기 때문에 read -s하고 &>, 사용은 if sudo -lS &>/dev/null <<<"$PASSWD"; then더 적은 휴대용 없습니다. 나는 당신이 당신이 가진 것을 바꾸어야한다고 제안하지 않습니다 (괜찮습니다). 오히려 독자들이이 옵션을 가지고 있음을 알 수 있도록 언급했습니다.
sudo -l무언가를 비활성화 했다고 생각했다. 아마도 사용자가 허용되지 않는 명령을 실행하려고 시도했을 때보 고되었을 것이다.
또 다른 방법 (아마도 실제 적용보다 이론적 내용에 더 흥미 롭습니다).
사용자 비밀번호는에 저장됩니다 /etc/shadow.
여기에 저장된 비밀번호는를 사용하여 최신 Ubuntu 릴리스에서 암호화됩니다 SHA-512.
특히 암호를 만들 때 일반 텍스트의 암호는을 통해 소금에 절이고 암호화됩니다 SHA-512.
한 가지 해결책은 주어진 암호를 소금 / 암호화하고 주어진 사용자의 /etc/shadow항목에 저장된 암호화 된 사용자 암호와 일치시키는 것 입니다.
암호는 각 사용자에 저장하는 방법에 대한 간단한 고장 제공하려면 /etc/shadow항목을 여기에 샘플의 /etc/shadow사용자에 대한 항목 foo암호는 bar:
foo:$6$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/:16566:0:99999:7:::
foo: 사용자 이름6: 비밀번호의 암호화 유형lWS1oJnmDlaXrx1F: 비밀번호의 암호화 솔트h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/: SHA-512소금에 절인 암호bar주어진 사용자에 대해 주어진 암호를 일치시키기 위해 foo가장 먼저 할 일은 소금을 얻는 것입니다.
$ sudo getent shadow foo | cut -d$ -f3
lWS1oJnmDlaXrx1F
그런 다음 완전히 소금에 절인 / 암호화 된 암호를 가져옵니다.
$ sudo getent shadow foo | cut -d: -f2
$6$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/
그런 다음 주어진 암호를 소금에 절인 / 암호화하고 /etc/shadow다음에 저장된 소금에 절인 / 암호화 된 사용자의 비밀번호와 일치시킬 수 있습니다 .
$ python -c 'import crypt; print crypt.crypt("bar", "$6$lWS1oJnmDlaXrx1F")'
$6$lWS1oJnmDlaXrx1F$h4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/
일치합니다! bash스크립트에 넣은 모든 것 :
#!/bin/bash
read -p "Username >" username
IFS= read -p "Password >" password
salt=$(sudo getent shadow $username | cut -d$ -f3)
epassword=$(sudo getent shadow $username | cut -d: -f2)
match=$(python -c 'import crypt; print crypt.crypt("'"${password}"'", "$6$'${salt}'")')
[ ${match} == ${epassword} ] && echo "Password matches" || echo "Password doesn't match"
산출:
$ ./script.sh
Username >foo
Password >bar
Password matches
$ ./script.sh
Username >foo
Password >bar1
Password doesn't match
sudo getent shadow>> sudo grep .... 그렇지 않으면 좋습니다. 이것이 내가 원래 생각한 것인데 너무 게으르다.
getent+ grep+ cut> grep+cut
getent당신을 찾고 있습니다. 나는 편집의 자유를 가져 갔다.
mkpasswd사용자 입력과 기존 솔트에서 해시를 생성하는 데 사용했지만 (추가 기능 및 진단 포함) 대신 mkpasswd가장 중요한 기능을 구현하는 간단한 Python 프로그램을 코딩하여 사용했습니다. IFS=비밀번호 입력을 읽을 때 설정 하고로 인용 ${password}하는 "것이 좋으므로 공백으로 비밀번호를 입력해도 스크립트가 손상되지 않습니다.