入力したパスワードを確認する方法は、このユーザーの有効なパスワードですか?


14

シナリオ:

bashスクリプトでは、ユーザーが指定したパスワードが有効なユーザーパスワードかどうかを確認する必要があります。

つまり、パスワードPAのユーザーAがいるとします。スクリプトでは、ユーザーAにパスワードを入力するように要求しました。


有効なパスワードとはどういう意味ですか?それが本当にユーザーのパスワードかどうかをテストしたいですか?
AB

呼び出したいものは何でも@AB有効なパスワード=ログインパスワードは...私は、このユーザーのためにそのパスワードを意味する
Maythux

@AB私は..あなたはすでに、ユーザー名を知っている...つまりそのわずかテストをパスワードは、このユーザーのためであれば、ここでそれはセキュリティホールになります知っているが
Maythux

3
ここにあなたの答えは次のとおりです。unix.stackexchange.com/a/21728/107084
AB

上ベースのソリューションexpectstackoverflow.com/a/1503831/320594
ハイメHablutzel

回答:


14

これをシェルスクリプトで実行したいので、「Linuxでパスワードを確認する方法」にいくつかの貢献があります。Unix.SEAB提案)は特に関連しています:

  • エントリに一致するパスワードハッシュの生成に関するrozcietrzewiacz回答/etc/shadowは、ソリューションの一部を提供します。
  • Daniel Alderコメントは、mkpasswdDebian(およびUbuntu)に存在するコマンドの異なる構文を説明しています。

文字列が実際にユーザーのパスワードであるかどうかを手動で確認するには、ユーザーのシャドウエントリと同じハッシュアルゴリズムと、ユーザーのシャドウエントリと同じソルトでハッシュする必要があります。次に、そこに保存されているパスワードハッシュと比較できます。

これを行う方法を示す完全な作業スクリプトを作成しました。

  • 名前を付けるとchkpass、実行することができ、標準入力から行を読み取り、パスワードであるかどうかを確認します。chkpass useruser
  • whois whoisをインストールするパッケージをインストールして、mkpasswdこのスクリプトが依存するユーティリティを取得します。
  • 成功するには、このスクリプトをrootとして実行する必要があります。
  • このスクリプトまたはその一部を使用して実際の作業を行う前に、以下のセキュリティノートを参照してください。
#!/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

セキュリティノート

それは正しいアプローチではないかもしれません。

このようなアプローチを安全であると見なすべきかどうかは、提供していないユースケースの詳細に依存します(この記事の執筆時点では)。

監査されていません。

このスクリプトの作成中は注意を払っていますが、セキュリティの脆弱性については適切に監査されていません。これはデモンストレーションを目的としており、プロジェクトの一部としてリリースされた場合、「アルファ」ソフトウェアになります。さらに...

「監視」している別のユーザーは、ユーザーのsaltを発見できる可能性があります。

mkpasswdソルトデータの受け入れ方法の制限により、このスクリプトには既知のセキュリティ上の欠陥が含まれています。デフォルトでは、Ubuntuおよび他のほとんどのGNU / Linuxシステムのユーザーは、コマンドライン引数を含む、他のユーザー(ルートを含む)によって実行されるプロセスに関する情報を表示できます。ユーザーの入力も保存されたパスワードハッシュも、コマンドライン引数として外部ユーティリティに渡されません。しかしから抽出され、shadowデータベースが、されにコマンドライン引数として与えられmkpasswd、これはユーティリティは、入力として、塩を受け入れることが唯一の方法であるので、。

