การอัพเดตการแฮชรหัสผ่านโดยไม่บังคับให้ใช้รหัสผ่านใหม่สำหรับผู้ใช้ที่มีอยู่


32

คุณรักษาแอปพลิเคชันที่มีอยู่ด้วยฐานผู้ใช้ที่สร้างไว้แล้ว เมื่อเวลาผ่านไปมีการตัดสินใจว่าเทคนิคการแฮ็กรหัสผ่านปัจจุบันล้าสมัยและจำเป็นต้องอัพเกรด นอกจากนี้สำหรับเหตุผล UX คุณไม่ต้องการให้ผู้ใช้ปัจจุบันถูกบังคับให้อัปเดตรหัสผ่านของพวกเขา การอัพเดตการแฮชรหัสผ่านทั้งหมดต้องเกิดขึ้นหลังหน้าจอ

สมมติว่าฐานข้อมูล 'ง่าย' สำหรับผู้ใช้ที่มี:

  1. ID
  2. อีเมล์
  3. รหัสผ่าน

วิธีการหนึ่งที่จะแก้ไขข้อกำหนดดังกล่าวได้อย่างไร


ความคิดปัจจุบันของฉันคือ:

  • สร้างวิธีการแฮ็กใหม่ในชั้นเรียนที่เหมาะสม
  • อัพเดตตารางผู้ใช้ในฐานข้อมูลเพื่อเก็บฟิลด์รหัสผ่านเพิ่มเติม
  • เมื่อผู้ใช้เข้าสู่ระบบสำเร็จโดยใช้แฮรหัสผ่านที่ล้าสมัยแล้วให้กรอกฟิลด์รหัสผ่านที่สองด้วยแฮชที่อัปเดต

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

นอกจากนี้โดยทั่วไปหมายความว่าเทคนิคการแปลงแป้นพิมพ์แบบเก่าอาจถูกบังคับให้อยู่ต่อไปเรื่อย ๆ จนกว่าผู้ใช้ทุกคนจะอัปเดตรหัสผ่านของตน ในขณะนั้นฉันสามารถเริ่มลบการตรวจสอบการแฮชเก่าและลบฟิลด์ฐานข้อมูลฟุ่มเฟือย

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


4
ทำไมคุณไม่สามารถแยกความแตกต่างระหว่างชุดและแฮชรองที่ไม่ได้ตั้งค่าได้ เพียงทำให้คอลัมน์ฐานข้อมูลเป็นโมฆะและตรวจสอบค่าว่าง
Kilian Foth

6
ไปกับประเภทแฮชเป็นคอลัมน์ใหม่ของคุณแทนที่จะเป็นเขตข้อมูลสำหรับแฮชใหม่ เมื่อคุณอัปเดตแฮชให้เปลี่ยนฟิลด์ประเภท ด้วยวิธีนี้เมื่อสิ่งนี้เกิดขึ้นอีกในอนาคตคุณจะอยู่ในสถานะที่จะจัดการและไม่มีโอกาสที่คุณจะแฮชเข้าสู่แฮชเก่า (น่าจะปลอดภัยน้อยกว่า) แฮช
Michael Kohne

1
ฉันคิดว่าคุณกำลังอยู่ในเส้นทางที่ถูกต้อง 6 เดือนหรือหนึ่งปีนับจากนี้คุณสามารถบังคับให้ผู้ใช้ที่เหลืออยู่เปลี่ยนรหัสผ่านได้ หนึ่งปีต่อมาหรืออะไรก็ตามคุณสามารถกำจัดสนามเก่าได้
GlenPeterson

3
ทำไมไม่เพียงแค่แฮชแฮชเก่า?
Siyuan Ren

เพราะคุณต้องการรหัสผ่านที่จะถูกแฮช (ไม่ใช่แฮชเก่า) เพื่อให้มันทำการแฮช (เช่นการเปรียบเทียบกับแฮชที่ผู้ใช้กำหนด) ให้คุณ ... รหัสผ่าน - ไม่ใช่ค่าอื่นที่ต้องเป็น 'dehashed' ภายใต้วิธีการแบบเก่า เป็นไปได้ แต่ไม่สะอาด
Michael Durrant

