ใส่รหัสผ่านในการเรียกใช้ REST API


31

สมมติว่าฉันมี REST API ที่ใช้ในการตั้งค่า / รีเซ็ตรหัสผ่าน สมมติว่าวิธีนี้ใช้ได้กับการเชื่อมต่อ HTTPS มีเหตุผลที่ดีที่จะไม่ใส่รหัสผ่านนั้นในเส้นทางการโทรหรือเปล่าสมมุติว่าฉันจะเข้ารหัสใน BASE64

ตัวอย่างจะรีเซ็ตรหัสผ่านดังนี้:

http://www.example.com/user/joe/resetpassword/OLDPASSWD/NEWPASSWD

ฉันเข้าใจว่า BASE64 ไม่ใช่การเข้ารหัส แต่ฉันเพียงต้องการปกป้องรหัสผ่านเพื่อท่องในกรณีนี้


61
คุณกำลังแนะนำผลข้างเคียงกับ GET หรือไม่? นั่นคือการละเมิดโปรโตคอลที่นั่น
Esben Skov Pedersen

27
นี่ไม่ใช่การพักผ่อนจริง ๆ เพราะresetpassword/OLDPASSWD/NEWPASSWDไม่ใช่ทรัพยากร มันเป็นการอุทธรณ์ของกระบวนการ คุณไม่จำเป็นต้องยัดทุกอย่างลงใน URL
usr

5
@Esben: ใครบอกว่ามันเป็น GET? OP ไม่เคยพูดอย่างนั้น
dagnelies

3
จริงเขาไม่ได้อยู่ในคำถาม แต่ความคิดเห็นของเขาที่มีต่อคำตอบของ Netch กล่าวว่า "เดาว่าฉันจะต้องใช้ POST หลังจากทั้งหมด" ดังนั้นเราสามารถสรุปได้ว่าเขาตั้งใจ / ถามเกี่ยวกับ GET ซึ่งอย่างที่เอสเบนชี้ให้เห็นคือสิ่งที่ไม่ดี GET ควรอ่านอย่างเดียว
Mawg

คำตอบ:


76

เซิร์ฟเวอร์ที่ดีจะบันทึกคำขอทั้งหมดที่ส่งไปให้รวมถึง URL (มักจะไม่มีส่วนที่เป็นตัวแปรหลังจาก '?'), IP ต้นทางเวลาดำเนินการ ... คุณต้องการบันทึกนี้จริงๆ (อาจเป็นกลุ่มผู้ดูแลระบบจำนวนมากที่อ่าน) ข้อมูลที่ปลอดภัยอย่างยิ่งในฐานะรหัสผ่าน? Base64 ไม่ใช่การหยุดยั้งพวกเขา


42
นี่ไม่ใช่เหตุผลที่ดีที่สุดสำหรับการใช้ POST มันเป็นเหตุผลด้านความปลอดภัย แต่ในขณะที่ Esben ทุกคนพร้อมที่จะรับฟังความคิดเห็นการเปลี่ยนสถานะด้วย GET เป็นการละเมิดบริการส่วนที่เหลือ
Pinoniq

2
@BartFriederichs: และประวัติเบราว์เซอร์จะจดจำ URL และทดลองใช้รหัสผ่านโดยไม่ระบุตัวตนโดยสร้างหน้าเว็บที่มีลิงก์สำหรับรหัสผ่านทั้งหมดที่คุณต้องการลองและปล่อยให้ Googlebot ทำตามคำขอจริง ...
RemcoGerlich

2
"แต่เมื่อ Esben จดบันทึกความคิดเห็นไว้เรียบร้อยแล้วการเปลี่ยนสถานะด้วย GET เป็นการละเมิดบริการส่วนที่เหลือ" ฉันสังเกตเห็นความคิดเห็นนั้นเช่นกัน แต่ฉันไม่เห็นว่ามีใครบอกว่านี่เป็นคำขอ GET คุณสามารถฝังข้อมูลใน URI และยังคงโพสต์มันหลังจากทั้งหมด มันไม่ได้สงบจริงๆเลยเนื่องจาก URI ไม่ได้ตั้งชื่อทรัพยากรจริงๆ
Joshua Taylor

สวัสดีคำตอบที่ดี แต่ฉันไม่เห็นด้วยกับ"ไม่มีส่วนหลัง" '" ... มีหลายอย่างที่เก็บ URL แบบเต็ม !!!
dagnelies

