วิธีที่ปลอดภัยและง่ายที่สุดในการใช้รหัสผ่านที่ผู้ใช้พิมพ์บน bash กลายเป็นส่วนหนึ่งของ stdin ไปยังโปรแกรมอย่างไร


12

ฉันกำลังมองหา (1) ที่ปลอดภัยที่สุดและ (2) วิธีที่ง่ายที่สุดในการให้ผู้ใช้พิมพ์รหัสผ่านบน bash shell prompt และให้รหัสผ่านนั้นกลายเป็นส่วนหนึ่งของ stdin ไปยังโปรแกรม

นี่คือสิ่งที่ stdin จำเป็นต้องมีลักษณะ: {"username":"myname","password":"<my-password>"}ซึ่ง<my-password>เป็นสิ่งที่พิมพ์ลงในเชลล์พรอมต์ ถ้าฉันควบคุมโปรแกรม stdin ได้ฉันสามารถแก้ไขมันเพื่อขอรหัสผ่านอย่างปลอดภัยและใส่ลงไป แต่ downstream เป็นคำสั่งทั่วไปที่เป็นมาตรฐาน

ฉันได้พิจารณาและปฏิเสธวิธีการที่ใช้สิ่งต่อไปนี้:

  • ผู้ใช้ที่พิมพ์รหัสผ่านลงในบรรทัดคำสั่ง: รหัสผ่านจะปรากฏบนหน้าจอและผู้ใช้ทุกคนสามารถมองเห็นได้ด้วย "ps"
  • การแก้ไขตัวแปรเชลล์ในอาร์กิวเมนต์ของโปรแกรมภายนอก (เช่น...$PASSWORD...): รหัสผ่านจะยังคงปรากฏให้ผู้ใช้ทุกคนเห็นผ่าน "ps"
  • ตัวแปรสภาพแวดล้อม (ถ้าทิ้งไว้ในสภาพแวดล้อม): รหัสผ่านจะสามารถมองเห็นได้ในกระบวนการลูกทั้งหมด แม้กระบวนการที่น่าเชื่อถืออาจเปิดเผยรหัสผ่านหากพวกเขาถ่ายโอนข้อมูลหลักหรือตัวแปรสภาพแวดล้อมการถ่ายโอนข้อมูลเป็นส่วนหนึ่งของการวินิจฉัย
  • รหัสผ่านที่อยู่ในไฟล์เป็นระยะเวลานานแม้ไฟล์ที่มีสิทธิ์ จำกัด : ผู้ใช้อาจเผลอเปิดเผยรหัสผ่านและผู้ใช้รูทอาจบังเอิญเห็นรหัสผ่าน

ฉันจะแก้ปัญหาปัจจุบันของฉันเป็นคำตอบด้านล่าง แต่จะเลือกคำตอบที่ดีกว่าถ้ามีคนมาด้วย ฉันคิดว่าควรจะมีบางสิ่งที่ง่ายกว่าหรืออาจมีบางคนเห็นข้อกังวลด้านความปลอดภัยที่ฉันพลาดไป


(กรณีใช้อย่างเต็มรูปแบบคือรหัสผ่านจำเป็นต้องเป็นส่วนหนึ่งของเนื้อความของ POST ไปยังเซิร์ฟเวอร์ HTTPS แต่ฉันไม่ต้องการให้คำถามนี้เจาะจงเกินไป stdin กลายเป็นเนื้อความ POST ผ่านทาง curl "-d @ - ".)
Jim Hoagland

คำตอบ:


14

ด้วยbashหรือ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

โดยไม่IFS=, readจะดึงชั้นนำและต่อท้ายช่องว่างจากรหัสผ่านที่คุณพิมพ์

หากไม่มี-rก็จะดำเนินการแบ็กสแลชเป็นอักขระที่อ้างถึง

คุณต้องการให้แน่ใจว่าคุณอ่านจากเทอร์มินัลเท่านั้น

echoไม่สามารถใช้อย่างน่าเชื่อถือ ในbashและzsh, printfเป็น builtin psดังนั้นบรรทัดคำสั่งที่จะไม่แสดงในการส่งออกของ

ในbashคุณจะต้องพูด$passwordเช่นมิฉะนั้นตัวดำเนินการแยก + globถูกนำไปใช้กับมัน

ยังคงผิดพลาดอยู่เหมือนคุณจะต้องเข้ารหัสสตริงนั้นเป็น JSON ตัวอย่างเช่นเครื่องหมายคำพูดคู่และแบ็กสแลชอย่างน้อยจะเป็นปัญหา คุณอาจต้องกังวลเกี่ยวกับการเข้ารหัสของตัวละครเหล่านั้น โปรแกรมของคุณคาดหวังสตริง UTF-8 หรือไม่? เทอร์มินัลของคุณส่งอะไร

ในการเพิ่มสตริงให้โดยzsh:

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

ด้วยbash:

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

