มีการใช้งานจาวาสคริปต์ SHA-256 ที่โดยทั่วไปถือว่าน่าเชื่อถือหรือไม่?


99

ฉันกำลังเขียนล็อกอินสำหรับฟอรัมและจำเป็นต้องแฮชรหัสผ่านฝั่งไคลเอ็นต์ในจาวาสคริปต์ก่อนที่จะส่งไปยังเซิร์ฟเวอร์ ฉันมีปัญหาในการหาการใช้งาน SHA-256 ที่ฉันสามารถเชื่อถือได้ ฉันคาดหวังว่าจะมีสคริปต์ที่เชื่อถือได้บางประเภทที่ทุกคนใช้ แต่ฉันพบโครงการต่างๆมากมายด้วยการใช้งานของตัวเอง

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

แก้ไขเนื่องจากมีการแสดงความคิดเห็นมากมาย: ใช่เราทำการแฮชที่เข้มงวดมากขึ้นอีกครั้งในฝั่งเซิร์ฟเวอร์ การแฮชฝั่งไคลเอ็นต์ไม่ใช่ผลลัพธ์สุดท้ายที่เราบันทึกในฐานข้อมูล การแฮชฝั่งไคลเอ็นต์เป็นเพราะไคลเอนต์ที่เป็นมนุษย์ร้องขอ พวกเขาไม่ได้ให้เหตุผลที่เฉพาะเจาะจงว่าทำไมพวกเขาอาจชอบ overkill


9
เพื่อไม่ให้หลุดหัวข้อ แต่ทำไมคุณถึงแฮชรหัสผ่านทางฝั่งไคลเอ็นต์?
Steve

5
@ddyer ไม่แม้แต่จะปิด "อย่าม้วนของคุณเอง" ใช้กับการประดิษฐ์อัลกอริทึมของคุณเองการเขียนอัลกอริทึมของคุณเองการพัฒนาโปรโตคอลของคุณเองบนอัลกอริธึมการเข้ารหัสลับหรืออะไรก็ตามที่เหนือกว่าโดยใช้เป็นนามธรรมระดับสูงเท่าที่มีอยู่ หากคุณคิดว่าคุณจะยึดติดกับแกนที่ปลอดภัยได้อย่างปลอดภัยและเพียงเขียนโค้ดกาวคุณจะมีช่วงเวลาที่เลวร้าย
Stephen Touset

26
หากคุณใช้รหัสผ่านที่แฮชโดยไม่มีโปรโตคอลการท้าทาย / การตอบกลับรหัสผ่านที่แฮชจะเป็นรหัสผ่านและเป็นเช่นเดียวกับการส่งรหัสผ่านในข้อความที่ชัดเจน
ddyer

26
@ddyer มีค่าบางอย่างในการปกป้องรหัสผ่านข้อความธรรมดาของผู้ใช้สำหรับไซต์อื่น ๆ ทั้งหมดที่พวกเขาอาจใช้หากไม่ใช่สำหรับไซต์ของเราโดยเฉพาะ เป็นการแก้ไขที่ง่ายซึ่งอาจจะช่วยเราไม่ได้ แต่อาจช่วยผู้ใช้ได้หากเราพลาดที่ใดสักแห่ง และอย่างที่ฉันพูดคำขอของลูกค้าฉันไม่สามารถทำอะไรได้แม้ว่าฉันต้องการ
jono

5
@Anorov ฉันเปิดใจมากกว่าที่จะเปลี่ยนใจ :) แต่ในกรณีนี้ฉันไม่เข้าใจว่าประเด็นของคุณใช้อย่างไร เราแฮชรหัสผ่านสองครั้ง: หนึ่งครั้งในฝั่งไคลเอ็นต์ด้วย SHA-256 แบบธรรมดาและอีกครั้งที่ฝั่งเซิร์ฟเวอร์พร้อมกับสิ่งที่ต้องการมากกว่า สิ่งแรกที่ปกป้องข้อความธรรมดาในกรณีของ MITM หรือที่คล้ายกันและอย่างที่สองสำหรับการป้องกันเดรัจฉาน แม้ว่าคุณจะถูกระงับฐานข้อมูลและแฮชของผู้ดูแลระบบคุณก็ไม่สามารถใช้ข้อมูลนั้นเพื่อตรวจสอบการเข้าสู่ระบบได้โดยตรง
jono

คำตอบ:


115

ล้าสมัย:เบราว์เซอร์สมัยใหม่จำนวนมากรองรับการใช้งาน crypto ระดับเฟิร์สคลาสแล้ว ดูคำตอบของ Vitaly Zdanevichด้านล่าง


