Giải pháp tốt hơn là lưu trữ "hàm băm một chiều" của mật khẩu, thường sử dụng hàm như
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
0 hoặc database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
0tài khoản người dùngsha1[mật khẩu]john@hotmail. com5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8betty@gmail. comcbfdac6008f9cab4083784cbd1874f76618d2a97Mặc dù máy chủ không lưu trữ mật khẩu văn bản thuần túy ở bất kỳ đâu nhưng nó vẫn có thể xác thực người dùng
def is_password_correct[user, password_attempt]:
return sha1[password_attempt] == user["sha1_password"]
Phân tích
Giải pháp này an toàn hơn lưu trữ mật khẩu văn bản thuần túy, vì theo lý thuyết, không thể "hoàn tác" hàm băm một chiều và tìm một chuỗi đầu vào tạo ra cùng một giá trị băm. Thật không may, tin tặc đã tìm ra cách xung quanh điều này
hậu quả xấu
Một vấn đề là nhiều hàm băm [bao gồm cả
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
0 và database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
0] xét cho cùng không phải là "một chiều" và các chuyên gia bảo mật đề xuất rằng các hàm này không được sử dụng nữa cho các ứng dụng bảo mật. [Thay vào đó, bạn nên sử dụng các hàm băm tốt hơn như database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
3 cho đến nay chưa có bất kỳ lỗ hổng nào được biết đến. ]Nhưng có một vấn đề lớn hơn. tin tặc hoàn toàn không cần phải "hoàn tác" hàm băm; . Điều này tương tự như thử tất cả các kết hợp của khóa kết hợp. Đây là mã sẽ trông như thế nào
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
Bạn có thể nghĩ rằng có quá nhiều mật khẩu khả thi để kỹ thuật này khả thi. Nhưng có ít mật khẩu phổ biến hơn nhiều so với bạn nghĩ. Hầu hết mọi người sử dụng mật khẩu dựa trên các từ trong từ điển [có thể thêm một vài số hoặc chữ cái]. Và hầu hết các hàm băm như sha1[] có thể được thực thi rất nhanh -- một máy tính thực sự có thể thử hàng tỷ kết hợp mỗi giây. Điều đó có nghĩa là hầu hết các mật khẩu có thể được tìm ra trong vòng chưa đầy 1 giờ cpu. Các chương trình như John The Ripper có thể làm điều này
Qua một bên. nhiều năm trước, máy tính không nhanh như vậy, vì vậy cộng đồng hacker đã tạo ra các bảng cầu vồng đã tính toán trước một tập hợp lớn các giá trị băm này trước thời hạn. Ngày nay, không ai sử dụng bảng cầu vồng nữa vì máy tính đủ nhanh mà không cần chúng
Vì vậy, tin xấu là bất kỳ người dùng nào có mật khẩu đơn giản như "password" hoặc "password123" hoặc bất kỳ mật khẩu nào trong số hàng tỷ mật khẩu có khả năng xảy ra cao nhất đều sẽ bị đoán mật khẩu. Nếu bạn có một mật khẩu cực kỳ phức tạp [hơn 16 số và chữ cái ngẫu nhiên], có lẽ bạn đã an toàn
Cũng lưu ý rằng đoạn mã trên đang tấn công hiệu quả tất cả các mật khẩu cùng một lúc. Không thành vấn đề nếu có 10 người dùng trong cơ sở dữ liệu của bạn hoặc 10 triệu, tin tặc sẽ không mất nhiều thời gian để đoán mật khẩu phù hợp. Tất cả vấn đề là tin tặc có thể lặp lại các mật khẩu tiềm năng nhanh như thế nào. [Và trên thực tế, việc có nhiều người dùng thực sự giúp ích cho tin tặc, vì nhiều khả năng ai đó trong hệ thống đã sử dụng mật khẩu "password123". ]
sha1[password] là những gì LinkedIn sử dụng để lưu trữ mật khẩu của nó. Và vào năm 2012, một lượng lớn mật khẩu đã bị rò rỉ. Theo thời gian, tin tặc có thể tìm ra mật khẩu văn bản thuần túy cho hầu hết các hàm băm này
Tóm lược. lưu trữ một hàm băm đơn giản [không có muối] không an toàn -- nếu tin tặc giành được quyền truy cập vào cơ sở dữ liệu của bạn, chúng sẽ có thể tìm ra phần lớn mật khẩu của người dùng
Một nỗ lực để làm cho mọi thứ an toàn hơn là "muối" mật khẩu trước khi băm nó
tài khoản người dùngsha1["salt123456789" + mật khẩu]john@hotmail. comb467b644150eb350bbc1c8b44b21b08af99268aabetty@gmail. com31aa70fd38fee6f1f8b3142942ba9613920dfea0.Muối ăn
Muối được cho là một chuỗi byte dài ngẫu nhiên. Nếu tin tặc có quyền truy cập vào các hàm băm mật khẩu mới này [nhưng không phải là muối], thì tin tặc sẽ khó đoán mật khẩu hơn nhiều vì chúng cũng cần biết muối.
hậu quả xấu
Tuy nhiên, nếu tin tặc đã đột nhập vào máy chủ của bạn, họ cũng có thể có quyền truy cập vào mã nguồn của bạn, vì vậy họ cũng sẽ biết được điều đó. Đó là lý do tại sao các nhà thiết kế bảo mật chỉ giả định điều tồi tệ nhất và không tin rằng muối là bí mật
Nhưng ngay cả khi muối không phải là một bí mật, thì việc sử dụng những chiếc bàn cầu vồng kiểu cũ mà tôi đã đề cập trước đây vẫn khó sử dụng hơn. [Những bảng cầu vồng đó được xây dựng với giả định là không có muối, vì vậy hàm băm có muối sẽ ngăn chặn chúng. ] Tuy nhiên, vì không ai sử dụng bảng cầu vồng nữa nên việc thêm một loại muối cố định không giúp được gì nhiều. Tin tặc vẫn có thể thực hiện cùng một vòng lặp cơ bản ở trên
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[SALT + password] in database_table:
print "Hacker wins! I guessed a password!", password
Tóm lược
thêm một loại muối cố định vẫn không đủ an toàn
Bước tiếp theo trong bảo mật là tạo một cột mới trong cơ sở dữ liệu và lưu trữ một loại muối khác nhau cho mỗi người dùng. Muối được tạo ngẫu nhiên khi tài khoản người dùng được tạo lần đầu [hoặc khi người dùng thay đổi mật khẩu của họ]
tài khoản người dùngaltsha1[muối + mật khẩu]john@hotmail. com2dc7fcc. 1a74404cb136dd60041dbf694e5c2ec0e7d15b42betty@gmail. comfadb2f. e33ab75f29a9cf3f70d3fd14a7f47cd752e9c550Xác thực người dùng không khó hơn nhiều so với trước đây
def is_password_correct[user, password_attempt]:
return sha1[user["salt"] + password_attempt] == user["password_hash"]
Tin tặc
Bằng cách có muối cho mỗi người dùng, chúng tôi nhận được một lợi ích to lớn. tin tặc không thể tấn công tất cả mật khẩu người dùng của bạn cùng một lúc
Thay vào đó, mã tấn công của anh ta phải thử từng người dùng một
for user in users:
PER_USER_SALT = user["salt"]
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[PER_USER_SALT + password] in database_table:
print "Hacker wins! I guessed a password!", password
Vì vậy, về cơ bản, nếu bạn có 1 triệu người dùng, việc có muối cho mỗi người dùng sẽ khiến việc tìm ra mật khẩu của tất cả người dùng của bạn khó hơn gấp 1 triệu lần. Nhưng điều này vẫn không phải là không thể đối với một hacker. Thay vì 1 giờ cpu, giờ đây họ cần 1 triệu cpu giờ, có thể dễ dàng thuê từ Amazon với giá khoảng 40.000 đô la
Vấn đề thực sự với tất cả các hệ thống mà chúng ta đã thảo luận cho đến nay là các hàm băm như sha1[] [hoặc thậm chí sha256[]] có thể được thực thi trên mật khẩu với tốc độ 100M+/giây [hoặc thậm chí nhanh hơn bằng cách sử dụng GPU]. Mặc dù các hàm băm này được thiết kế có tính đến bảo mật, nhưng chúng cũng được thiết kế sao cho chúng sẽ nhanh khi được thực thi trên các đầu vào dài hơn như toàn bộ tệp. dòng dưới cùng. các hàm băm này không được thiết kế để sử dụng cho việc lưu trữ mật khẩu
Thay vào đó, có một tập hợp các hàm băm được thiết kế riêng cho mật khẩu. Ngoài chức năng băm "một chiều" an toàn, chúng còn được thiết kế để hoạt động chậm.
Một ví dụ là Bcrypt
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
4 mất khoảng 100 mili giây để tính toán, chậm hơn sha1[] khoảng 10.000 lần. 100 mili giây đủ nhanh để người dùng không nhận thấy khi họ đăng nhập, nhưng đủ chậm để việc thực thi đối với một danh sách dài các mật khẩu có khả năng trở nên kém khả thi hơn. Chẳng hạn, nếu một hacker muốn tính toán bcrypt[] dựa trên danh sách một tỷ mật khẩu có khả năng xảy ra, sẽ mất khoảng 30.000 giờ cpu [khoảng 1200 đô la] -- và đó là cho một mật khẩu duy nhất. Chắc chắn không phải là không thể, nhưng còn nhiều việc hơn hầu hết các tin tặc sẵn sàng làmcách thức hoạt động của Bcrypt
Về cơ bản, "mẹo" là nó thực thi hàm mã hóa/băm nội bộ nhiều lần trong một vòng lặp. [Có những lựa chọn thay thế khác cho Bcrypt, chẳng hạn như
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
5 sử dụng thủ thuật tương tự. ]Ngoài ra, Bcrypt có thể định cấu hình, với tham số log_rounds cho biết số lần thực thi hàm băm bên trong đó. Nếu đột nhiên, Intel tung ra một máy tính mới nhanh hơn 1000 lần so với máy tính hiện đại nhất hiện nay, bạn có thể định cấu hình lại hệ thống của mình để sử dụng log_rounds nhiều hơn 10 lần so với trước đây [
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
6 là logarit], điều này sẽ hủy bỏ Bởi vì bcrypt[] quá chậm nên ý tưởng về các bảng cầu vồng lại trở nên hấp dẫn, do đó, muối cho mỗi người dùng được tích hợp vào hệ thống Bcrypt. Trên thực tế, các thư viện như bcrypt trên pypi lưu trữ muối trong cùng một chuỗi với hàm băm mật khẩu, vì vậy bạn thậm chí sẽ không phải tạo một cột cơ sở dữ liệu riêng cho muối
1. Đầu tiên, hãy cài đặt nó
def is_password_correct[user, password_attempt]:
return sha1[password_attempt] == user["sha1_password"]
12. Mã Python khi tạo tài khoản người dùng mới [hoặc đặt lại mật khẩu của họ]
def is_password_correct[user, password_attempt]:
return sha1[password_attempt] == user["sha1_password"]
2Hãy phân tích chuỗi đầu ra đó một chút
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
6 được sử dụng để tạo mật khẩu, kiểm soát lượng công việc [i. e. nó chậm như thế nào] để tính toánNếu bạn muốn hàm băm chậm hơn, bạn chuyển một giá trị lớn hơn cho
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
8def is_password_correct[user, password_attempt]:
return sha1[password_attempt] == user["sha1_password"]
5Trong mọi trường hợp, bạn lưu trữ chuỗi này trong cơ sở dữ liệu và khi cùng người dùng đó cố gắng đăng nhập, bạn truy xuất cùng giá trị được băm đó và thực hiện việc này
def is_password_correct[user, password_attempt]:
return sha1[password_attempt] == user["sha1_password"]
6Bạn có thể thắc mắc tại sao bạn lại chuyển hàm băm dưới dạng đối số salt sang
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
9Lý do điều này hiệu quả là hàm
database_table = {
"5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8": "john@hotmail.com",
"cbfdac6008f9cab4083784cbd1874f76618d2a97": "betty@gmail.com",
...}
for password in LIST_OF_COMMON_PASSWORDS:
if sha1[password] in database_table:
print "Hacker wins! I guessed a password!"
9 thông minh và có thể trích xuất muối từ chuỗi for password in LIST_OF_COMMON_PASSWORDS:
if sha1[SALT + password] in database_table:
print "Hacker wins! I guessed a password!", password
1 đóĐiều này thật tuyệt, vì điều đó có nghĩa là bạn không bao giờ phải tự mình lưu trữ, phân tích cú pháp hoặc xử lý bất kỳ giá trị muối nào -- giá trị duy nhất bạn cần xử lý là chuỗi băm duy nhất chứa mọi thứ bạn cần