Lý luận các khả năng tấn công với mã hash? tính chất không đảo ngược ? Ứng dụng với lưu mật khẩu với Bscrypt
Posted on November 6, 2022 • 13 minutes • 2566 words
Mở đầu:
Lời đầu : Mật khẩu của khách hàng trong mỗi hệ thống hầu như đều được lưu dạng các “hash”, Vậy tại sao hash lại là thứ được chọn để lưu lại thông tin nhạy cảm như password ?
Nếu hacker có trong tay thông tin về tài khoản và mật khẩu dạng “hash” của khách hàng thì hacker có tấn công các tài khoản trong hệ thống một cách hàng loạt được không?
Mục đích của bài viết xây dựng kịch bản tấn công của kẻ tấn công khi lấy được thông tin tài khoản của bạn và mật khẩu hash. Đưa ra lý luận mức độ secure của hash với thuật toán. Cũng như một phần trả lời cho câu hỏi vì sao chúng ta phải đặt mật khẩu dài ít nhất 8 ký tự gồm ký tự đặt biệt . Tại sao chúng ta hay bị giới hạn số lần đăng nhập ? Hay cứ 6 tháng chúng ta luôn bị nhắc nhở phải thay mật khẩu.
p/s: Mình luôn chọn sha256 làm ví dụ. tại sao mình chọn thuật toán này vì đây là một thuật toán mạnh và được sử dụng rộng rãi
Bài này được viết như là một phần của bài https://hoangtrungkien210895.github.io/en/blog/sha-256-how-to-work/
Các khả năng tấn công đối với mã hash
Như các bạn biết đấy đối với password thì mã hash có công dụng che dấu mật khẩu gốc để tránh một cuộc tấn công thử nghiệm dựa trên dựa trên dữ liệu gốc . Cho nên trên các nền tảng cơ sở dữ liệu về tài khoản, thông tin về mật khẩu khách hàng luôn được lưu dựa trên cơ chế hash để tạo lớp phòng thủ cho mật khẩu của khách hàng.
Cho dù có bị lộ cả cơ sở dữ liệu về thông tin tài khoản hacker cũng không thể dựa vào đó để đăng nhập vào tài khoản của bạn .
Vậy tại sao hacker lại không làm được điều đó cho dù đã nắm toàn bộ cơ sở dữ liệu của bạn?
Trước hết trả lời câu hỏi trên mình sẽ đặt câu hỏi ngược lại tại không lưu mật khẩu của bạn dưới dạng thô không mã hoá .
Trả lời : nếu mật khẩu ta được lưu công khai khi bị phát hiện hacker có thể sử dụng mật khẩu của mình theo 1 cách thẳng thắn mà không hề tốn công sức chút nào.
Khi có được mật khẩu clear text (mật khẩu thô) vd: 123456a@ hacker sẽ nhận biết được thói quen cài đặt mật khẩu . Qua tên ngày sinh . quốc tịch màu da , tôn giáo.
Hacker có thể dùng nó để dựng kích bản tấn công thử nghiệm password với các tài khoản khác của bạn với tỉ lệ trùng khớp cao .
Quay lại câu hỏi tại sao hacker lại không đăng nhập được cho dù đã nắm toàn bộ cơ sở dữ liệu của bạn?
Vậy nếu hacker thu thập được đoạn mã hash của password và tên tài khoản thì cách hacker tấn công đoạn mã đó để lấy ra được thông tin gốc của password sẽ có kịch bản như thế nào? (Đặt tình huống hacker bắt buộc phải tìm được mật khẩu gốc của khách hàng mới có thể tấn công tài khoản khách hàng thông qua cơ chế đăng nhập . Mình sẽ viết kịch bản theo hướng tấn công login -> cấp quyền đương nhiên sẽ có những kiểu tấn công khác nhưng nó lại chưa liên quan đến chủ đề lần này):
Tình huống giả lập
Mình sẽ xây dựng giả lập có 2 tình huống sảy ra. 1: Đảo ngược Hash và 2: Xây dựng từ điển hash để tìm kiếm hoặc “đào hash”
Cách 1: Đảo ngược Hash -> Hacker tìm hướng đảo ngược đoạn hash dựa vào thuật toán để tìm được chuỗi mật khẩu gốc
Thử nghiệm với mẫu thuật toán băm SHA256
Trong thuật toán băm dùng các toán tử tính toán với các unit . Đơn vị trong thuật toán sha 256 dùng là 32 bit cho một “unit” . Ví dụ bạn có một dãy hash đã được hash bởi thuật toán gồm 64 ký tự .Bạn cố gắng xây dựng logic để đảo ngược chuỗi hash này thành clear data . Bước bạn sẽ phải chia tách mỗi 8 ký tự trở thành 1 “unit 32 bit” trong 64 ký tự hash gốc .
Ngay tiếp theo bạn sẽ phải đối mặt với việc lấy 1 “unit” là kết quả cuả 1 phép cộng 2 chuỗi bit theo công thức là “h0 + a” = unit bạn đang xử lý . Việc đảo ngược là không thể bởi cả “h0” và “a” là các biến số được tính toán từ trước đó dựa vào clear data .
Vậy bạn sẽ phải đoán 1 chuỗi bit 32 ký tự bạn có là kết quả của các “h0” + “a” nào cho ra kết quả trùng khớp với “unit” bạn có và mỗi mẫu h0 a “hợp lý” như vậy nó phải được tính toán song song với với 7 “unit” còn lại (để bạn dễ hình dung ví dụ mình có 10 và tìm x , y tổng bằng 10 : ta có các mẫu thử 1 và 9| 9 và 1 | 2 và 8 |8 và 2| 3 và 7| 7 và 3 | 4 và 6 | 6 và 4| 5 và 5 .Với sha256 trong trường hợp xấu nhất unit sẽ có giá trị 4294967296 vậy ta sẽ có các mẫu thử là 4294967295 và 1, 4294967294 và 2 …. )
Mỗi một cách chọn h0 và a của 1 unit khi thay đổi 1 chút sẽ khi kết hợp với 7 phần còn lại nó sẽ tạo ra 1 chỉnh hợp mới . Không có cách nào để bạn chắc chắn kết quả của bạn là chắc chắn là của x + y để bạn có thể thực hiện cơ chế đào sâu.
Tương đương như vậy sau khi đoán xong với các mẫu chỉnh hợp ta vào bên trong. Ta lại có hai mẫu “h0 và a” này được tính toán bằng các toán tử cộng trừ & hoặc xor với cách tính 3 ngôi , 5 ngôi . Được lồng trong các vòng lặp 0 -> 64 và 16 -> 63 và vòng lặp này lại được lặp trong lồng to hơn khi kích thước đầu vào lớn hơn tối thiểu . Khi các biến số sử dụng của vòng lặp trước là kết quả tính toán của vòng lặp sau . Tất cả các trường hợp có thể xảy ra sẽ tạo ra 1 “thông tin mật khẩu gốc khác nhau hoàn toàn” nếu đảo ngược . Vậy với việc giả lập và đảo ngược data hash và tìm ra chuỗi gốc và hoàn toàn không khả thi khi độ phức tạp là siêu lớn và không hạn lượng được đầu vào với các mẫu dự đoán.
Ta có đoạn giả mã của sha256
Initialize hash values:
(first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
h0 := 0x6a09e667
h1 := 0xbb67ae85
h2 := 0x3c6ef372
h3 := 0xa54ff53a
h4 := 0x510e527f
h5 := 0x9b05688c
h6 := 0x1f83d9ab
h7 := 0x5be0cd19
Initialize array of round constants:
(first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
k[0..63] :=
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
Process the message in successive 512-bit chunks:
break message into 512-bit chunks
for each chunk
create a 64-entry message schedule array w[0..63] of 32-bit words
(The initial values in w[0..63] don't matter, so many implementations zero them here)
copy chunk into first 16 words w[0..15] of the message schedule array
Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array:
for i from 16 to 63
s0 := (w[i-15] rightrotate 7) xor (w[i-15] rightrotate 18) xor (w[i-15] rightshift 3)
s1 := (w[i-2] rightrotate 17) xor (w[i-2] rightrotate 19) xor (w[i-2] rightshift 10)
w[i] := w[i-16] + s0 + w[i-7] + s1
Initialize working variables to current hash value:
a := h0
b := h1
c := h2
d := h3
e := h4
f := h5
g := h6
h := h7
Compression function main loop:
for i from 0 to 63
S1 := (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)
ch := (e and f) xor ((not e) and g)
temp1 := h + S1 + ch + k[i] + w[i]
S0 := (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)
maj := (a and b) xor (a and c) xor (b and c)
temp2 := S0 + maj
h := g
g := f
f := e
e := d + temp1
d := c
c := b
b := a
a := temp1 + temp2
Add the compressed chunk to the current hash value:
h0 := h0 + a
h1 := h1 + b
h2 := h2 + c
h3 := h3 + d
h4 := h4 + e
h5 := h5 + f
h6 := h6 + g
h7 := h7 + h
Produce the final hash value (big-endian):
digest := hash := h0 append h1 append h2 append h3 append h4 append h5 append h6 append h7
Cách 2: Hacker biết thuật toán băm chúng ta . Tổ chức xây dựng bộ từ điển hash từ đó so sánh hash hoặc sử dụng máy tính thử nghiệm từng trường hợp từ mật khẩu sinh ra hash và so sánh với hash đang có:
Với cách này mình sẽ tính cost của việc xây dựng một bộ từ điển trong đó chứa tất cả khả năng của một password nếu sắp xếp các ký tự thành một chuỗi mật khẩu 8 ký tự hoàn chỉnh với sha256.
Vậy để xây dựng 1 dictory cho một đoạn mật khẩu gồm 8 ký tự thì ta sẽ tính ra với 8 ký tự sẽ có bao nhiêu cách chọn .Giả sử với các ký tự char từ 33 đến 126 (93 ký tự )của bảng mã ascii là ký tự valid để lập mật khẩu vậy ta sẽ có tổng :
Chỉnh hợp của 8 chập 93 =4.106.246.912.127.360 sấp xỉ 4 triệu tỷ cách sắp sếp mật khẩu 8 ký tự
=> Tương ứng nếu bạn xây dựng 1 dictory cho để tìm kiếm chuỗi gốc cho mật khẩu được hash với 8 ký tự thì bạn phải sinh ra số lượng hash tương ứng .
Ta cho đoạn hash được sử dụng là sha256 tức là 1 đoạn mã hash ít nhất để lưu lại sẽ cần 256 bit = 32 byte bộ nhớ , với 32 đoạn hash ta sẽ có được 1MB . ta có 1024 MB = 1 GB và 1024 GB = 1 TB .
Vậy cần 4.106.246.912.127.360 / (32x 1024x1024) = 122.375.694 (tb) để lưu 1 dictory cho 8 ký tự .
Và việc này nếu là 9 ký tự 10 ký tự thì kết quả sẽ được tính với cấp số nhân cho nên việc xây dựng dictory với đầy đủ khả năng lưu trữ cho tất cả các tình huống của password là điều gần như không có thể.
Vậy nếu sử dụng các siêu máy tính để thực hiện “đào” với trường hợp 8 ký tự , 9 ký tự , 10 ký tự thì sao việc có khả năng tìm được mật khẩu gốc chứ ?
Câu trả là có ? Tức là vẫn có thể
Vậy làm sao để ngăn chặn điều này ?
Giải pháp : Ta dùng BCrypt
BCrypt là gì?
Là việc vận dụng hash function làm tham số cùng các tham số : cost (số lần hash liên tiếp) , salt một đoạn mã input đầu vào được trộn vào message gốc và message gốc.
Cơ chế hoạt động:
Ví dụ là password là message gốc: 123456 , sử dụng với mã salt: abcdef , thuật toán băm sử dụng sha256 , và một cost 10 .
Thì với Bcrypt sẽ được tính toán bằng việc hash liên tục message ( password+ salt ) (“123456abcdef”) với số lần hash 2^10 hay 1024 vòng lặp .
Mã ouput của nó sẽ là :
$2y$10$RT2Pnlx7aaG2EDHy8SgdIunndkeiLB.kUTzSdVoKgL2x/TEr1tkZG
|__|__|_____________________|_______________________________|
Alg Cost Salt Hash
Vậy tại sao lại nên sử dụng bcrypt ?
- Lý do bcrypt cung cấp 2 cách thức để phòng chống việc đào đó là cost và salt .
Vậy cost ảnh hưởng như nào với việc xây dựng từ điển hay là việc đào hash ?
- Sau khi hash một clear text thì hash đó của bạn vẫn chưa phải là bản final để so sánh vì sau khi chuỗi gốc được hash thì nó sẽ hash đi hash lại rất nhiều lần hacker phải biết chính xác cost của. của một chuỗi bcrypt để có thể hash cùng số lần tương ứng.
Và salt thì ảnh hưởng thế nào ?
- Với việc thêm salt vào chuỗi cleartext thực chất nó làm 2 việc . Biến đổi dữ liệu clear text thành một chuỗi trộn (giữa clear text và salt) và thứ 2 nó làm tăng kích thước đầu vào khi hash . ta tưởng tưởng tượng với 8 ký tự đầu vào ta phải tính với 4 triệu tỷ khả năng thì ta thêm 20 ký tự salt vào ký tự đầu vào vậy ta sẽ phải tính với bài toán 38 ký tự đầu vào =)))
Với việc có salt thì : Password hacker đoán được qua việc đào luôn luôn không phải là password gốc mà là một chuỗi được trộn . từ việc này ta phòng chống việc hacker thử nhiều lần bằng việc thử password clear text thông qua đăng nhập = giới hạn số lần đăng nhập 5 lần chẳng hạn.
Cũng như vậy để chống lại việc tấn công theo kiểu đào hash thì thường trong vòng 3 tháng chúng ta phải đổi mật khẩu một lần.
Kết luận: vậy với cách thức mã hoá mật khẩu thành chuỗi hash để lưu thông tin thì hacker gần như là không thể lấy được thông tin về mật khẩu gốc của bạn cũng như cái giá cho việc này là rất đắt. Người ta đã từng tính toán nếu việc này nếu sử dụng một máy tính thông thường để tính toán ra chuỗi mật khẩu gốc của bạn sẽ mất cực kì nhiều năm và sau khi đọc bài luận này bạn cũng sẽ một phần hiểu được vì sao chúng ta luôn bị các hãng lớn như fb ,google, bắt đặt mật khẩu theo các chuẩn secure mạnh của họ (đủ độ dài , đủ phức tạp ) và tại sao thông thường 6 tháng chúng ta bị ép đổi mật khẩu một lần =)).
Hoàng Kiên 06/11/2022