2
"การเปลี่ยนสถานะด้วย GET เป็นการละเมิดบริการส่วนที่เหลือ" - อย่าให้จมอยู่ใน REST เกินไป การเปลี่ยนแปลงของรัฐที่มีการ GET คือการละเมิดของHTTP
Dan Ellis

69

สิ่งที่คุณเสนอไม่ปลอดภัยหรือสงบ

@Netch ได้พูดถึงปัญหาเกี่ยวกับบันทึกแล้ว แต่ยังมีอีกปัญหาหนึ่งที่คุณแสดงรหัสผ่านที่ถูกส่งโดย HTTP ทำให้การจับรหัสผ่านด้วยการดมสายหรือการโจมตีจากคนกลาง

เมื่อคุณทำคำขอ GET โดยใช้ REST องค์ประกอบต่าง ๆ ใน URL จะแสดงองค์ประกอบที่ละเอียดยิ่งขึ้น URL ของคุณจะอ่านเหมือนว่าคุณส่งคืน NEWPASSWD ส่วนหนึ่งของ OLDPASSWD ซึ่งเป็นส่วนหนึ่งของรหัสผ่านรีเซ็ต นั่นไม่ได้ทำให้รู้สึกถึงความหมายใด ๆ ไม่ควรใช้ GET เพื่อบันทึกข้อมูล

คุณควรทำสิ่งนี้:

POST https://www.example.com/user/joe/resetpassword/
{oldpasswd:[data], newpasswd:[data]}

POST เพราะคุณกำลังเขียนข้อมูลและ https เพราะคุณไม่ต้องการดมกลิ่น

(นี่คือการรักษาความปลอดภัยระดับต่ำมากจริงๆขั้นต่ำที่คุณควรทำ)


2
นี่ไม่ได้หมายความว่าการแฮ็รหัสผ่านที่ฝั่งไคลเอ็นต์หรือไม่? แนะนำนี้หรือไม่
โรวันฟรีแมน

1
หมายเหตุ: สิ่งนี้จะไม่อนุญาตให้มีการบังคับใช้ข้อกำหนดรหัสผ่าน (ความยาวและอื่น ๆ ) นั่นอาจไม่เป็นปัญหาในกรณีของคุณแม้ว่าจะเป็นแนวทางปฏิบัติด้านความปลอดภัยบ่อยครั้งและบางครั้งก็จำเป็นสำหรับบางหน่วยงาน
พอลเดรเปอร์

8
คุณไม่ได้สร้างบันทึกใหม่ แต่อัปเดตระเบียนที่มีอยู่ (โดยปกติ) ดังนั้นควรเป็น PUT แทนที่จะเป็น POST

4
สิ่งนี้ไม่สงบมาก resetpassword ไม่ใช่ทรัพยากรย่อยเพียงอย่างเดียว อย่างไรก็ตาม/user/joe/passwordจะดีขึ้นเล็กน้อย แต่ไม่เหมาะสม
วังวน

12
@CamilStaps ไม่คุณไม่สามารถใช้PUTเพราะPUTเป็น idempotent แต่เมื่อรหัสผ่านถูกเปลี่ยนจากsecretเป็นsupersecretสำเร็จแล้วคำขอเดียวกันจะล้มเหลวในครั้งที่สองดังนั้นจึงPOSTถูกต้องที่นี่ แน่นอนตามที่ @whirlwin กล่าวว่าทรัพยากรนี้มีชื่อไม่ดี
Residuum

60

โครงการที่เสนอมีปัญหาในหลายพื้นที่

ความปลอดภัย

เส้นทาง URL ถูกบันทึกบ่อยๆ การใส่รหัสผ่านที่ไม่ได้แฮชบนเส้นทางเป็นการปฏิบัติที่ไม่ดี

HTTP

ข้อมูลการพิสูจน์ตัวตน / การอนุญาตควรปรากฏในส่วนหัวการอนุญาต หรือส่วนหัวคุกกี้

ส่วนที่เหลือ

คำกริยาเช่นresetpasswordใน URL ของคุณโดยทั่วไปเป็นสัญญาณที่ชัดเจนของกระบวนทัศน์การโอนรัฐที่ไม่ได้ดำเนินการ URL ควรแสดงถึงทรัพยากร GET หมายถึงresetpasswordอะไร? หรือลบ?

API

ชุดรูปแบบนี้ต้องการทราบรหัสผ่านก่อนหน้าเสมอ คุณอาจต้องการอนุญาตให้มีกรณีเพิ่มเติม เช่นรหัสผ่านหาย