คำตอบ:


25

ฉันขอแนะนำให้เพิ่มเขตข้อมูลใหม่ "hash_method" โดยอาจมี 1 เพื่อแสดงความหมายของวิธีเก่าและ 2 เพื่อแสดงถึงวิธีการใหม่

การพูดอย่างสมเหตุสมผลถ้าคุณใส่ใจเรื่องแบบนี้และแอปพลิเคชันของคุณค่อนข้างยาวนาน (ซึ่งเห็นได้ชัดอยู่แล้ว) นี่อาจจะเกิดขึ้นอีกครั้งเนื่องจากการเข้ารหัสและการรักษาความปลอดภัยของข้อมูลนั้นเป็นฟิลด์ที่พัฒนาและคาดเดาไม่ได้ มีบางครั้งที่การเรียกใช้ผ่าน MD5 เป็นเรื่องง่ายหากใช้การแปลงแป้นพิมพ์ทั้งหมด! ถ้าอย่างนั้นใคร ๆ ก็คิดว่าพวกเขาควรใช้ SHA1 และตอนนี้ก็มีเกลือ, เกลือทั่วโลก + เกลือสุ่มส่วนบุคคล, SHA3, วิธีการสร้างตัวเลขสุ่มพร้อม crypto ที่แตกต่างกัน ... นี่ไม่ใช่แค่ 'หยุด' ดังนั้นคุณอาจ แก้ไขปัญหานี้ด้วยวิธีที่ขยายได้และทำซ้ำได้

ดังนั้นให้บอกว่าตอนนี้คุณมีสิ่งที่ต้องการ (ใน pseudo-javascript เพื่อความเรียบง่ายฉันหวังว่า):

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword());


if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword)
{
    // TODO: Learn what "hash" means
    return clearPassword + "H@$I-I";
}

ตอนนี้เมื่อรู้ว่ามีวิธีที่ดีกว่าคุณเพียงแค่ต้องทำการปรับโครงสร้างเล็กน้อย:

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword(), user.getHashingMethod());

if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword, hashMethod)
{
    // Note: Hash doesn't mean what we thought it did. Oops...

    var hash;
    if (hashMethod == 1)
    {
        hash = clearPassword + "H@$I-I";
    }
    else if (hashMethod == 2)
    {
        // Totally gonna get it right this time.
        hash = SuperMethodTheNSASaidWasAwesome(clearPassword);
    }
    return hash;
}

ไม่มีตัวแทนลับหรือโปรแกรมเมอร์ได้รับอันตรายในการผลิตคำตอบนี้


+1 ดูเหมือนว่าจะเป็นนามธรรมที่สมเหตุสมผลขอบคุณ แม้ว่าฉันอาจท้ายย้าย hash และ hashmethod ฟิลด์ไปยังตารางที่แยกต่างหากเมื่อฉันใช้นี้
วิลเล็ม

1
ปัญหาเกี่ยวกับเทคนิคนี้คือสำหรับบัญชีที่ไม่ได้ลงชื่อเข้าใช้คุณไม่สามารถอัปเดตแฮชได้ ในประสบการณ์ของฉันผู้ใช้ส่วนใหญ่จะไม่เข้าสู่ระบบเป็นเวลาหลายปีถ้าเคย (เว้นแต่คุณจะลบผู้ใช้ที่ไม่ได้ใช้งาน) สิ่งที่เป็นนามธรรมของคุณก็ใช้งานไม่ได้เช่นกัน สิ่งที่เป็นนามธรรมมาตรฐานมีสองฟังก์ชั่นหนึ่งสำหรับการตรวจสอบและหนึ่งสำหรับการสร้าง
CodesInChaos

