Оскільки ви хочете зробити це в сценарії оболонки, декілька внесків у розділі Як перевірити пароль за допомогою Linux? (на Unix.SE , запропонований AB ) особливо актуальні:
Щоб вручну перевірити, чи дійсно рядок є паролем користувача, ви повинні хеш-версію з тим самим алгоритмом хешу, що і в тіньовому записі користувача, з тією ж сіллю, що і в тіньовому записі користувача. Тоді його можна порівняти з збереженим там хешем паролів.
Я написав повний робочий сценарій, який демонструє, як це зробити.
- Якщо ви дасте ім'я
chkpass, можете запустити, і він прочитає рядок зі стандартного вводу та перевірить, чи це пароль.chkpass useruser
- Встановіть пакет 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
Примітки безпеки
Це може бути не правильний підхід.
Незалежно від того, чи слід вважати такий підхід захищеним, а іншим чином підходящим, залежить від деталей щодо вашої справи використання, яку ви не надали (станом на це написання).
Він не перевірявся.
Хоча я намагався бути обережним під час написання цього сценарію, він не був належним чином перевірений на предмет вразливості безпеки . Він задуманий як демонстрація і був би "альфа" програмним забезпеченням, якщо його випустили в рамках проекту. Крім того ...
Інший користувач, який "дивиться", може виявити сіль користувача .
Через обмеження в прийняттіmkpasswd сольових даних, цей скрипт містить відомий недолік безпеки , який ви можете вважати або не вважати прийнятним залежно від випадку використання. За замовчуванням користувачі Ubuntu та більшості інших систем GNU / Linux можуть переглядати інформацію про процеси, якими керуються інші користувачі (включаючи root), включаючи аргументи їх командного рядка. Ні введення користувача, ні збережений хеш пароля не передаються як аргумент командного рядка будь-якій зовнішній утиліті. Але сіль , витягнута з shadowбази даних, буде дана в якості аргументу командного рядка для mkpasswd, так як це єдиний спосіб , яким утиліта приймає сіль в якості вхідних даних.
Якщо
- інший користувач у системі, або
- будь-хто, хто має можливість змусити будь-який обліковий запис користувача (наприклад,
www-data) запустити свій код, або
- будь-хто, хто в іншому випадку може переглядати інформацію про запущені процеси (в тому числі, перевіряючи записи вручну
/proc)
може перевірити аргументи командного рядка на те, mkpasswdяк він керується цим скриптом, тоді вони можуть отримати копію солі користувача з shadowбази даних. Вони , можливо , повинні бути в змозі вгадати , коли ця команда виконується, але іноді можна досягти.
Зловмисник з сіллю не такий поганий, як зловмисник з сіллю і хешем , але це не ідеально. Сіль не надає достатньо інформації, щоб хтось відкрив ваш пароль. Але це дозволяє комусь генерувати веселкові таблиці або попередньо обчислені хеші словника, характерні для цього користувача в цій системі. Це спочатку нічого не варте, але якщо ваша безпека буде порушена згодом і буде отримано повний хеш, він може бути зламаний швидше, щоб отримати пароль користувача, перш ніж вони отримають шанс змінити його.
Таким чином, цей недолік безпеки є загострюючим фактором у складнішому сценарії атаки, а не повністю вразливою вразливістю. І ви можете вважати описану вище ситуацію надуманою. Але я неохоче рекомендую будь-який метод для загального використання в реальному світі, який пропускає будь -які непублічні дані /etc/shadowдо некористувального користувача.
Ви можете повністю уникнути цієї проблеми:
- написання частини вашого сценарію в Perl або іншою мовою , який дозволяє викликати функції C, як і показані у відповіді Жиля в до відповідного Unix.SE питання , чи
- писати весь сценарій / програму такою мовою, а не використовувати bash. (Виходячи з того, як ви позначили це питання, схоже, ви вважаєте за краще використовувати bash.)
Будьте уважні, як ви називаєте цей сценарій.
Якщо ви дозволите ненадійному користувачу запустити цей скрипт як root або запустити будь-який процес як root, який викликає цей скрипт, будьте обережні . Змінюючи середовище, вони можуть зробити цей скрипт - або будь-який сценарій, що працює як root - робити все, що завгодно . Якщо ви не можете запобігти цьому, ви не повинні дозволяти користувачам підвищених привілеїв для запуску скриптів оболонки.
Див. 10.4. Мови сценаріїв оболонки (похідні та шш-шш) в Безпечному програмуванні Девіда А. Уілера для Linux та Unix HOWTO для отримання додаткової інформації про це. Хоча його презентація зосереджена на встановлених сценаріях, інші механізми можуть стати здобиччю деяких тих же проблем, якщо вони неправильно не санітують навколишнє середовище.
Інші примітки
Він підтримує хеші для читання лише з shadowбази даних.
Паролі повинні бути затінені, щоб цей сценарій працював (тобто їх хеші повинні знаходитися в окремому /etc/shadowфайлі, який може читати тільки root, а не в /etc/passwd).
Це завжди має бути в Ubuntu. У будь-якому випадку, якщо потрібно, сценарій можна тривіально розширити, щоб читати хеши паролів 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'), ви можете в кінцевому підсумку змусити ваш сценарій вести себе якимись дивними, здавалося б, способами.