คุณสามารถใช้การรับรองความถูกต้องแบบพื้นฐานหรือแบบย่อซึ่งเป็นรูปแบบที่เข้าใจกันดี

PUT /user/joe/password HTTP/1.0
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: text/plain
Host: www.example.com

NEWPASSWD

มันไม่ได้ใส่ข้อมูลที่ละเอียดอ่อนเป็นพิเศษในเส้นทางและเป็นไปตามการประชุม HTTP และ REST

หากคุณต้องการอนุญาตสำหรับโหมดการอนุญาตอื่น ๆ (เช่นโทเค็นบางอันที่ส่งผ่านช่องทางที่ได้รับการยืนยันเพื่อรีเซ็ตรหัสผ่าน) คุณสามารถใช้ส่วนหัวการให้สิทธิ์ที่แตกต่างกันโดยไม่ต้องเปลี่ยนอะไรอีก


4

นอกเหนือจากความปลอดภัยแล้วปัญหาของเรื่องนี้ก็คือมันไม่ใช่วิธีที่สงบมาก

OLDPASSWDและNEWPASSWDอย่ายืนหยัดเพื่ออะไรในลำดับชั้นทรัพยากรของคุณและยิ่งแย่ลงไปกว่านั้นการดำเนินการไม่ได้เป็น idempotent

ดังนั้นคุณสามารถใช้POSTเป็นคำกริยาของคุณเท่านั้นและคุณไม่ควรใส่รหัสผ่านที่สองในเส้นทางทรัพยากรของคุณ


1
คุณไม่ได้สร้างบันทึกใหม่ แต่อัปเดตระเบียนที่มีอยู่ (โดยปกติ) ดังนั้นควรเป็น PUT แทนที่จะเป็น POST

2
@CamilStaps หากเป็นเพียงการตั้งรหัสผ่านก็อาจเป็นได้ แต่เนื่องจากสันนิษฐานว่ารหัสผ่านเก่าจำเป็นต้องได้รับการตรวจสอบด้วยเช่นกันจึงทำให้การดำเนินการที่ไม่ใช่ idempotent และทำให้PUTถูกตัดสิทธิ์เป็นคำกริยา มันอาจถูก rejigged ให้ทำงานด้วยPUTแต่ในรูปแบบปัจจุบันมันไม่ได้
biziclop

OLDPASSWD เป็นข้อมูลการรับรองความถูกต้องและไม่ควรอยู่ใน URL เลย

ไม่จำเป็นต้องเป็นเรื่องธรรมดาที่จะต้องขอรหัสผ่านเก่าอย่างชัดเจนบนการรับรองความถูกต้อง
biziclop

3

ปัญหาคือการหลีกเลี่ยงรหัสผ่านข้อความธรรมดาในคำขอของคุณ มีสองตัวเลือกเพื่อตอบสนองความต้องการของเว็บเซอร์ที่สงบ

1. การฝั่งไคลเอ็นต์

  • ฉันจะเดาว่าคุณเก็บรหัสผ่านของคุณเช่นแฮช (รหัสผ่าน + เกลือ)
  • คุณสามารถแฮรหัสผ่านใหม่ด้วยเกลือบนฝั่งไคลเอ็นต์
  • นั่นหมายถึง: สร้างเกลือใหม่บนฝั่งไคลเอ็นต์สร้างแฮชเช่นแฮช (newPassword + newSalt)
  • ส่งแฮชที่สร้างขึ้นใหม่พร้อมเกลือไปยังเว็บเซอร์ของคุณเพื่อการพักผ่อน
  • ส่งรหัสผ่านเก่าเป็นแฮช (oldPassword + oldSalt)