การแฮ็นรหัสผ่านแทบไม่ได้รับการพัฒนาในช่วง 15 ปีที่ผ่านมา Bcrypt เผยแพร่ในปี 1999 และยังเป็นแฮชที่แนะนำ มีการปรับปรุงที่สำคัญเพียงอย่างเดียวเกิดขึ้นตั้งแต่นั้น: หน่วยความจำฮาร์ฮาร์ดตามลำดับซึ่งบุกเบิกโดย scrypt
CodesInChaos

สิ่งนี้ทำให้แฮ็กเก่ามีความเสี่ยง (เช่นถ้ามีคนขโมย DB) การแฮชแฮชเก่าแต่ละอันด้วยวิธีใหม่ที่ปลอดภัยยิ่งขึ้นช่วยลดระดับนี้ได้ในระดับหนึ่ง (คุณจะต้องใช้ประเภทแฮชเพื่อแยกความแตกต่างระหว่างnewHash(oldHash, salt)หรือnewHash(password, salt)
dbkk

42

หากคุณสนใจพอที่จะเปิดตัวรูปแบบการแฮชใหม่ให้กับผู้ใช้ทั้งหมดโดยเร็วที่สุด (เช่นเพราะอันเก่าไม่ปลอดภัยจริง ๆ ) มีวิธีการ "โยกย้าย" แบบทันทีทันใดของทุกรหัสผ่าน

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

hash = H2(hash)  # where hash was previously equal to H1(p) 

หลังจากทำการแปลงแล้วคุณยังสามารถทำการยืนยันรหัสผ่านได้อย่างสมบูรณ์ คุณเพียงแค่ต้องคำนวณH2(H1(p'))แทนก่อนหน้านี้เมื่อใดก็ตามที่ผู้ใช้พยายามเข้าสู่ระบบด้วยรหัสผ่านH1(p')p'

ในทางทฤษฎีเทคนิคนี้สามารถนำไปใช้การโยกย้ายหลาย ๆ ( H3, H4ฯลฯ ) ในทางปฏิบัติคุณต้องการกำจัดฟังก์ชันแฮชเก่าทั้งเพื่อประสิทธิภาพและเหตุผลในการอ่าน โชคดีที่มันค่อนข้างง่าย: ในการเข้าสู่ระบบครั้งต่อไปที่ประสบความสำเร็จเพียงแค่คำนวณแฮชของรหัสผ่านของผู้ใช้ใหม่และแทนที่แฮชของแฮชที่มีอยู่ด้วย:

hash = H2(p)

นอกจากนี้คุณยังจะต้องมีคอลัมน์เพิ่มเติมเพื่อจดจำแฮชที่คุณกำลังจัดเก็บ: รหัสผ่านหนึ่งรหัสหรือแฮชเก่า ในเหตุการณ์ที่น่าเสียดายของการรั่วไหลของฐานข้อมูลคอลัมน์นี้ไม่ควรทำให้แคร็กเกอร์ทำงานได้ง่ายขึ้น โดยไม่คำนึงถึงความคุ้มค่าคุ้มโจมตียังจะต้องย้อนกลับที่เชื่อถือได้ของอัลกอริทึมมากกว่าเก่าH2H1


3
ยักษ์ +1: H2 (H1 (p)) มักจะปลอดภัยเท่ากับการใช้ H2 (p) โดยตรงแม้ว่า H1 จะแย่มากเพราะ (1) เกลือและการยืดได้รับการดูแลโดย H2 และ (2) ปัญหา ด้วยแฮช "ที่เสียหาย" เช่น MD5 จะไม่มีผลกับการแฮ็กรหัสผ่าน ที่เกี่ยวข้อง: crypto.stackexchange.com/questions/2945/…
orip

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

4
ถ้า H1 แย่จริงๆนี่จะไม่ปลอดภัย การเป็นคนแย่มากในบริบทนี้ส่วนใหญ่เกี่ยวกับการสูญเสียข้อมูลจำนวนมากจากข้อมูล แม้แต่ MD4 และ MD5 ก็เกือบจะสมบูรณ์แบบในเรื่องนี้ดังนั้นวิธีนี้จึงไม่ปลอดภัยสำหรับแฮ็ชโฮมบ์หรือแฮชที่มีเอาต์พุตสั้น ๆ (ต่ำกว่า 80 บิต)
CodesInChaos

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

8

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

เพื่อหลีกเลี่ยงสถานการณ์นี้คุณอาจรอให้ผู้ใช้ที่ใช้งานมากที่สุดของคุณเปลี่ยนเป็นอัลกอริทึมการแฮชใหม่จากนั้น:

  1. ลบบัญชีของผู้ใช้ที่ไม่ได้ตรวจสอบสิทธิ์นานเกินไป ไม่มีอะไรผิดปกติในการลบบัญชีของผู้ใช้ที่ไม่เคยเข้ามาในเว็บไซต์เป็นเวลาสามปี

  2. ส่งอีเมลไปยังผู้ใช้ที่เหลือในหมู่ผู้ที่ไม่ได้รับการรับรองความถูกต้องชั่วขณะหนึ่งโดยบอกว่าพวกเขาอาจสนใจสิ่งใหม่ ๆ มันจะช่วยลดจำนวนบัญชีที่ใช้การแฮชที่เก่ากว่า

    ระวัง: หากคุณมีตัวเลือกที่ไม่มีสแปมผู้ใช้สามารถตรวจสอบบนเว็บไซต์ของคุณอย่าส่งอีเมลไปยังผู้ใช้ที่ตรวจสอบมัน

  3. ในที่สุดไม่กี่สัปดาห์หลังจากขั้นตอนที่ 2 ทำลายรหัสผ่านสำหรับผู้ใช้ที่ยังคงใช้เทคนิคเก่า เมื่อใดและหากพวกเขาพยายามตรวจสอบสิทธิ์พวกเขาจะเห็นว่ารหัสผ่านไม่ถูกต้อง หากพวกเขายังคงสนใจในบริการของคุณพวกเขาสามารถรีเซ็ตได้


1

คุณอาจไม่เคยมีแฮชมากกว่าสองประเภทดังนั้นคุณสามารถแก้ไขได้โดยไม่ต้องขยายฐานข้อมูลของคุณ

หากวิธีการนั้นมีความยาวแฮชต่างกันและความยาวแฮชของแต่ละวิธีนั้นมีค่าคงที่ (เช่นการเปลี่ยนจาก md5 เป็น hmac-sha1) คุณสามารถบอกวิธีได้จากความยาวแฮช หากมีความยาวเท่ากันคุณสามารถคำนวณแฮชโดยใช้วิธีการใหม่ก่อนจากนั้นจึงใช้วิธีเดิมหากการทดสอบครั้งแรกล้มเหลว หากคุณมีช่อง (ไม่แสดง) ที่บอกเวลาที่ผู้ใช้ปรับปรุง / สร้างครั้งล่าสุดคุณสามารถใช้สิ่งนั้นเป็นตัวบ่งชี้สำหรับวิธีการที่จะใช้


1

ฉันทำสิ่งนี้และมีวิธีที่จะบอกว่ามันเป็นแฮชใหม่และทำให้ปลอดภัยยิ่งขึ้นหรือไม่ ฉันเดาว่าปลอดภัยมากขึ้นเป็นสิ่งที่ดีสำหรับคุณถ้าคุณสละเวลาในการอัพเดทแฮช ;-)

เพิ่มคอลัมน์ใหม่ลงในตารางฐานข้อมูลของคุณที่เรียกว่า "เกลือ" และใช้เป็นเกลือที่สร้างขึ้นโดยผู้ใช้แบบสุ่ม (สวยมากป้องกันการโจมตีตารางสายรุ้ง)

ด้วยวิธีนี้หากรหัสผ่านของฉันคือ "pass123" และรหัสแบบสุ่มคือ 3333 รหัสผ่านใหม่ของฉันจะเป็นแฮชของ "pass1233333"

หากผู้ใช้มีเกลือคุณรู้ว่ามันเป็นแฮชใหม่ หากเกลือของผู้ใช้เป็นโมฆะคุณจะรู้ว่ามันเป็นแฮชเก่า


0

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

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


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