Có bất kỳ triển khai javascript SHA-256 nào thường được coi là đáng tin cậy không?


95

Tôi đang viết một đăng nhập cho một diễn đàn và cần phải băm phía máy khách mật khẩu trong javascript trước khi gửi nó đến máy chủ. Tôi đang gặp khó khăn khi tìm cách triển khai SHA-256 mà tôi thực sự có thể tin tưởng. Tôi đã mong đợi có một số loại kịch bản có thẩm quyền mà mọi người đều sử dụng, nhưng tôi đang tìm thấy vô số dự án khác nhau, tất cả đều có cách triển khai của riêng chúng.

Tôi nhận ra rằng việc sử dụng tiền điện tử của người khác luôn là một bước nhảy vọt về niềm tin trừ khi bạn đủ điều kiện để tự mình xem xét nó và không có định nghĩa chung về "đáng tin cậy", nhưng điều này có vẻ như một điều gì đó phổ biến và đủ quan trọng để cần phải có một số loại đồng thuận về những gì sẽ sử dụng. Tôi chỉ ngây thơ thôi sao?

Chỉnh sửa vì nó xuất hiện rất nhiều trong các nhận xét: Có, chúng tôi thực hiện lại băm nghiêm ngặt hơn ở phía máy chủ. Hàm băm phía máy khách không phải là kết quả cuối cùng mà chúng tôi lưu trong cơ sở dữ liệu. Hàm băm phía máy khách là do máy khách con người yêu cầu. Họ chưa đưa ra lý do cụ thể tại sao, chắc họ chỉ thích quá mức cần thiết.


9
Không phải để lạc đề, nhưng tại sao bạn lại băm mật khẩu ở phía máy khách?
Steve

5
@ddyer Thậm chí không gần. "Don't roll your own" áp dụng cho việc phát minh ra thuật toán của riêng bạn, viết cách triển khai thuật toán của riêng bạn, phát triển giao thức của riêng bạn trên các thuật toán tiền điện tử hoặc gần như bất cứ điều gì ở trên bằng cách sử dụng trừu tượng cấp cao như có sẵn. Nếu bạn nghĩ rằng bạn sẽ an toàn khi dính vào một lõi an toàn và chỉ viết mã keo, bạn sẽ có một khoảng thời gian tồi tệ.
Stephen Touset

26
nếu bạn sử dụng mật khẩu băm mà không có giao thức thử thách / phản hồi, thì mật khẩu băm LÀ mật khẩu và nó thực sự giống như việc truyền mật khẩu dưới dạng văn bản rõ ràng.
ddyer

26
@ddyer Có một số giá trị trong việc bảo vệ mật khẩu văn bản rõ ràng của người dùng cho tất cả các trang web khác mà họ có thể sử dụng mật khẩu đó, nếu không phải cho trang web của chúng tôi nói riêng. Đó là một bản sửa lỗi dễ dàng có thể không giúp ích được gì cho chúng tôi nhưng có khả năng giúp ích cho người dùng nếu chúng tôi mắc lỗi ở đâu đó. Và như tôi đã nói, yêu cầu của khách hàng, tôi không thể làm gì với nó ngay cả khi tôi muốn.
jono

4
@Anorov Tôi sẵn sàng thay đổi suy nghĩ của mình :) nhưng trong trường hợp này, tôi không thực sự hiểu quan điểm của bạn áp dụng như thế nào. Chúng tôi băm mật khẩu hai lần: một lần ở phía máy khách với SHA-256 đơn giản và một lần ở phía máy chủ với thứ gì đó đòi hỏi nhiều hơn. Đầu tiên để bảo vệ bản rõ trong trường hợp MITM hoặc tương tự, và thứ hai để bảo vệ brute. Ngay cả khi bạn có cơ sở dữ liệu và băm quản trị viên, bạn không thể sử dụng trực tiếp đó để xác thực đăng nhập.
jono

Câu trả lời:


111

Các Stanford JS Crypto Library chứa một thực hiện của SHA-256. Mặc dù tiền điện tử trong JS không thực sự được kiểm tra kỹ lưỡng như các nền tảng triển khai khác, nhưng nền tảng này ít nhất được phát triển một phần bởi và ở một mức độ nhất định được tài trợ bởi Dan Boneh , một cái tên nổi tiếng và đáng tin cậy trong lĩnh vực tiền mã hóa , và có nghĩa là dự án có một số giám sát bởi một người thực sự biết anh ta đang làm gì. Dự án cũng được hỗ trợ bởi NSF .