2. การเข้ารหัส

  • สร้างทรัพยากร "คีย์ครั้งเดียว" (otk) สำหรับผู้ใช้เช่น / otk / john
  • รีซอร์สนี้ส่งคืนคีย์เดี่ยวแบบสุ่มที่ปลอดภัยที่ไม่ซ้ำกันเช่น kbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWLMUXN6ZWxLdTZESFRs และ ID ที่ไม่ซ้ำกันเช่น 95648915125
  • เว็บเซอร์ที่สงบของคุณต้องเก็บ otk แบบสุ่มนี้ไว้เพื่อการสื่อสารที่ปลอดภัยครั้งต่อไปด้วย ID 95648915125
  • เข้ารหัสรหัสผ่านใหม่และเก่าของคุณด้วย otk เช่น AES (เพื่อเหตุผลด้านความปลอดภัยคุณควรใช้สอง otks แยกต่างหากสำหรับรหัสผ่านเก่าและใหม่)
  • ส่งรหัสผ่านที่เข้ารหัสไปยังทรัพยากรรหัสผ่านการเปลี่ยนแปลงของคุณด้วย ID 95648915125
  • ชุดค่าผสม otk และ ID หนึ่งชุดได้รับอนุญาตให้ทำงานเพียงครั้งเดียวเท่านั้นดังนั้นคุณต้องลบชุดค่าผสมนั้นหลังจากที่สแกนรหัสผ่าน
  • ตัวเลือกที่ดีกว่าที่เป็นไปได้: ส่งรหัสผ่านปัจจุบัน / เก่าโดย Basic-Auth

หมายเหตุ:จำเป็นต้องใช้ HTTPS สำหรับตัวเลือกทั้งสอง!


1
เหตุใดฉันจึงต้องเข้ารหัสสองครั้ง (รูปแบบการเข้ารหัสของคุณกับ OTK และ HTTPS) การแลกเปลี่ยนรหัสผ่าน เวกเตอร์การโจมตีที่นี่ที่ไม่ครอบคลุมโดย HTTPS คืออะไร
Bart Friederichs

1
จุดประสงค์เดียวคือเพื่อหลีกเลี่ยงการบันทึกเซิร์ฟเวอร์ที่เป็นไปได้ เนื้อความคำขอ HTTP (เช่นด้วยรหัสผ่านใหม่แบบข้อความธรรมดา) สามารถบันทึกได้แม้ว่าจะใช้ HTTPS การโจมตีที่เป็นไปได้อื่น ๆ คือการใช้ใบรับรองที่ลงนามด้วยตนเองซึ่งจะใช้เป็นพิเศษสำหรับวัตถุประสงค์ภายใน
maz258

2

คุณสมบัติของการดำเนินการรีเซ็ตรหัสผ่านคืออะไร

  1. มันเปลี่ยนอะไรบางอย่าง
  2. มีค่าที่ตั้งค่าเป็น
  3. มีเพียงบางคนเท่านั้นที่ได้รับอนุญาตให้ทำได้ (ผู้ใช้ผู้ดูแลระบบหรืออย่างใดอย่างหนึ่งอาจมีกฎที่แตกต่างกันในการทำเช่นนั้น)

จุดที่ 1 ที่นี่หมายความว่าคุณไม่สามารถใช้ GET ได้คุณต้อง POST บางอย่างที่แสดงถึงการดำเนินการเปลี่ยนรหัสผ่านกับ URI ซึ่งเป็นตัวแทนของทรัพยากรที่จัดการการเปลี่ยนแปลงรหัสผ่านหรือ PUT ที่แสดงถึงรหัสผ่านใหม่ไปยัง URI ที่แสดงถึงรหัสผ่าน ผู้ใช้) ซึ่งรหัสผ่านนั้นเป็นคุณสมบัติ

โดยทั่วไปแล้วเราจะโพสต์ไม่น้อยเพราะมันน่าอึดอัดใจที่จะนำสิ่งที่เราไม่สามารถหาได้ในภายหลังและแน่นอนว่าเราไม่สามารถรับรหัสผ่านได้

จุดที่ 2 จะเป็นข้อมูลที่แสดงถึงรหัสผ่านใหม่ในสิ่งที่โพสต์แล้ว

จุดที่ 3 หมายถึงเราจะต้องอนุญาตการร้องขอซึ่งหมายความว่าหากผู้ใช้เป็นผู้ใช้ปัจจุบันเราจะต้องใช้รหัสผ่านปัจจุบันเพื่อพิสูจน์ให้เราเห็น (แม้ว่าไม่จำเป็นต้องได้รับรหัสผ่านปัจจุบันเช่นความท้าทายที่ใช้แฮช ถูกใช้เพื่อพิสูจน์ความรู้โดยไม่ต้องส่ง)

URI จึงควรเป็นสิ่งที่ต้องการ<http://example.net/changeCurrentUserPassword>หรือ<http://example.net/users/joe/changePassword>หรือ

เราอาจตัดสินใจว่าเราต้องการรับรหัสผ่านปัจจุบันในข้อมูล POST รวมถึงกลไกการอนุญาตทั่วไปที่ใช้

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