Da Sie dies in einem Shell-Skript tun möchten, finden Sie unter Wie überprüfe ich das Kennwort unter Linux? Einige Beiträge . (auf Unix.SE , vorgeschlagen von AB ) sind besonders relevant:
Um manuell zu überprüfen, ob es sich bei einer Zeichenfolge tatsächlich um ein Benutzerkennwort handelt, müssen Sie es mit demselben Hash-Algorithmus wie im Schatteneintrag des Benutzers und mit demselben Salt wie im Schatteneintrag des Benutzers hashen. Dann kann es mit dem dort gespeicherten Passwort-Hash verglichen werden.
Ich habe ein komplettes, funktionierendes Skript geschrieben, das zeigt, wie das geht.
- Wenn Sie es benennen
chkpass
, können Sie es ausführen und es liest eine Zeile von der Standardeingabe und überprüft, ob es das Passwort ist.chkpass user
user
- Installieren Sie das whois- Paket, um das
mkpasswd
Dienstprogramm zu erhalten, von dem dieses Skript abhängt.
- Dieses Skript muss als root ausgeführt werden, um erfolgreich zu sein.
- Bevor Sie dieses Skript oder einen Teil davon für echte Arbeit verwenden, lesen Sie bitte die Sicherheitshinweise unten.
#!/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
Sicherheitshinweise
Es könnte nicht der richtige Ansatz sein.
Ob ein solcher Ansatz als sicher und ansonsten angemessen angesehen werden sollte oder nicht, hängt von den Details Ihres Anwendungsfalls ab, die Sie (zum Zeitpunkt des Schreibens) nicht angegeben haben.
Es wurde nicht geprüft.
Obwohl ich versucht habe, beim Schreiben dieses Skripts Vorsicht walten zu lassen, wurde es nicht ordnungsgemäß auf Sicherheitslücken überprüft . Es ist als Demonstration gedacht und wäre "Alpha" -Software, wenn es als Teil eines Projekts veröffentlicht würde. Außerdem...
Ein anderer Benutzer, der "zusieht", ist möglicherweise in der Lage, das Salz des Benutzers zu entdecken .
Aufgrund von Einschränkungen beim mkpasswd
Akzeptieren von Salt-Daten enthält dieses Skript eine bekannte Sicherheitslücke , die Sie je nach Anwendungsfall für akzeptabel halten oder nicht. Standardmäßig können Benutzer auf Ubuntu und den meisten anderen GNU / Linux-Systemen Informationen zu Prozessen anzeigen, die von anderen Benutzern (einschließlich root) ausgeführt werden, einschließlich ihrer Befehlszeilenargumente. Weder die Eingabe des Benutzers noch der gespeicherte Kennwort-Hash werden als Befehlszeilenargument an ein externes Dienstprogramm übergeben. Das aus der Datenbank extrahierte Salt wird jedoch als Befehlszeilenargument für angegeben , da das Dienstprogramm nur so ein Salt als Eingabe akzeptiert.shadow
mkpasswd
Wenn
- ein anderer Benutzer im System oder
- Jeder, der die Möglichkeit hat, ein Benutzerkonto zu
www-data
erstellen (z. B. ), führt seinen Code aus, oder
- Jeder, der ansonsten Informationen zu laufenden Prozessen anzeigen kann (auch durch manuelles Überprüfen von Einträgen in
/proc
)
kann die Befehlszeilenargumente so prüfen, mkpasswd
wie sie von diesem Skript ausgeführt werden, und dann eine Kopie des Salt des Benutzers aus der shadow
Datenbank abrufen. Sie müssen möglicherweise raten können, wann dieser Befehl ausgeführt wird, aber das ist manchmal erreichbar.
Ein Angreifer mit Ihrem Salz ist nicht so schlecht wie ein Angreifer mit Ihrem Salz und Hasch , aber es ist nicht ideal. Das Salz liefert nicht genug Informationen, damit jemand Ihr Passwort herausfinden kann. Aber es erlaubt jemandem, Regenbogentabellen oder vorberechnete Wörterbuch-Hashes zu generieren, die für diesen Benutzer auf diesem System spezifisch sind. Dies ist anfangs wertlos. Wenn Ihre Sicherheit jedoch zu einem späteren Zeitpunkt gefährdet wird und der vollständige Hash erhalten wird, kann das Kennwort des Benutzers möglicherweise schneller geknackt werden, bevor er die Möglichkeit hat, es zu ändern.
Daher ist diese Sicherheitslücke ein erschwerender Faktor in einem komplexeren Angriffsszenario und keine vollständig ausnutzbare Sicherheitsanfälligkeit. Und Sie könnten die obige Situation für weit hergeholt halten. Aber ich bin nur ungern jede Methode für die allgemeinen, realen Einsatz zu empfehlen , dass Lecks jegliche nicht-öffentliche Daten aus /etc/shadow
zu einem Nicht-Root - Benutzer.
Sie können dieses Problem vollständig vermeiden, indem Sie:
- Schreiben eines Teils Ihres Skripts in Perl oder einer anderen Sprache, mit der Sie C-Funktionen aufrufen können, wie in Gilles 'Antwort auf die zugehörige Unix.SE-Frage gezeigt , oder
- Schreiben Sie Ihr gesamtes Skript / Programm in einer solchen Sprache, anstatt bash zu verwenden. (Basierend auf der Art und Weise, wie Sie die Frage markiert haben, scheint es, dass Sie Bash bevorzugen.)
Seien Sie vorsichtig, wie Sie dieses Skript aufrufen.
Seien Sie vorsichtig, wenn Sie einem nicht vertrauenswürdigen Benutzer erlauben, dieses Skript als Root oder einen Prozess als Root auszuführen, der dieses Skript aufruft . Durch Ändern der Umgebung können sie dieses Skript - oder jedes Skript, das als Root ausgeführt wird - zu beliebigen Aktionen veranlassen . Sofern Sie dies nicht verhindern können, dürfen Sie Benutzern keine erhöhten Berechtigungen für die Ausführung von Shell-Skripten gewähren.
Siehe 10.4. Weitere Informationen zu Shell-Skriptsprachen (sh- und csh-Derivate) finden Sie in David A. Wheelers Secure Programming für Linux und Unix HOWTO . Während sich sein Vortrag auf Setuid-Skripte konzentriert, können andere Mechanismen denselben Problemen zum Opfer fallen, wenn sie die Umgebung nicht ordnungsgemäß bereinigen.
Weitere Hinweise
Es unterstützt nur das Lesen von Hashes aus der shadow
Datenbank.
Passwörter müssen mit einem Shadow versehen sein, damit dieses Skript funktioniert (dh ihre Hashes sollten sich in einer separaten /etc/shadow
Datei befinden, die nur root lesen kann, nicht in /etc/passwd
).
Dies sollte in Ubuntu immer der Fall sein. In jedem Fall kann das Skript , wenn nötig werden , um zu lesen Passwort - Hashes von trivialer Weise erweitert passwd
sowie shadow
.
Halten Sie IFS
daran , wenn Sie dieses Skript zu ändern.
Ich setze IFS=$
am Anfang, da die drei Daten im Hashfeld eines Schatteneintrags durch getrennt sind $
.
- Sie haben auch einen führenden
$
, weshalb der Hash-Typ und das Salz eher "${ent[1]}"
und "${ent[2]}"
als "${ent[0]}"
und "${ent[1]}"
sind.
Die einzigen Stellen in diesem Skript, an denen $IFS
festgelegt wird, wie die Shell Wörter aufteilt oder kombiniert
Wenn diese Daten in ein Array aufgeteilt werden, initialisieren Sie sie anhand der nicht zitierten $(
)
Befehlsersetzung in:
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
Wenn das Array in eine Zeichenfolge rekonstruiert wird, die mit dem vollständigen Feld von verglichen werden soll shadow
, wird der folgende "${ent[*]}"
Ausdruck verwendet:
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
Wenn Sie das Skript ändern und in anderen Situationen eine Wortteilung (oder Wortverbindung) durchführen lassen, müssen Sie IFS
für verschiedene Befehle oder verschiedene Teile des Skripts unterschiedliche Werte festlegen.
Wenn Sie dies nicht berücksichtigen und davon ausgehen $IFS
, dass das übliche Leerzeichen ( $' \t\n'
) verwendet wird, kann dies dazu führen, dass sich Ihr Skript auf merkwürdige Weise verhält.