Tuy nhiên, cần chỉ ra
rằng ... ... rằng nếu bạn băm mật khẩu phía máy khách trước khi gửi nó, thì băm là mật khẩu và mật khẩu ban đầu trở nên không liên quan. Kẻ tấn công chỉ cần chặn mã băm để mạo danh người dùng và nếu hàm băm đó được lưu trữ không sửa đổi trên máy chủ, thì máy chủ đang lưu trữ mật khẩu thực (mã băm) ở dạng văn bản thuần túy .

Vì vậy, bảo mật của bạn bây giờ kém hơn vì bạn đã quyết định thêm các cải tiến của riêng mình cho những gì trước đây là một chương trình đáng tin cậy.


31
Tuy nhiên, nếu bạn băm lại trên máy chủ, cách làm này hoàn toàn hợp pháp.
Nick Brunt

13
@NickBrunt nếu bạn băm trên máy chủ thì bạn sẽ không lưu mật khẩu đã truyền, nhưng bạn chưa thêm bất kỳ bảo mật nào ngoài việc truyền mật khẩu ban đầu thay vì truyền mật khẩu băm, vì trong cả hai trường hợp, những gì bạn đang truyền là mật khẩu thực .
tylerl

54
Đúng, nhưng nó không tệ hơn việc gửi một mật khẩu có thể được sử dụng ở nơi khác trên Internet ở dạng bản rõ.
Nick Brunt

15
Tôi đồng ý với @NickBrunt. Sẽ tốt hơn nếu kẻ tấn công đọc một chuỗi băm ngẫu nhiên có thể chỉ phù hợp với ứng dụng cụ thể này hơn là mật khẩu ban đầu có thể được sử dụng ở một số nơi.
Aebsubis

12
Tôi cần sử dụng băm phía máy khách vì tôi không muốn máy chủ nhìn thấy mật khẩu văn bản rõ của người dùng. Không phải để tăng cường bảo mật cho dịch vụ tôi đang cung cấp, mà hướng tới người dùng. Tôi không chỉ lưu hàm băm của người dùng có muối không đổi (không đổi cho mỗi người dùng, không phải trên toàn cầu) mà còn băm lại bằng muối phiên ngẫu nhiên mỗi lần đăng nhập mà KHÔNG cung cấp thêm một chút bảo mật qua mạng chống lại việc đánh hơi. Nếu máy chủ bị xâm phạm, trò chơi sẽ kết thúc, nhưng điều đó đúng với bất kỳ điều gì không phải p2p thực sự.
Hatagashira

49

Trên https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest, tôi tìm thấy đoạn mã này sử dụng mô-đun js nội bộ:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder('utf-8').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;
}

Lưu ý rằng crypto.subtlechỉ có sẵn trên httpshoặc localhost- ví dụ: để phát triển địa phương của bạn với python3 -m http.serverbạn, bạn cần thêm dòng này vào /etc/hosts: 0.0.0.0 localhost

Khởi động lại - và bạn có thể mở localhost:8000khi đang làm việc crypto.subtle.


2
Điều này thật tuyệt. Cảm ơn bạn. Một API tương đương có sẵn trong Node.js không?
Con Antonakos

2
Thư viện 'tiền điện tử' nội trang sẽ hoạt động tốt: nodejs.org/dist/latest-v11.x/docs/api/crypto.html
tytho

17

Việc triển khai SHA-256 của Forge nhanh chóng và đáng tin cậy.

Để chạy thử nghiệm trên một số triển khai JavaScript SHA-256, hãy truy cập http://brillout.github.io/test-javascript-hash-implementations/ .

Kết quả trên máy của tôi cho thấy giả mạo là triển khai nhanh nhất và cũng nhanh hơn đáng kể so với Thư viện mã hóa Javascript Stanford (sjcl) được đề cập trong câu trả lời được chấp nhận.

Forge có dung lượng lớn 256 KB, nhưng việc giải nén mã liên quan SHA-256 sẽ giảm kích thước xuống còn 4,5 KB, hãy xem https://github.com/brillout/forge-sha256