printf - นั่นคือสิ่งที่เราต้องหลีกเลี่ยงการขอร้อง Perl - เคล็ดลับที่ดี เป็นเรื่องดีที่คุณมีunset passwordและset +aแม้ว่าจะสามารถข้ามไปได้หากคุณสามารถตั้งสมมติฐานเพิ่มเติมเกี่ยวกับเชลล์ปัจจุบันได้ จุดดีเกี่ยวกับตัวเลือก -r ในการอ่านและวางไว้IFS=ข้างหน้าเพื่อความเป็นสากลที่ดีขึ้นด้วยรหัสผ่านที่หลากหลาย ดีไปจาก / dev / tty เพื่อบังคับอินพุตจากเทอร์มินัล
Jim Hoagland

1
ใช่เรายังคงมีปัญหากับเครื่องหมายคำพูดคู่และแบ็กสแลชและอาจเป็นตัวละครอื่น ๆ เราจะต้องเข้ารหัสสิ่งเหล่านั้นก่อนที่จะใส่เข้าไปในช่องที่มีเครื่องหมายคำพูดคู่ในบล็อก JSON ไม่แน่ใจว่าถ้าขดคาดว่า UTF-8
Jim Hoagland

1
python3 -c 'import json; print(json.dumps({"username": "myname", "password": input()}))'ถ้าคุณมี Python อยู่รอบ ๆ สำหรับ Python 2, s / input / raw_input / หากคุณไพพ์รหัสผ่านลงในคำสั่งนั้นมันจะคาย JSON ที่เหมาะสม
เควิน

Kevin นั่นจะเป็นคำแนะนำที่ดีถ้าเราสามารถรับรหัสผ่านอย่างปลอดภัยใน stdin และไม่สนใจที่จะเรียกใช้ python สิ่งนี้สร้าง JSON ได้ทันที สิ่งนี้จะทำงานได้ดีถ้าใช้ getpass.getpass () ใน Python แม้ว่าคุณจะสูญเสียความสามารถในการใช้รหัสผ่านที่เก็บไว้ซ้ำ ๆ
Jim Hoagland

2

นี่คือทางออกที่ฉันมี (ผ่านการทดสอบแล้ว):

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

คำอธิบาย:

  1. read -sอ่านบรรทัด stdin (รหัสผ่าน) โดยไม่ต้องสะท้อนบรรทัดไปที่หน้าจอ มันเก็บอยู่ในตัวแปรเชลล์รหัสผ่าน
  2. echo -n $PASSWORDใส่รหัสผ่านใน stdout โดยไม่ต้องขึ้นบรรทัดใหม่ (echo เป็นคำสั่งในตัวของเชลล์ดังนั้นจึงไม่มีการสร้างกระบวนการใหม่ดังนั้น (AFAIK) รหัสผ่านเนื่องจากอาร์กิวเมนต์สำหรับ echo จะไม่แสดงบน ps)
  3. Perl ถูกเรียกและอ่านรหัสผ่านจาก stdin ถึง $_
  4. perl ใส่รหัสผ่าน$_เป็นข้อความเต็มและพิมพ์เป็น stdout

ถ้านี่จะเป็น HTTPS POST บรรทัดที่สองจะเป็นดังนี้:

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

3
มันดูดีทีเดียว ฉันจะทราบว่าไม่มีเหตุผลที่จะเรียกใช้ Perl ได้เลย คุณสามารถทำสิ่งที่ชอบได้อย่างสมบูรณ์แบบprintf '{"username":"myname","password":"%s"}' $PASSWORDซึ่งยังคงคุณสมบัติความปลอดภัยที่เหมือนกันและช่วยให้คุณประหยัดกระบวนการภายนอก
Tom Hunt

2
หากคุณต้องการวางแนวทางแก้ปัญหาให้กับreadคำสั่งที่ไม่สนับสนุนแฟล็ก -s คุณสามารถใช้ "stty -echo; อ่าน PASSWORD; stty echo"
Jeff Schaller

2
ไปป์เริ่มกระบวนการใหม่ที่สองกระบวนการหนึ่งสำหรับแต่ละด้าน ใช้สตริงที่นี่แทน: perl -e '...' <<< "$PASSWORD".
chepner

2
@chepner <<<ในการจัดbashเก็บเนื้อหาในไฟล์ชั่วคราวที่แย่กว่านั้น
Stéphane Chazelas

1
ตาม TLDP มันสร้างไฟล์ แต่มันไม่สามารถอ่านได้โดยกระบวนการอื่น ๆ "เอกสารนี้สร้างไฟล์ชั่วคราว แต่ไฟล์เหล่านี้จะถูกลบหลังจากเปิดและไม่สามารถเข้าถึงกระบวนการอื่น ๆ ได้" tldp.org/LDP/abs/html/here-docs.htmlหลังตัวอย่าง 19-12
dragon788

0

หากเวอร์ชันของคุณreadไม่รองรับ-sคุณลองใช้วิธีที่เข้ากันได้กับ POSIX ที่เห็นในคำตอบนี้

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

set +aควรจะป้องกันไม่ให้อัตโนมัติ "ส่งออก" ของตัวแปรที่มีต่อสิ่งแวดล้อม คุณควรตรวจสอบหน้า man สำหรับsttyมีตัวเลือกมากมาย <<<"$passwd"จะยกมาเพราะรหัสผ่านที่ดีสามารถมีช่องว่าง สุดท้ายechoหลังจากเปิดใช้งานstty echoคือการมีคำสั่ง / ผลลัพธ์ต่อไปเริ่มต้นในบรรทัดใหม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.