Vấn đề ở đây về cơ bản là vấn đề của entropy. Vì vậy, hãy bắt đầu tìm kiếm ở đó:
Entropy mỗi ký tự
Số bit của entropy trên mỗi byte là:
- Ký tự Hex
- Số bit: 4
- Giá trị: 16
- Entropy trong 72 ký tự: 288 bit
- Alpha-Numeric
- Số bit: 6
- Giá trị: 62
- Entropy trong 72 ký tự: 432 bit
- Ký hiệu "Chung"
- Số bit: 6,5
- Giá trị: 94
- Entropy trong 72 ký tự: 468 bit
- Byte đầy đủ
- Số bit: 8
- Giá trị: 255
- Entropy trong 72 ký tự: 576 bit
Vì vậy, cách chúng ta hành động phụ thuộc vào kiểu nhân vật mà chúng ta mong đợi.
Vấn đề đầu tiên
Vấn đề đầu tiên với mã của bạn, là bước băm "tiêu" của bạn đang xuất ra các ký tự hex (vì tham số thứ tư đến hash_hmac()
không được đặt).
Do đó, bằng cách băm hạt tiêu của bạn vào, bạn đang cắt một cách hiệu quả entropy tối đa có sẵn cho mật khẩu theo hệ số 2 (từ 576 đến 288 bit có thể ).
Vấn đề thứ hai
Tuy nhiên, sha256
chỉ cung cấp 256
các bit entropy ngay từ đầu. Vì vậy, bạn đang cắt một cách hiệu quả 576 bit có thể xuống 256 bit. Bước băm của bạn * ngay lập tức *, theo định nghĩa sẽ mất
ít nhất 50% entropy có thể có trong mật khẩu.
Bạn có thể giải quyết một phần điều này bằng cách chuyển sang SHA512
, nơi bạn chỉ giảm entropy có sẵn khoảng 12%. Nhưng đó vẫn là một sự khác biệt không đáng kể. 12% đó làm giảm số hoán vị đi một hệ số 1.8e19
. Đó là một con số lớn ... Và đó là yếu tố nó làm giảm nó bằng ...
Vấn đề cơ bản
Vấn đề cơ bản là có ba loại mật khẩu trên 72 ký tự. Tác động của hệ thống phong cách này lên chúng sẽ rất khác nhau:
Lưu ý: từ đây trở đi, tôi giả sử chúng ta đang so sánh với hệ thống tiêu sử dụng SHA512
với đầu ra thô (không phải hex).
Mật khẩu ngẫu nhiên entropy cao
Đây là những người dùng của bạn sử dụng trình tạo mật khẩu tạo ra số lượng khóa lớn cho mật khẩu. Chúng là ngẫu nhiên (được tạo ra, không phải do con người chọn) và có entropy cao trên mỗi ký tự. Những loại này đang sử dụng byte cao (ký tự> 127) và một số ký tự điều khiển.
Đối với nhóm này, hàm băm của bạn sẽ làm giảm đáng kể lượng entropy có sẵn của chúng vào bcrypt
.
Hãy để tôi nói rằng một lần nữa. Đối với những người dùng đang sử dụng mật khẩu dài, entropy cao, giải pháp của bạn làm giảm đáng kể độ mạnh của mật khẩu của họ xuống một lượng có thể đo lường được. (62 bit entropy bị mất đối với mật khẩu 72 ký tự và nhiều hơn nữa đối với mật khẩu dài hơn)
Mật khẩu ngẫu nhiên entropy trung bình
Nhóm này đang sử dụng mật khẩu có chứa các ký hiệu chung, nhưng không có byte cao hoặc ký tự điều khiển. Đây là những mật khẩu có thể đánh máy của bạn.
Đối với nhóm này, bạn sẽ mở khóa nhiều entropy hơn một chút (không tạo nó, nhưng cho phép nhiều entropy hơn để phù hợp với mật khẩu bcrypt). Khi tôi nói hơi, tôi có nghĩa là hơi. Hòa vốn xảy ra khi bạn sử dụng tối đa 512 bit mà SHA512 có. Do đó, đỉnh cao là 78 ký tự.
Hãy để tôi nói rằng một lần nữa. Đối với loại mật khẩu này, bạn chỉ có thể lưu trữ thêm 6 ký tự trước khi hết entropy.
Mật khẩu không ngẫu nhiên entropy thấp
Đây là nhóm đang sử dụng các ký tự chữ-số có lẽ không được tạo ngẫu nhiên. Một cái gì đó giống như một trích dẫn kinh thánh hoặc tương tự. Các cụm từ này có khoảng 2,3 bit entropy trên mỗi ký tự.
Đối với nhóm này, bạn có thể mở khóa nhiều entropy hơn đáng kể (không phải tạo nó, nhưng cho phép nhiều entropy hơn để phù hợp với đầu vào mật khẩu bcrypt) bằng cách băm. Điểm hòa vốn là khoảng 223 ký tự trước khi bạn hết entropy.
Hãy nói lại điều đó. Đối với loại mật khẩu này, việc băm trước chắc chắn tăng tính bảo mật đáng kể.
Trở lại với thế giới thực
Những loại tính toán entropy này không thực sự quan trọng nhiều trong thế giới thực. Điều quan trọng là đoán entropy. Đó là những gì ảnh hưởng trực tiếp đến những gì kẻ tấn công có thể làm. Đó là những gì bạn muốn tối đa hóa.
Trong khi có rất ít nghiên cứu về việc đoán entropy, có một số điểm mà tôi muốn chỉ ra.
Cơ hội đoán ngẫu nhiên 72 ký tự chính xác liên tiếp là cực kỳ thấp. Bạn có nhiều khả năng trúng xổ số Powerball hơn 21 lần, hơn là để xảy ra vụ va chạm này ... Đó là con số lớn mà chúng ta đang nói đến.
Nhưng chúng ta có thể không vấp phải nó về mặt thống kê. Trong trường hợp các cụm từ, cơ hội của 72 ký tự đầu tiên giống nhau cao hơn rất nhiều so với một mật khẩu ngẫu nhiên. Nhưng nó vẫn rất thấp (nhiều khả năng bạn sẽ thắng xổ số Powerball 5 lần, dựa trên 2,3 bit cho mỗi ký tự).
Thực tế
Thực tế, nó không thực sự quan trọng. Khả năng ai đó đoán đúng 72 ký tự đầu tiên, trong đó những ký tự sau tạo ra sự khác biệt đáng kể là rất thấp nên không đáng lo ngại. Tại sao?
Vâng, giả sử bạn đang sử dụng một cụm từ. Nếu một người có thể viết đúng 72 ký tự đầu tiên, họ thực sự may mắn (không có khả năng xảy ra), hoặc đó là một cụm từ phổ biến. Nếu đó là một cụm từ phổ biến, biến số duy nhất là thời gian tạo ra nó.
Hãy lấy một ví dụ. Hãy trích dẫn từ kinh thánh (chỉ vì đó là một nguồn văn bản dài phổ biến, không phải vì bất kỳ lý do nào khác):
Bạn sẽ không thèm muốn nhà hàng xóm của bạn. Bạn không được thèm muốn vợ của người hàng xóm, người hầu gái hay hầu gái của anh ta, con bò hay con lừa của anh ta, hoặc bất cứ thứ gì thuộc về hàng xóm của bạn.
Đó là 180 ký tự. Ký tự thứ 73 là ký tự g
thứ hai neighbor's
. Nếu bạn đoán nhiều như vậy, có khả năng bạn sẽ không dừng lại nei
, mà tiếp tục với phần còn lại của câu (vì đó là cách mật khẩu có thể được sử dụng). Do đó, "băm" của bạn không thêm nhiều.
BTW: Tôi TUYỆT ĐỐI KHÔNG ủng hộ việc sử dụng trích dẫn kinh thánh. Trong thực tế, hoàn toàn ngược lại.
Phần kết luận
Bạn sẽ không thực sự giúp được nhiều người sử dụng mật khẩu dài bằng cách băm trước. Một số nhóm bạn chắc chắn có thể giúp đỡ. Một số bạn chắc chắn có thể bị thương.
Nhưng cuối cùng, không có điều nào trong số đó là quá đáng kể. Những con số mà chúng tôi đang giải quyết chỉ là CÁCH quá cao. Sự khác biệt về entropy sẽ không nhiều.
Tốt hơn hết bạn nên để nguyên bcrypt. Bạn có nhiều khả năng làm hỏng việc băm (theo nghĩa đen, bạn đã làm điều đó rồi và bạn không phải là người đầu tiên hoặc cuối cùng mắc phải sai lầm đó) hơn là cuộc tấn công mà bạn đang cố gắng ngăn chặn sẽ xảy ra.
Tập trung vào việc bảo mật phần còn lại của trang web. Và thêm một máy đo entropy mật khẩu vào ô mật khẩu khi đăng ký để chỉ ra độ mạnh của mật khẩu (và cho biết nếu mật khẩu quá dài mà người dùng có thể muốn thay đổi nó) ...
Đó là ít nhất 0,02 đô la của tôi (hoặc có thể nhiều hơn 0,02 đô la) ...
Như cách sử dụng hạt tiêu "bí mật":
Thực sự không có nghiên cứu nào về việc đưa một hàm băm vào bcrypt. Do đó, không rõ liệu việc cấp một hàm băm "peppered" vào bcrypt có bao giờ gây ra các lỗ hổng không xác định hay không (chúng tôi biết việc làm này hash1(hash2($value))
có thể làm lộ ra các lỗ hổng đáng kể xung quanh khả năng chống va chạm và các cuộc tấn công preimage).
Vì bạn đang cân nhắc việc lưu trữ một khóa bí mật ("hạt tiêu"), tại sao không sử dụng nó theo cách đã được nghiên cứu và hiểu rõ? Tại sao không mã hóa băm trước khi lưu trữ?
Về cơ bản, sau khi bạn băm mật khẩu, hãy cấp toàn bộ đầu ra băm vào một thuật toán mã hóa mạnh. Sau đó lưu trữ kết quả được mã hóa.
Bây giờ, một cuộc tấn công SQL-Injection sẽ không làm rò rỉ bất kỳ thứ gì hữu ích, vì chúng không có khóa mật mã. Và nếu khóa bị rò rỉ, những kẻ tấn công không tốt hơn là nếu bạn sử dụng một hàm băm đơn giản (có thể chứng minh được, thứ gì đó với tiêu "pre-hash" không cung cấp).
Lưu ý: nếu bạn chọn làm điều này, hãy sử dụng thư viện. Đối với PHP, tôi mạnh mẽ khuyên Zend Framework 2 của Zend\Crypt
gói. Nó thực sự là người duy nhất tôi muốn giới thiệu vào thời điểm hiện tại. Nó đã được xem xét kỹ lưỡng và đưa ra tất cả các quyết định cho bạn (đó là một điều rất tốt) ...
Cái gì đó như:
use Zend\Crypt\BlockCipher;
public function createHash($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
return $blockCipher->encrypt($hash);
}
public function verifyHash($password, $hash) {
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
$hash = $blockCipher->decrypt($hash);
return password_verify($password, $hash);
}
Và nó có lợi vì bạn đang sử dụng tất cả các thuật toán theo những cách được hiểu rõ và nghiên cứu kỹ lưỡng (ít nhất là tương đối). Nhớ lại:
Bất cứ ai, từ những người nghiệp dư khó hiểu nhất đến những nhà mật mã giỏi nhất, đều có thể tạo ra một thuật toán mà bản thân anh ta không thể phá vỡ.