Tôi đã quản lý để cài đặt nó với npm install node-forge, nhưng bây giờ, làm thế nào để sử dụng thư viện để tạo một băm từ chuỗi foo?
người dùng

15

Đối với những người quan tâm, đây là mã để tạo hàm băm SHA-256 bằng cách sử dụng sjcl:

import sjcl from 'sjcl'

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

4
Điều này nên được kết hợp trong câu trả lời được chấp nhận cho những người muốn sử dụng giải pháp ở đó. (Khỉ thật, ngay cả tài liệu của riêng họ cũng không có đoạn mã như thế này)
MagicLegend 5/1018

Đây là câu trả lời tốt nhất. Tôi đã thử giải pháp tiền điện tử nhưng nó không hiệu quả với tôi.
Augusto Gonzalez

11

Không, không có cách nào sử dụng JavaScript của trình duyệt để cải thiện tính bảo mật của mật khẩu. Tôi rất khuyên bạn nên đọc bài viết này . Trong trường hợp của bạn, vấn đề lớn nhất là vấn đề trứng gà:

"Vấn đề với quả trứng gà" với việc phân phối mật mã Javascript là gì?

Nếu bạn không tin tưởng mạng cung cấp mật khẩu, hoặc tệ hơn, không tin tưởng máy chủ không giữ bí mật của người dùng, bạn không thể tin tưởng họ cung cấp mã bảo mật. Cũng chính kẻ tấn công đã đánh hơi mật khẩu hoặc đọc nhật ký trước khi bạn giới thiệu tiền điện tử chỉ đơn giản là chiếm đoạt mã tiền điện tử sau khi bạn làm vậy.

[...]

Tại sao tôi không thể sử dụng TLS / SSL để phân phối mã tiền điện tử Javascript?

Bạn có thể. Nghe có vẻ khó hơn nhưng bạn có thể truyền mã hóa Javascript tới trình duyệt bằng SSL một cách an toàn. Vấn đề là, sau khi thiết lập một kênh an toàn với SSL, bạn không cần mã hóa Javascript nữa; bạn có mật mã "thực".

Dẫn đến điều này:

Vấn đề với việc chạy mã tiền điện tử trong Javascript là trên thực tế bất kỳ chức năng nào mà tiền điện tử phụ thuộc vào đều có thể bị ghi đè âm thầm bởi bất kỳ phần nội dung nào được sử dụng để xây dựng trang lưu trữ. Bảo mật tiền điện tử có thể được hoàn tác sớm trong quá trình này (bằng cách tạo các số ngẫu nhiên không có thật hoặc bằng cách giả mạo các hằng số và thông số được sử dụng bởi các thuật toán) hoặc sau đó (bằng cách viết lại tài liệu chính cho kẻ tấn công) hoặc --- trong trường hợp có khả năng xảy ra nhất --- bằng cách bỏ qua hoàn toàn tiền điện tử.

Không có cách nào đáng tin cậy cho bất kỳ đoạn mã Javascript nào để xác minh môi trường thực thi của nó. Mã tiền điện tử Javascript không thể hỏi, "tôi thực sự đang xử lý một trình tạo số ngẫu nhiên hay với một số bản fax của một số do kẻ tấn công cung cấp?" Và chắc chắn không thể khẳng định "không ai được phép làm bất cứ điều gì với bí mật tiền điện tử này ngoại trừ những cách mà tôi, tác giả, chấp thuận". Đây là hai thuộc tính thường được cung cấp trong các môi trường khác sử dụng tiền điện tử và chúng là không thể trong Javascript.

Về cơ bản, vấn đề là:

  • Khách hàng của bạn không tin tưởng máy chủ của bạn, vì vậy họ muốn thêm mã bảo mật bổ sung.
  • Mã bảo mật đó được phân phối bởi các máy chủ của bạn (những máy chủ mà họ không tin tưởng).

Hay cách khác,

  • Khách hàng của bạn không tin tưởng SSL, vì vậy họ muốn bạn sử dụng mã bảo mật bổ sung.
  • Mã bảo mật đó được gửi qua SSL.

Lưu ý: Ngoài ra, SHA-256 không phù hợp cho việc này, vì quá dễ dàng để ép buộc các mật khẩu không lặp lại không được đánh giá cao . Nếu bạn vẫn quyết định thực hiện việc này, hãy tìm cách triển khai bcrypt , scrypt hoặc PBKDF2 .


