シナリオ:
bashスクリプトでは、ユーザーが指定したパスワードが有効なユーザーパスワードかどうかを確認する必要があります。
つまり、パスワードPAのユーザーAがいるとします。スクリプトでは、ユーザーAにパスワードを入力するように要求しました。
expect
でstackoverflow.com/a/1503831/320594。
シナリオ:
bashスクリプトでは、ユーザーが指定したパスワードが有効なユーザーパスワードかどうかを確認する必要があります。
つまり、パスワードPAのユーザーAがいるとします。スクリプトでは、ユーザーAにパスワードを入力するように要求しました。
expect
でstackoverflow.com/a/1503831/320594。
回答:
これをシェルスクリプトで実行したいので、「Linuxでパスワードを確認する方法」にいくつかの貢献があります。(Unix.SEでABが提案)は特に関連しています:
/etc/shadow
は、ソリューションの一部を提供します。mkpasswd
Debian(およびUbuntu)に存在するコマンドの異なる構文を説明しています。文字列が実際にユーザーのパスワードであるかどうかを手動で確認するには、ユーザーのシャドウエントリと同じハッシュアルゴリズムと、ユーザーのシャドウエントリと同じソルトでハッシュする必要があります。次に、そこに保存されているパスワードハッシュと比較できます。
これを行う方法を示す完全な作業スクリプトを作成しました。
chkpass
、実行することができ、標準入力から行を読み取り、パスワードであるかどうかを確認します。chkpass user
user
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システムのユーザーは、コマンドライン引数を含む、他のユーザー(ルートを含む)によって実行されるプロセスに関する情報を表示できます。ユーザーの入力も保存されたパスワードハッシュも、コマンドライン引数として外部ユーティリティに渡されません。しかし塩から抽出され、shadow
データベースが、されにコマンドライン引数として与えられmkpasswd
、これはユーティリティは、入力として、塩を受け入れることが唯一の方法であるので、。
もし
www-data
)でコードを実行できる機能を持っている人、または/proc
)mkpasswd
このスクリプトによって実行されるコマンドライン引数をチェックでき、ユーザーはshadow
データベースからユーザーのソルトのコピーを取得できます。彼らはそのコマンドがいつ実行されるかを推測できなければならないかもしれませんが、それは時々達成可能です。
saltを使用した攻撃者は、salt とhashを使用した攻撃者ほど悪くはありませんが、理想的ではありません。塩は、誰かがあなたのパスワードを発見するのに十分な情報を提供しません。ただし、そのシステムでそのユーザーに固有のレインボーテーブルまたは事前に計算された辞書ハッシュを生成することはできます。これは最初は価値がありませんが、後日セキュリティが危険にさらされ、完全なハッシュが取得された場合、ユーザーのパスワードを変更する前にユーザーのパスワードを取得するためにより迅速にクラックされる可能性があります。
したがって、このセキュリティ上の欠陥は、完全に悪用可能な脆弱性というよりも、より複雑な攻撃シナリオにおける悪化要因です。そして、あなたは上記の状況を大げさに考えるかもしれません。しかし、私は一般的な、現実世界での使用のための任意の方法をお勧めするには消極的だが漏れているいかなるから非公開データ/etc/shadow
root以外のユーザーへ。
次の方法でこの問題を完全に回避できます。
信頼できないユーザーがこのスクリプトをルートとして実行したり、このスクリプトを呼び出すプロセスをルートとして実行したりすることを許可する場合は、注意してください。環境を変更することで、このスクリプト(またはルートとして実行される任意のスクリプト)で何でも実行できます。これを防ぐことができない限り、ユーザーにシェルスクリプトを実行するための昇格された特権を許可しないでください。
10.4を参照してください。これについての詳細は、David A. WheelerのLinuxおよびUnix用のセキュアプログラミングHOWTOのシェルスクリプト言語(shおよびcsh派生語)を参照してください。彼のプレゼンテーションはsetuidスクリプトに焦点を合わせていますが、他のメカニズムが環境を正しくサニタイズしない場合、同じ問題のいくつかの犠牲になります。
shadow
データベースからのハッシュの読み取りのみをサポートします。このスクリプトが機能するには、パスワードをシャドウする必要があります(つまり、そのハッシュは/etc/shadow
、rootではなく、rootだけが読み取ることができる別のファイルにある必要があります/etc/passwd
)。
これは、Ubuntuで常に当てはまるはずです。いずれの場合でも、必要に応じてスクリプトを簡単に拡張して、パスワードハッシュpasswd
およびを読み取ることができますshadow
。
IFS
このスクリプトを変更するときは注意してください。IFS=$
シャドウエントリのハッシュフィールドの3つのデータはで区切られて$
いるため、最初に設定しました。
$
ハッシュの種類と塩である理由である、"${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特権をテストし、stdinからパスワードを読み込むためsudo
の-l
オプションがあります-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
その他の問題は次のとおりです(Eliahに感謝)。
/var/log/auth.log
sudo
認証対象のユーザーとして実行する必要があります。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
いるため、usingの移植性は劣りません。私はあなたが持っているものを変えるべきだと提案しているのではありません(それは大丈夫です)。むしろ、このオプションがあることを読者が知っているように言及します。
sudo -l
何かを無効にしたと思った-おそらく、ユーザーが許可されていないコマンドを実行しようとしたときに報告されていた。
別の方法(おそらく実用的なアプリケーションよりも理論的な内容のほうが興味深い)。
ユーザーのパスワードはに保存され/etc/shadow
ます。
ここに保存されているパスワードは、最新のUbuntuリリースではを使用して暗号化されていSHA-512
ます。
具体的には、パスワードの作成時に、クリアテキストのパスワードがソルト処理され、暗号化されますSHA-512
。
1つの解決策は、指定されたパスワードをソルト/暗号化し、指定されたユーザーの/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
、ユーザの入力と既存の塩(および追加機能と診断機能を含む)からハッシュを生成するために、あなたの代わりに実装する簡単なPythonプログラムコード化されてきたmkpasswd
の最も重要な機能をしていることを使用します。IFS=
パスワード入力を読み取るときに設定し、で引用${password}
することをお勧めします"
。これにより、空白文字を使用したパスワード試行がスクリプトを中断しないようにします。