Apa cara paling aman dan paling sederhana untuk memiliki kata sandi yang diketik pengguna di bash menjadi bagian dari stdin ke suatu program?

12

Saya mencari (1) cara paling aman dan (2) paling sederhana untuk meminta pengguna mengetikkan kata sandi di bash shell prompt dan menjadikan kata sandi itu menjadi bagian dari stdin ke suatu program.

Ini adalah apa yang perlu stdin terlihat seperti:, di {"username":"myname","password":"<my-password>"}mana <my-password>apa yang diketikkan ke prompt shell. Jika saya memiliki kendali atas program stdin, maka saya bisa memodifikasinya untuk secara aman meminta kata sandi dan meletakkannya, tetapi downstream adalah perintah tujuan umum standar.

Saya telah mempertimbangkan dan menolak pendekatan yang menggunakan yang berikut:

  • pengguna mengetik kata sandi ke dalam baris perintah: kata sandi akan ditampilkan di layar dan juga akan terlihat oleh semua pengguna melalui "ps"
  • interpolasi variabel shell menjadi argumen ke program eksternal (misalnya, ...$PASSWORD...): kata sandi masih akan terlihat oleh semua pengguna melalui "ps"
  • variabel lingkungan (jika dibiarkan di lingkungan): kata sandi akan terlihat oleh semua proses anak; bahkan proses yang dapat dipercaya dapat mengekspos kata sandi jika mereka membuang inti atau membuang variabel lingkungan sebagai bagian dari diagnostik
  • kata sandi yang tersimpan dalam file untuk waktu yang lama, bahkan file dengan izin ketat: pengguna dapat secara tidak sengaja mengekspos kata sandi dan pengguna root mungkin secara tidak sengaja melihat kata sandi

Saya akan meletakkan solusi saya saat ini sebagai jawaban di bawah, tetapi dengan senang hati akan memilih jawaban yang lebih baik jika seseorang datang dengan satu. Saya pikir harus ada sesuatu yang lebih sederhana atau mungkin seseorang melihat masalah keamanan yang saya lewatkan.

Jim Hoagland
sumber
(Kasus penggunaan penuh adalah bahwa kata sandi perlu menjadi bagian dari tubuh POST ke server HTTPS, tetapi saya tidak ingin membuat pertanyaan ini terlalu spesifik. Stdin menjadi badan POST melalui curl "-d @ - ".)
Jim Hoagland

Jawaban:

14

Dengan bashatau 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

Tanpa IFS=, readakan menghapus blanko terkemuka dan tertinggal dari kata sandi yang Anda ketikkan.

Tanpa -r, itu akan memproses garis miring terbalik sebagai karakter mengutip.

Anda ingin memastikan bahwa Anda hanya pernah membaca dari terminal.

echotidak bisa digunakan dengan andal. Di bashdan zsh, printfdibangun sehingga baris perintah tidak akan ditampilkan di output ps.

Di bash, Anda perlu mengutip $passwordkarena jika tidak operator split + glob diterapkan padanya.

Itu masih salah karena Anda harus menyandikan string itu sebagai JSON. Sebagai contoh, double-quote dan backslash setidaknya akan menjadi masalah. Anda mungkin perlu khawatir tentang pengkodean karakter tersebut. Apakah program Anda mengharapkan string UTF-8? Apa yang dikirim terminal Anda?

Untuk menambahkan string prompt, dengan zsh:

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

Dengan bash:

IFS= read -rsp 'Please enter a password: ' password
Stéphane Chazelas
sumber
printf - itu yang kita butuhkan untuk menghindari doa perl - tip yang baik. Adalah baik bahwa Anda memiliki unset passworddan di set +asana, meskipun itu dapat dilewati jika Anda dapat membuat lebih banyak asumsi tentang shell saat ini. Poin bagus tentang opsi -r untuk membaca dan mengedepankannya IFS=untuk generalisasi yang lebih baik dengan beragam kata sandi. Baik untuk beralih dari / dev / tty untuk memaksa input dari terminal.
Jim Hoagland
1
ya, kami masih memiliki masalah dengan double-quote dan backslash dan mungkin beberapa karakter lainnya. Kita harus menyandikannya sebelum memasukkannya ke dalam kolom tanda kutip ganda di gumpalan JSON. Tidak yakin apakah curl mengharapkan UTF-8.
Jim Hoagland
1
python3 -c 'import json; print(json.dumps({"username": "myname", "password": input()}))', Jika Anda memiliki Python di sekitar. Untuk Python 2, s / input / raw_input /. Jika Anda memasukkan kata sandi ke perintah itu, itu akan memuntahkan JSON yang sesuai.
Kevin
Kevin, itu saran yang bagus jika kita bisa memasukkan kata sandi dengan aman ke stdin dan tidak keberatan memanggil python. Ini membangun JSON dengan cepat. Ini akan bekerja dengan baik jika menggunakan getpass.getpass () di dalam Python, meskipun Anda kehilangan kemampuan untuk tetap menggunakan kata sandi yang disimpan berulang kali.
Jim Hoagland 3-15
2

Inilah solusi yang saya miliki (sudah diuji):

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

Penjelasan:

  1. read -smembaca garis stdin (kata sandi) tanpa menggema garis ke layar. Ini menyimpan dalam variabel PASSWORD shell.
  2. echo -n $PASSWORDmenempatkan kata sandi di stdout tanpa baris baru. (Gema adalah perintah bawaan shell sehingga tidak ada proses baru yang dibuat sehingga (AFAIK) kata sandi sebagai argumen untuk gema tidak akan ditampilkan pada ps.)
  3. perl dipanggil dan membaca kata sandi dari stdin ke $_
  4. perl memasukkan kata sandi $_ke dalam teks lengkap dan mencetaknya ke stdout

Jika ini menuju HTTPS POST, baris kedua akan menjadi seperti:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>
Jim Hoagland
sumber
3
Ini terlihat cukup bagus. Saya hanya akan mencatat tidak ada alasan untuk memohon perl sama sekali; Anda dapat dengan sempurna melakukan sesuatu seperti printf '{"username":"myname","password":"%s"}' $PASSWORD, yang mempertahankan properti keamanan yang sama dan menghemat proses eksternal.
Tom Hunt
2
Jika Anda ingin menggeneralisasi solusi untuk readperintah yang tidak mendukung flag -s, Anda dapat menggunakan "stty -echo; baca PASSWORD; stty echo"
Jeff Schaller
2
Pipa memulai dua proses baru, satu untuk setiap sisi. Gunakan sini-string sebagai gantinya: perl -e '...' <<< "$PASSWORD".
chepner
2
@ chepner, <<<dalam bashmenyimpan konten dalam file sementara yang lebih buruk.
Stéphane Chazelas
1
Menurut TLDP itu membuat file tetapi tidak dapat dibaca oleh proses lain. "Di sini dokumen membuat file sementara, tetapi file-file ini dihapus setelah dibuka dan tidak dapat diakses oleh proses lain." tldp.org/LDP/abs/html/here-docs.html tepat setelah Contoh 19-12.
dragon788
0

Jika versi readAnda tidak mendukung, -sAnda mencoba cara yang kompatibel POSIX yang terlihat dalam jawaban ini .

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

The set +aseharusnya untuk mencegah auto "ekspor" dari variabel lingkungan. Anda harus memeriksa halaman manual untuk stty, ada banyak opsi yang tersedia. Itu <<<"$passwd"dikutip karena kata sandi yang baik dapat memiliki ruang. Yang terakhir echosetelah mengaktifkan stty echoadalah untuk memiliki perintah / output berikutnya mulai pada baris baru.

dragon788
sumber