1
Điều này LAF không đúng. Bảo mật mật khẩu có thể được cải thiện bằng cách băm ở phía máy khách. Cũng sai khi SHA-256 không phù hợp với điều kiện là PBKDF2 được sử dụng ở phía máy chủ. Thứ ba, mặc dù bài viết về matasano có chứa thông tin chi tiết hữu ích, nhưng điểm mấu chốt là sai vì giờ đây chúng ta có các ứng dụng trình duyệt thay đổi cơ bản vấn đề gà và trứng.
user239558

2
Bạn nói đúng rằng PBKDF2 nên được sử dụng trên máy khách. Cơn thịnh nộ chống lại tiền điện tử javascript phía máy khách giả định rằng trang đầu tiên và các yêu cầu tiếp theo có cùng thuộc tính bảo mật. Điều đó rõ ràng không đúng đối với các ứng dụng trình duyệt trong đó trang ban đầu được cài đặt riêng biệt và là trang tĩnh. Nó cũng không đúng với bất kỳ trang nào có ngữ nghĩa vĩnh viễn trong bộ nhớ cache. Trong những trường hợp này, TOFU giống hệt như khi cài đặt một chương trình phía máy khách. Điều này cũng có thể đúng trong các thiết lập phức tạp hơn trong đó việc phân phát các trang tĩnh và động được tách biệt trên máy chủ.
user239558,

3
Việc tránh quản trị viên hệ thống có quyền truy cập vào mật khẩu của người dùng là điều hợp pháp, vì vậy việc băm ở phía máy khách ngăn DBA hoặc các chuyên gia CNTT khác xem mật khẩu của người dùng và cố gắng sử dụng lại mật khẩu đó để truy cập vào các dịch vụ / hệ thống khác, vì rất nhiều người dùng lặp lại mật khẩu của họ .
Yamada

1
@Xenland Tất cả những gì bạn đang làm là tạo một mật khẩu mới là "mã thông báo + mật khẩu". Tất cả các vấn đề ban đầu vẫn được áp dụng. Ví dụ: kẻ tấn công có thể thay thế JavaScript bằng mã để gửi cho chúng mã thông báo + mật khẩu.
Brendan Long

2
@Xenland Vấn đề con gà và quả trứng không liên quan gì đến những yêu cầu bạn gửi. Vấn đề là khách hàng của bạn đang sử dụng mã JavaScript được tải xuống qua một kết nối không an toàn để thực hiện công việc bảo mật. Cách duy nhất để gửi JavaScript đến trình duyệt web một cách an toàn là phân phát nó qua TLS và sau khi làm điều đó, bạn không cần phải làm gì khác vì kết nối của bạn đã được bảo mật. Không quan trọng giao thức của bạn là gì nếu bạn đang sử dụng mã JavaScript mà kẻ tấn công có thể thay thế.
Brendan Dài

10

Tôi thấy việc triển khai này rất dễ sử dụng. Cũng có một giấy phép kiểu BSD hào phóng:

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

Tôi cần một cách nhanh chóng để có được biểu diễn chuỗi hex của hàm băm SHA-256. Nó chỉ mất 3 dòng:

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

1

Ngoài Stanford lib mà tylerl đã đề cập. Tôi thấy jsrsasign rất hữu ích (Github repo tại đây: https://github.com/kjur/jsrsasign ). Tôi không biết độ tin cậy chính xác của nó như thế nào, nhưng tôi đã sử dụng API của nó là SHA256, Base64, RSA, x509, v.v. và nó hoạt động khá tốt. Trên thực tế, nó cũng bao gồm cả Stanford lib.

Nếu tất cả những gì bạn muốn làm là SHA256, jsrsasign có thể là một việc làm quá mức cần thiết. Nhưng nếu bạn có nhu cầu khác trong lĩnh vực liên quan, tôi cảm thấy nó rất phù hợp.


2
Nó là an toàn hơn và nhanh hơn để sử dụng developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich

2
Đồng ý, nhưng công bằng mà nói, tôi đã trả lời câu hỏi này vào năm 2015 trong khi thông số kỹ thuật API web tiền điện tử của mozilla được xuất bản vào tháng 1 năm 2017
Faraway
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.