Vì bạn muốn làm điều này trong một tập lệnh shell, một vài đóng góp trong Cách kiểm tra mật khẩu với Linux? (trên Unix.SE , được đề xuất bởi AB ) đặc biệt có liên quan:
Để kiểm tra thủ công xem một chuỗi có thực sự là mật khẩu của người dùng hay không, bạn phải băm nó bằng thuật toán băm giống như trong mục nhập bóng của người dùng, với cùng một muối như trong mục nhập bóng của người dùng. Sau đó, nó có thể được so sánh với mật khẩu băm được lưu trữ ở đó.
Tôi đã viết một kịch bản hoàn chỉnh, làm việc chứng minh làm thế nào để làm điều này.
- Nếu bạn đặt tên cho nó
chkpass
, bạn có thể chạy và nó sẽ đọc một dòng từ đầu vào tiêu chuẩn và kiểm tra xem đó có phải là mật khẩu của bạn không.chkpass user
user
- Cài đặt gói whois
để có được mkpasswd
tiện ích mà tập lệnh này phụ thuộc vào.
- Kịch bản này phải được chạy như root để thành công.
- Trước khi sử dụng tập lệnh này hoặc bất kỳ phần nào của tập lệnh để thực hiện công việc thực tế, vui lòng xem Ghi chú bảo mật bên dưới.
#!/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
Ghi chú bảo mật
Nó có thể không phải là phương pháp đúng đắn.
Việc tiếp cận như thế này có nên được coi là an toàn hay không và phù hợp tùy thuộc vào chi tiết về trường hợp sử dụng của bạn mà bạn chưa cung cấp (kể từ khi viết bài này).
Nó đã không được kiểm toán.
Mặc dù tôi đã cố gắng thực hiện việc chăm sóc trong khi viết kịch bản này, nhưng nó đã không được kiểm tra chính xác về các lỗ hổng bảo mật . Nó được dùng như một bản trình diễn và sẽ là phần mềm "alpha" nếu được phát hành như một phần của dự án. Hơn nữa ...
Một người dùng khác đang "xem" có thể phát hiện ra muối của người dùng .
Do những hạn chế trong cách mkpasswd
chấp nhận dữ liệu muối, tập lệnh này chứa một lỗ hổng bảo mật đã biết , mà bạn có thể hoặc không thể xem là chấp nhận được tùy theo trường hợp sử dụng. Theo mặc định, người dùng trên Ubuntu và hầu hết các hệ thống GNU / Linux khác có thể xem thông tin về các quy trình được chạy bởi những người dùng khác (bao gồm cả root), bao gồm cả các đối số dòng lệnh của họ. Cả đầu vào và mật khẩu băm của người dùng đều không được truyền dưới dạng đối số dòng lệnh cho bất kỳ tiện ích bên ngoài nào. Nhưng muối , được trích xuất từ shadow
cơ sở dữ liệu, được đưa ra dưới dạng đối số dòng lệnh mkpasswd
, vì đây là cách duy nhất mà tiện ích chấp nhận muối làm đầu vào.
Nếu
- người dùng khác trên hệ thống, hoặc
- bất cứ ai có khả năng tạo bất kỳ tài khoản người dùng nào (ví dụ
www-data
:) chạy mã của họ, hoặc
- bất cứ ai khác có thể xem thông tin về các quy trình đang chạy (bao gồm bằng cách kiểm tra thủ công các mục trong
/proc
)
có thể kiểm tra các đối số dòng lệnh để mkpasswd
nó được chạy bởi tập lệnh này, sau đó chúng có thể lấy một bản sao muối của người dùng từ shadow
cơ sở dữ liệu. Họ có thể phải đoán khi nào lệnh đó được chạy, nhưng điều đó đôi khi có thể đạt được.
Kẻ tấn công bằng muối của bạn không tệ như kẻ tấn công bằng muối và băm của bạn , nhưng nó không lý tưởng. Muối không cung cấp đủ thông tin để ai đó khám phá mật khẩu của bạn. Nhưng nó cho phép ai đó tạo các bảng cầu vồng hoặc băm từ điển được tính toán trước cụ thể cho người dùng đó trên hệ thống đó. Điều này ban đầu là vô giá trị, nhưng nếu bảo mật của bạn bị xâm phạm vào một ngày sau đó và lấy được hàm băm đầy đủ, thì nó có thể bị bẻ khóa nhanh hơn để lấy mật khẩu của người dùng trước khi họ có cơ hội thay đổi nó.
Do đó, lỗ hổng bảo mật này là một yếu tố làm trầm trọng thêm trong một kịch bản tấn công phức tạp hơn là một lỗ hổng có thể khai thác hoàn toàn. Và bạn có thể xem xét tình huống trên rất xa. Nhưng tôi miễn cưỡng đề xuất bất kỳ phương pháp nào cho việc sử dụng chung, trong thế giới thực, rò rỉ bất kỳ dữ liệu không công khai nào từ /etc/shadow
người dùng không root.
Bạn có thể tránh vấn đề này hoàn toàn bằng cách:
- viết một phần tập lệnh của bạn bằng Perl hoặc một số ngôn ngữ khác cho phép bạn gọi các hàm C, như thể hiện trong câu trả lời của Gilles cho câu hỏi Unix.SE liên quan , hoặc
- viết toàn bộ kịch bản / chương trình của bạn bằng ngôn ngữ như vậy, thay vì sử dụng bash. (Dựa trên cách bạn đã gắn thẻ câu hỏi, có vẻ như bạn thích sử dụng bash hơn.)
Hãy cẩn thận làm thế nào bạn gọi kịch bản này.
Nếu bạn cho phép người dùng không tin cậy chạy tập lệnh này với quyền root hoặc chạy bất kỳ quy trình nào dưới dạng root gọi tập lệnh này, hãy cẩn thận . Bằng cách thay đổi môi trường, họ có thể tạo tập lệnh này - hoặc bất kỳ tập lệnh nào chạy bằng root - làm bất cứ điều gì . Trừ khi bạn có thể ngăn điều này xảy ra, bạn không được phép người dùng nâng cao đặc quyền để chạy các kịch bản shell.
Xem 10,4. Shell Scripting Languages (sh và csh phái sinh) trong David A. Wheeler Lập trình bảo mật cho Linux và Unix HOWTO để biết thêm thông tin về điều này. Mặc dù bài thuyết trình của ông tập trung vào các tập lệnh setuid, các cơ chế khác có thể trở thành con mồi của một số vấn đề tương tự nếu chúng không vệ sinh môi trường một cách chính xác.
Ghi chú khác
Nó chỉ hỗ trợ đọc băm từ shadow
cơ sở dữ liệu.
Mật khẩu phải được làm mờ để tập lệnh này hoạt động (nghĩa là băm của chúng phải ở trong một /etc/shadow
tệp riêng biệt mà chỉ root mới có thể đọc, không phải trong /etc/passwd
).
Điều này luôn luôn là trường hợp trong Ubuntu. Trong mọi trường hợp, nếu cần kịch bản có thể được trivially mở rộng để đọc băm mật khẩu từ passwd
cũng như shadow
.
Hãy IFS
ghi nhớ khi sửa đổi tập lệnh này.
Tôi đặt IFS=$
ở đầu, vì ba dữ liệu trong trường băm của mục nhập bóng được phân tách bằng $
.
- Họ cũng có một hàng đầu
$
, đó là lý do tại sao các loại băm và muối là "${ent[1]}"
và "${ent[2]}"
hơn "${ent[0]}"
và "${ent[1]}"
tương ứng.
Các vị trí duy nhất trong tập lệnh này $IFS
xác định cách shell phân tách hoặc kết hợp các từ
khi các dữ liệu này được chia thành một mảng, bằng cách khởi tạo nó từ sự $(
)
thay thế lệnh không được trích dẫn trong:
set -f; ent=($(getent shadow "$1" | awk -F: '{print $2}')); set +f
khi mảng được hoàn nguyên thành một chuỗi để so sánh với trường đầy đủ từ đó shadow
, "${ent[*]}"
biểu thức trong:
if [[ "${ent[*]}" = "$(mkpasswd -sm $hashtype -S "${ent[2]}" <<<"$pass")" ]]
Nếu bạn sửa đổi tập lệnh và để nó thực hiện phân tách từ (hoặc nối từ) trong các tình huống khác, bạn sẽ cần đặt IFS
thành các giá trị khác nhau cho các lệnh khác nhau hoặc các phần khác nhau của tập lệnh.
Nếu bạn không ghi nhớ điều này và giả sử $IFS
được đặt thành khoảng trắng thông thường ( $' \t\n'
), cuối cùng bạn có thể khiến tập lệnh của mình hoạt động theo một số cách có vẻ khá kỳ lạ.