Cách an toàn và đơn giản nhất để mật khẩu người dùng gõ vào bash trở thành một phần của stdin cho chương trình là gì?


12

Tôi đang tìm cách (1) an toàn nhất và (2) cách đơn giản nhất để người dùng nhập mật khẩu trên dấu nhắc bash shell và để mật khẩu đó trở thành một phần của stdin cho chương trình.

Đây là những gì stdin cần phải trông giống như : {"username":"myname","password":"<my-password>"}, đâu <my-password>là những gì đã được gõ vào dấu nhắc shell. Nếu tôi có quyền kiểm soát chương trình stdin, thì tôi có thể sửa đổi nó thành lời nhắc an toàn cho mật khẩu và đặt nó vào vị trí, nhưng hạ lưu là một lệnh mục đích chung tiêu chuẩn.

Tôi đã xem xét và từ chối các phương pháp sử dụng như sau:

  • người dùng nhập mật khẩu vào dòng lệnh: mật khẩu sẽ được hiển thị trên màn hình và cũng sẽ hiển thị cho tất cả người dùng thông qua "ps"
  • Nội suy biến shell thành một đối số cho một chương trình bên ngoài (ví dụ ...$PASSWORD...:): mật khẩu vẫn sẽ hiển thị cho tất cả người dùng thông qua "ps"
  • biến môi trường (nếu chúng được để lại trong môi trường): mật khẩu sẽ hiển thị cho tất cả các tiến trình con; ngay cả các quy trình đáng tin cậy cũng có thể làm lộ mật khẩu nếu chúng kết xuất lõi hoặc kết xuất các biến môi trường như một phần của chẩn đoán
  • mật khẩu ngồi trong một tệp trong một thời gian dài, thậm chí một tệp có quyền hạn chặt chẽ: người dùng có thể vô tình để lộ mật khẩu và người dùng root có thể vô tình nhìn thấy mật khẩu

Tôi sẽ đặt giải pháp hiện tại của mình làm câu trả lời bên dưới, nhưng sẽ vui vẻ chọn câu trả lời tốt hơn nếu có ai đó đưa ra giải pháp. Tôi nghĩ rằng nên có một cái gì đó đơn giản hơn hoặc có thể ai đó nhìn thấy một mối quan tâm bảo mật mà tôi đã bỏ lỡ.