もし

  • システム上の別のユーザー、または
  • ユーザーアカウント(たとえば、www-data)でコードを実行できる機能を持っている人、または
  • それ以外の場合は、実行中のプロセスに関する情報を表示できる人(のエントリを手動で検査することを含む/proc

mkpasswdこのスクリプトによって実行されるコマンドライン引数をチェックでき、ユーザーはshadowデータベースからユーザーのソルトのコピーを取得できます。彼らそのコマンドがいつ実行されるかを推測できなければならないかもしれませんが、それは時々達成可能です。

saltを使用した攻撃者は、salt とhashを使用した攻撃者ほど悪くはありませんが、理想的ではありません。塩は、誰かがあなたのパスワードを発見するのに十分な情報を提供しません。ただし、そのシステムでそのユーザーに固有のレインボーテーブルまたは事前に計算された辞書ハッシュを生成することはできます。これは最初は価値がありませんが、後日セキュリティが危険にさらさ、完全なハッシュが取得された場合、ユーザーのパスワードを変更する前にユーザーのパスワードを取得するためにより迅速にクラックされる可能性があります。

したがって、このセキュリティ上の欠陥は、完全に悪用可能な脆弱性というよりも、より複雑な攻撃シナリオにおける悪化要因です。そして、あなたは上記の状況を大げさに考えるかもしれません。しかし、私は一般的な、現実世界での使用のための任意の方法をお勧めするには消極的だが漏れているいかなるから非公開データ/etc/shadowroot以外のユーザーへ。

次の方法でこの問題を完全に回避できます。

  • あなたのように、Cの関数を呼び出すことができますPerlや他のいくつかの言語でのスクリプトの一部を書いジルの答えに示す関連Unix.SE質問または
  • bashを使用するのではなく、そのような言語でスクリプト/プログラム全体を記述します。(質問にタグを付けた方法に基づいて、bashの使用を好むようです。)

このスクリプトの呼び出し方に注意してください。

信頼できないユーザーがこのスクリプトをルートとして実行したり、このスクリプトを呼び出すプロセスをルートとして実行したりすることを許可する場合は、注意してください。環境を変更することで、このスクリプト(またはルートとして実行される任意のスクリプト)で何でも実行できます。これを防ぐことができない限り、ユーザーにシェルスクリプトを実行するための昇格された特権を許可しないでください。

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')に設定されていると仮定した場合、スクリプトをかなり奇妙に見える方法で動作させることができます。


8

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設定されています
  • listpwnever

その他の問題は次のとおりです(Eliahに感謝)。

  • 誤った試行がログインされます /var/log/auth.log
  • sudo認証対象のユーザーとして実行する必要があります。sudoを実行できる特権がない限りsudo -u foo sudo -lS、ターゲットユーザーとしてスクリプトを実行する必要があります。

さて、ここでドキュメントを使用したのは、盗聴を妨げるためです。コマンドラインの一部として使用される変数は、より簡単に使用することによって明らかにされるtop、またはpsプロセスを検査するか、他のツール。


2
この方法は素晴らしいようです。珍しいsudo設定(おっしゃるとおり)のシステムでは動作せず/var/log/auth.log、各試行の記録が乱雑になりますが、これは迅速で簡単であり、車輪を再発明する(いわば)のではなく、既存のユーティリティを使用します。の設定をお勧めします。これIFS=read、空白が埋め込まれたユーザー入力と非空白のパスワードの誤った成功、および空白が埋め込まれたユーザー入力と埋め込まれたパスワードの誤った失敗を防ぐためです。(私のソリューションにも同様のバグがありました。)また、パスワードがチェックされているユーザーとして実行する方法を説明することもできます。
エリアケイガン

@EliahKagan失敗した各試行は記録されますが、はい、あなたは他のすべてについて正しいです。
ムル

成功した認証も記録されます。 sudo -lエントリに「COMMAND=list」が書き込まれます。Btw(無関係)、そうすることは客観的には良いことではありませんが、hereドキュメントの代わりにhere文字列を使用すると、同じセキュリティ目標が達成され、よりコンパクトになります。スクリプトは既にbashisms read -s&>を使用してif sudo -lS &>/dev/null <<<"$PASSWD"; thenいるため、usingの移植性は劣りません。私はあなたが持っているものを変えるべきだと提案しているのではありません(それは大丈夫です)。むしろ、このオプションがあることを読者が知っているように言及します。
エリアケイガン

@EliahKaganああ。私はsudo -l何かを無効にしたと思った-おそらく、ユーザーが許可されていないコマンドを実行しようとしたときに報告されていた。
ムル

@EliahKagan確かに。私は以前にそのようにヒアストリングを使用しました。私はここで誤解の下で働き、それがヒアドキュメントにつながりましたが、とにかくそれらを保持することにしました。
ムル

4

別の方法(おそらく実用的なアプリケーションよりも理論的な内容のほうが興味深い)。

ユーザーのパスワードはに保存され/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

1
sudo getent shadow>> sudo grep ...。そうでなければ、いい。これは私が元々考えていたもので、それから怠け者になりました。
ムル

@muruありがとう。確かに気分が良くなったので更新されましたが、この場合はgetent+ grep+ cut> grep+cut
kos

1
ええ getentあなたのための検索を行うことができます。編集の自由を取りました。
ムル

@muruうん、あなたは実際に持っている必要があります、今私はポイントを見ます!
コス

2
私は偏っているかもしれませんが、これは実際には非常に実用的だと思います:それは私が使用したのと同じ方法です。ただし、実装とプレゼンテーションはまったく異なります。これは間違いなく貴重な答えです。私が使用したのに対しmkpasswd、ユーザの入力と既存の塩(および追加機能と診断機能を含む)からハッシュを生成するために、あなたの代わりに実装する簡単なPythonプログラムコード化されてきたmkpasswdの最も重要な機能をしていることを使用します。IFS=パスワード入力を読み取るときに設定し、で引用${password}することをお勧めします"。これにより、空白文字を使用したパスワード試行がスクリプトを中断しないようにします。
エリアケイガン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.