Crypto ห้องสมุด Stanford JSมีการดำเนินการของ SHA-256 แม้ว่าการเข้ารหัสลับใน JS จะไม่ได้รับการตรวจสอบอย่างดีเท่าแพลตฟอร์มการนำไปใช้งานอื่น ๆ แต่อย่างน้อยก็มีการพัฒนาบางส่วนโดยDan Bonehซึ่งเป็นชื่อที่มีชื่อเสียงและเชื่อถือได้ในการเข้ารหัส และหมายความว่าโครงการมีการดูแลบางอย่างโดยคนที่รู้ว่าเขากำลังทำอะไรอยู่ โครงการนี้ยังได้รับการสนับสนุนโดยNSF

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

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


32
อย่างไรก็ตามหากคุณแฮชบนเซิร์ฟเวอร์อีกครั้งการปฏิบัตินี้ถูกต้องตามกฎหมายอย่างสมบูรณ์
Nick Brunt

13
@NickBrunt หากคุณแฮชบนเซิร์ฟเวอร์แสดงว่าคุณไม่ได้บันทึกรหัสผ่านที่ส่ง แต่คุณไม่ได้เพิ่มความปลอดภัยใด ๆ นอกเหนือจากการส่งรหัสผ่านเดิมซึ่งต่างจากการส่งแฮชรหัสผ่านเนื่องจากไม่ว่าในกรณีใดสิ่งที่คุณกำลังส่งคือรหัสผ่านจริง
tylerl

54
จริง แต่ก็ไม่เลวร้ายไปกว่าการส่งรหัสผ่านซึ่งอาจใช้ที่อื่นบนอินเทอร์เน็ตในรูปแบบข้อความธรรมดา
Nick Brunt

15
ฉันเห็นด้วยกับ @NickBrunt จะดีกว่าถ้าผู้โจมตีอ่านสตริงแฮชแบบสุ่มซึ่งอาจเหมาะกับแอพนี้โดยเฉพาะแทนที่จะใช้รหัสผ่านเดิมที่อาจใช้ได้ในหลายที่
Aebsubis

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

56

ในhttps://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digestฉันพบตัวอย่างข้อมูลนี้ที่ใช้โมดูล js ภายใน:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder().encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

โปรดทราบว่าcrypto.subtleมีให้เฉพาะในhttpsหรือlocalhost- ตัวอย่างเช่นสำหรับการพัฒนาในพื้นที่ของpython3 -m http.serverคุณคุณต้องเพิ่มบรรทัดนี้ใน/etc/hosts: 0.0.0.0 localhost

Reboot - และคุณสามารถเปิดกับการทำงานlocalhost:8000crypto.subtle


2
นี่มันเจ๋งมาก. ขอบคุณ. มี API ที่เทียบเคียงได้ใน Node.js หรือไม่
Con Antonakos

2
ไลบรารี 'crypto' ในตัวควรทำได้ดี: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho

18

การใช้งาน SHA-256 ของForgeนั้นรวดเร็วและเชื่อถือได้

เรียกใช้การทดสอบบน SHA-256 การใช้งานจาวาสคริหลายไปhttp://brillout.github.io/test-javascript-hash-implementations/

ผลลัพธ์บนเครื่องของฉันแสดงให้เห็นว่าการปลอมเป็นการใช้งานที่เร็วที่สุดและยังเร็วกว่า Stanford Javascript Crypto Library (sjcl) ที่กล่าวถึงในคำตอบที่ยอมรับ

Forge มีขนาดใหญ่ 256 KB แต่การแยกโค้ดที่เกี่ยวข้องกับ SHA-256 จะลดขนาดลงเหลือ 4.5 KB โปรดดูที่https://github.com/brillout/forge-sha256


ฉันจัดการเพื่อติดตั้งด้วยnpm install node-forgeแต่ตอนนี้วิธีใช้ไลบรารีจะสร้างแฮชจากสตริงได้fooอย่างไร
ผู้ใช้

17

สำหรับผู้ที่สนใจนี่คือรหัสสำหรับสร้างแฮช SHA-256 โดยใช้sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)

5
สิ่งนี้ควรรวมอยู่ในคำตอบที่ยอมรับสำหรับผู้ที่ต้องการใช้โซลูชันที่นั่น (Heck แม้แต่เอกสารของพวกเขาเองก็ไม่มีตัวอย่างเช่นนี้)
MagicLegend

นี่คือคำตอบที่ดีที่สุด ฉันกำลังลองใช้โซลูชันการเข้ารหัสลับ แต่มันไม่ได้ผลสำหรับฉัน
Augusto Gonzalez

12

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

"ปัญหาไก่ - ไข่" ในการส่งการเข้ารหัส Javascript คืออะไร

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