(Trường hợp sử dụng đầy đủ là mật khẩu cần một phần cơ thể của POST cho máy chủ HTTPS, nhưng tôi không muốn đặt câu hỏi này quá cụ thể. - ".)
Jim Hoagland

Câu trả lời:


13

Với bashhoặc zsh:

unset -v password # make sure it's not exported
set +o allexport  # make sure variables are not automatically exported
IFS= read -rs password < /dev/tty &&
  printf '{"username":"myname","password":"%s"}\n' "$password" | cmd

Nếu không IFS=, readsẽ loại bỏ các khoảng trống hàng đầu và dấu từ mật khẩu bạn nhập.

Nếu không -r, nó sẽ xử lý dấu gạch chéo ngược như một ký tự trích dẫn.

Bạn muốn chắc chắn rằng bạn chỉ đọc từ thiết bị đầu cuối.

echokhông thể được sử dụng đáng tin cậy. Trong bashzsh, printfđược tích hợp để dòng lệnh sẽ không hiển thị trong đầu ra của ps.

Trong bash, bạn cần trích dẫn $passwordnếu không thì toán tử split + global được áp dụng cho nó.

Điều đó vẫn sai mặc dù bạn cần mã hóa chuỗi đó dưới dạng JSON. Ví dụ, trích dẫn kép và dấu gạch chéo ngược ít nhất sẽ là một vấn đề. Bạn có thể cần phải lo lắng về mã hóa của các ký tự đó. Chương trình của bạn có mong đợi chuỗi UTF-8 không? Thiết bị đầu cuối của bạn gửi gì?

Để thêm một chuỗi dấu nhắc, với zsh:

IFS= read -rs 'password?Please enter a password: '

Với bash:

IFS= read -rsp 'Please enter a password: ' password

printf - đó là những gì chúng ta cần để tránh việc gọi perl - mẹo hay. Thật tốt khi bạn có unset passwordset +aở đó, mặc dù nó có thể được bỏ qua nếu bạn có thể đưa ra nhiều giả định về vỏ hiện tại. Điểm hay về tùy chọn -r để đọc và đặt IFS=trước nó để có tính tổng quát tốt hơn với nhiều loại mật khẩu. Tốt để đi từ / dev / tty để buộc đầu vào từ thiết bị đầu cuối.
Jim Hoagland

1
vâng, chúng tôi vẫn có một vấn đề với trích dẫn kép và dấu gạch chéo ngược và có thể là một vài ký tự khác. Chúng ta sẽ cần mã hóa chúng trước khi đặt vào bên trong trường được trích dẫn kép trong blob JSON. Không chắc chắn nếu curl mong đợi UTF-8.
Jim Hoagland

1
python3 -c 'import json; print(json.dumps({"username": "myname", "password": input()}))', nếu bạn có Python nằm xung quanh. Đối với Python 2, s / input / raw_input /. Nếu bạn đặt mật khẩu vào lệnh đó, nó sẽ nhổ JSON thích hợp.
Kevin

Kevin, đó sẽ là một gợi ý tốt nếu chúng ta có thể lấy mật khẩu một cách an toàn vào stdin và đừng bận tâm đến việc gọi trăn. Điều này xây dựng JSON một cách nhanh chóng. Điều này sẽ hoạt động tốt nếu sử dụng getpass.getpass () trong Python, mặc dù bạn mất khả năng tiếp tục sử dụng mật khẩu được lưu trữ nhiều lần.
Jim Hoagland

2

Đây là giải pháp tôi có (nó đã được thử nghiệm):

$ read -s PASSWORD
<user types password>
$ echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"'

Giải trình:

  1. read -sđọc một dòng stdin (mật khẩu) mà không lặp lại dòng trên màn hình. Nó lưu trữ trong PASSWORD biến vỏ.
  2. echo -n $PASSWORDđặt mật khẩu trên thiết bị xuất chuẩn mà không có bất kỳ dòng mới nào. (echo là một lệnh tích hợp shell nên không có quy trình mới nào được tạo nên (AFAIK) mật khẩu làm đối số cho echo sẽ không được hiển thị trên ps.)
  3. perl được gọi và đọc mật khẩu từ stdin đến $_
  4. perl đặt mật khẩu $_vào toàn văn bản và in nó ra thiết bị xuất chuẩn

Nếu điều này xảy ra với POST HTTPS, dòng thứ hai sẽ giống như:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>

3
Điều này có vẻ khá tốt. Tôi sẽ chỉ lưu ý rằng không có lý do nào để gọi perl cả; bạn hoàn toàn có thể làm tốt những việc như thế printf '{"username":"myname","password":"%s"}' $PASSWORD, giữ lại các thuộc tính bảo mật tương tự và tiết kiệm cho bạn một quy trình bên ngoài.
Tom Hunt

2
Nếu bạn muốn khái quát hóa giải pháp cho readcác lệnh không hỗ trợ cờ -s, bạn có thể sử dụng "stty -echo; đọc PASSWORD; stty echo"
Jeff Schaller

2
Các đường ống bắt đầu hai quá trình mới, một cho mỗi bên. Sử dụng chuỗi ở đây thay thế : perl -e '...' <<< "$PASSWORD".
chepner

2
@chepner, <<<trong bashcửa hàng lưu trữ nội dung trong một tệp tạm thời tệ hơn.
Stéphane Chazelas

1
Theo TLDP, nó tạo ra một tệp nhưng nó không thể đọc được bởi bất kỳ quy trình nào khác. "Ở đây các tài liệu tạo các tệp tạm thời, nhưng các tệp này bị xóa sau khi mở và không thể truy cập được đối với bất kỳ quy trình nào khác." tldp.org/LDP/abs/html/here-docs.html ngay sau Ví dụ 19-12.
dragon788

0

Nếu phiên bản readkhông hỗ trợ của -sbạn, bạn hãy thử cách tương thích POSIX được thấy trong câu trả lời này .

echo -n "USERNAME: "; read uname
echo -n "PASSWORD: "; set +a; stty -echo; read passwd; command <<<"$passwd"; set -a; stty echo; echo
passwd= # get rid of passwd possibly only necessary if running outside of a script

Điều set +anày được cho là để ngăn chặn việc tự động "xuất" một biến sang môi trường. Bạn nên kiểm tra các trang man cho stty, có rất nhiều tùy chọn có sẵn. Các <<<"$passwd"trích dẫn vì mật khẩu tốt có thể có các khoảng trống. Việc cuối cùng echosau khi kích hoạt stty echolà bắt đầu lệnh / đầu ra tiếp theo trên một dòng mới.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.