[... ]

เหตุใดฉันจึงไม่สามารถใช้ TLS / SSL เพื่อส่งรหัสการเข้ารหัส Javascript ได้

คุณสามารถ. มันยากกว่าที่คิด แต่คุณส่ง Javascript crypto ไปยังเบราว์เซอร์โดยใช้ SSL ได้อย่างปลอดภัย ปัญหาคือเมื่อสร้างช่องทางที่ปลอดภัยด้วย SSL แล้วคุณไม่จำเป็นต้องใช้การเข้ารหัส Javascript อีกต่อไป คุณมีการเข้ารหัส "ของจริง"

ซึ่งนำไปสู่สิ่งนี้:

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

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

โดยทั่วไปปัญหาคือ:

  • ลูกค้าของคุณไม่เชื่อถือเซิร์ฟเวอร์ของคุณดังนั้นพวกเขาจึงต้องการเพิ่มรหัสความปลอดภัยเพิ่มเติม
  • รหัสความปลอดภัยนั้นจัดส่งโดยเซิร์ฟเวอร์ของคุณ (รหัสที่พวกเขาไม่เชื่อถือ)

หรืออีกทางหนึ่งคือ

  • ลูกค้าของคุณไม่เชื่อถือ SSL ดังนั้นพวกเขาจึงต้องการให้คุณใช้รหัสความปลอดภัยเพิ่มเติม
  • รหัสความปลอดภัยนั้นถูกส่งผ่าน SSL

หมายเหตุ: นอกจากนี้ SHA-256 ไม่เหมาะสำหรับการนี้เนื่องจากมันจึงง่ายต่อการบังคับเดรัจฉานรหัสผ่านที่ไม่ซ้ำจืด ถ้าคุณตัดสินใจที่จะทำต่อไปนี้ดูสำหรับการดำเนินการของbcrypt , ScryptหรือPBKDF2


1
สิ่งนี้ไม่ถูกต้อง ความปลอดภัยของรหัสผ่านสามารถปรับปรุงได้โดยการแฮชที่ฝั่งไคลเอ็นต์ เป็นเรื่องผิดเช่นกันที่ SHA-256 ไม่เหมาะสมตราบใดที่มีการใช้ PBKDF2 ในฝั่งเซิร์ฟเวอร์ ประการที่สามในขณะที่บทความ matasano มีข้อมูลเชิงลึกที่เป็นประโยชน์การต่อยผิดเนื่องจากตอนนี้เรามีแอปเบราว์เซอร์ที่เปลี่ยนปัญหาไก่และไข่โดยพื้นฐานแล้ว
user239558

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

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

1
@Xenland สิ่งที่คุณทำคือสร้างรหัสผ่านใหม่ซึ่งก็คือ "โทเค็น + รหัสผ่าน" ปัญหาเดิมทั้งหมดยังคงมีผลบังคับใช้ ตัวอย่างเช่นผู้โจมตีสามารถแทนที่ JavaScript ด้วยรหัสเพื่อส่งโทเค็น + รหัสผ่านให้พวกเขา
Brendan Long

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

10

ฉันพบว่าการใช้งานนี้ใช้งานง่ายมาก นอกจากนี้ยังมีใบอนุญาตสไตล์ BSD ที่ใจกว้าง:

jsSHA: https://github.com/Caligatio/jsSHA

ฉันต้องการวิธีที่รวดเร็วในการรับการแทนค่า hex-string ของแฮช SHA-256 ใช้เวลาเพียง 3 บรรทัด:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");

1

นอกเหนือจาก Stanford lib ที่ tylerl กล่าวถึง ฉันพบว่าjsrsasignมีประโยชน์มาก (Github repo ที่นี่: https://github.com/kjur/jsrsasign ) ฉันไม่รู้ว่ามันน่าเชื่อถือแค่ไหน แต่ฉันใช้ API ของ SHA256, Base64, RSA, x509 เป็นต้นและมันก็ใช้ได้ดีทีเดียว ในความเป็นจริงมันรวมถึง lib Stanford ด้วย

หากสิ่งที่คุณต้องการทำคือ SHA256 jsrsasignอาจจะมากเกินไป แต่ถ้าคุณมีความต้องการอื่น ๆ ในส่วนที่เกี่ยวข้องฉันรู้สึกว่ามันเหมาะสมดี



2
เห็นด้วย แต่เพื่อความยุติธรรมฉันตอบคำถามนี้ในปี 2015 ในขณะที่ข้อกำหนดเกี่ยวกับเว็บ crypto API ของ mozilla ได้รับการเผยแพร่เมื่อมกราคม 2017
Faraway
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.