2010-07-12 42 views
10

Ứng dụng web chứa dữ liệu nhạy cảm của người dùng. Nhà điều hành ứng dụng web cũng như nhà cung cấp dịch vụ lưu trữ sẽ không thể xem dữ liệu này. Vì vậy, tôi muốn lưu trữ các dữ liệu này trong DB được mã hóa với mật khẩu truy cập của người dùng.Chiến lược mã hóa để bảo mật dữ liệu nhạy cảm

dataInDB = encrypt (rawData, user password) 

Với chiến lược này tuy nhiên nó không phải là có thể thực hiện các trường hợp sử dụng thông thường cho khôi phục mật khẩu: Kể từ thường chỉ có giá trị hash của mật khẩu được lưu trữ bởi các ứng dụng web, các ứng dụng không thể gửi cũ, quên mật khẩu cho người dùng. Và với việc gán mật khẩu ngẫu nhiên mới, dữ liệu được mã hóa trong DB không còn có thể đọc được nữa.

Có giải pháp nào khác không?

+1

+1: Câu hỏi hay. Thật là một tình huống khủng khiếp ... –

Trả lời

7

Một giải pháp có thể (tôi không chịu trách nhiệm về bất kỳ sự phá hủy):

Khi mã hóa dữ liệu nhạy cảm, không sử dụng mật khẩu của người sử dụng như chìa khóa. Thay vào đó, lấy được khóa từ mật khẩu của người dùng (tốt nhất là sử dụng thuật toán chuẩn như PBKDF2). Chỉ trong trường hợp người dùng quên mật khẩu của họ, bạn có thể giữ một bản sao của khóa dẫn xuất này (được mã hóa bằng một khóa khác có nguồn gốc từ câu trả lời của người dùng) của người dùng. Nếu người dùng quên mật khẩu của họ, họ có thể trả lời câu hỏi bảo mật của họ. Chỉ có câu trả lời đúng sẽ giải mã mật khẩu ban đầu khóa (không phải mật khẩu ban đầu). Điều này cho bạn cơ hội để mã hóa lại thông tin nhạy cảm.

Tôi sẽ chứng minh bằng mã giả (Python-esque), nhưng trước tiên hãy xem bảng có thể cho người dùng. Đừng để bị cuốn vào các cột chỉ được nêu ra, họ sẽ trở nên rõ ràng sớm ...

CREATE TABLE USERS 
(
    user_name    VARCHAR, 

    -- ... lots of other, useful columns ... 

    password_key_iterations NUMBER, 
    password_key_salt  BINARY, 
    password_key_iv   BINARY, 
    encrypted_password_key BINARY, 
    question    VARCHAR, 
    answer_key_iterations NUMBER, 
    answer_key_salt   BINARY 
) 

Khi nói đến thời gian để đăng ký một người sử dụng, họ phải cung cấp một câu hỏi và trả lời:

def register_user(user_name, password, question, answer): 
    user = User() 

    # The question is simply stored for later use 
    user.question = question 

    # The password secret key is derived from the user's password 
    user.password_key_iterations = generate_random_number(from=1000, to=2000) 
    user.password_key_salt = generate_random_salt() 
    password_key = derive_key(password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    # The answer secret key is derived from the answer to the user's security question 
    user.answer_key_iterations = generate_random_number(from=1000, to=2000) 
    user.answer_key_salt = generate_random_salt() 
    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The password secret key is encrypted using the key derived from the answer 
    user.password_key_iv = generate_random_iv() 
    user.encrypted_password_key = encrypt(password_key, key=answer_key, iv=user.password_key_iv) 

    database.insert_user(user) 

Nếu người dùng quên mật khẩu của họ, hệ thống sẽ vẫn phải yêu cầu người dùng trả lời câu hỏi bảo mật của họ. Không thể khôi phục mật khẩu của chúng tôi, nhưng khóa có nguồn gốc từ mật khẩu có thể được. Điều này cho phép hệ thống để tái mã hóa các thông tin nhạy cảm bằng cách sử dụng mật khẩu mới:

def reset_password(user_name, answer, new_password): 
    user = database.rerieve_user(user_name) 

    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The answer key decrypts the old password key 
    old_password_key = decrypt(user.encrypted_password_key, key=answer_key, iv=user.password_key_iv) 

    # TODO: Decrypt sensitive data using the old password key 

    new_password_key = derive_key(new_password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    # TODO: Re-encrypt sensitive data using the new password key 

    user.encrypted_password_key = encrypt(new_password_key, key=user.answer_key, iv=user.password_key_iv) 

    database.update_user(user) 

Tất nhiên, có một số nguyên tắc mã hóa nói chung không rõ ràng nêu bật tại đây (chế độ mật mã, vv ...) mà là những trách nhiệm của người thực hiện để tự làm quen với.

Hy vọng điều này sẽ giúp ích một chút! :)

Cập nhật kê biếu không của bình luận Eadwacer của

Như Eadwacer nhận xét:

tôi sẽ tránh phát sinh các khóa trực tiếp từ mật khẩu (entropy hạn chế và thay đổi mật khẩu sẽ yêu cầu tái mã hóa tất cả các dữ liệu). Thay vào đó, hãy tạo một khóa ngẫu nhiên cho mỗi người dùng và sử dụng mật khẩu để mã hóa khóa. Bạn cũng sẽ mã hóa khóa bằng cách sử dụng khóa bắt nguồn từ các câu hỏi bảo mật.

Đây là một phiên bản sửa đổi của giải pháp tôi đưa lời khuyên tuyệt vời của mình vào xem xét:

CREATE TABLE USERS 
(
    user_name      VARCHAR, 

    -- ... lots of other, useful columns ... 

    password_key_iterations  NUMBER, 
    password_key_salt    BINARY, 
    password_encrypted_data_key BINARY, 
    password_encrypted_data_key_iv BINARY, 
    question      VARCHAR, 
    answer_key_iterations   NUMBER, 
    answer_key_salt    BINARY, 
    answer_encrypted_data_key  BINARY, 
    answer_encrypted_data_key_iv BINARY, 
) 

Sau đó, bạn sẽ đăng ký người dùng như sau:

def register_user(user_name, password, question, answer): 
    user = User() 

    # The question is simply stored for later use 
    user.question = question 

    # The randomly-generated data key will ultimately encrypt our sensitive data 
    data_key = generate_random_key() 

    # The password key is derived from the password 
    user.password_key_iterations = generate_random_number(from=1000, to=2000) 
    user.password_key_salt = generate_random_salt() 
    password_key = derive_key(password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    # The answer key is derived from the answer 
    user.answer_key_iterations = generate_random_number(from=1000, to=2000) 
    user.answer_key_salt = generate_random_salt() 
    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The data key is encrypted using the password key 
    user.password_encrypted_data_key_iv = generate_random_iv() 
    user.password_encrypted_data_key = encrypt(data_key, key=password_key, iv=user.password_encrypted_data_key_iv) 

    # The data key is encrypted using the answer key 
    user.answer_encrypted_data_key_iv = generate_random_iv() 
    user.answer_encrypted_data_key = encrypt(data_key, key=answer_key, iv=user.answer_encrypted_data_key_iv) 

    database.insert_user(user) 

Bây giờ, đặt lại mật khẩu của người dùng trông giống như sau:

def reset_password(user_name, answer, new_password): 
    user = database.rerieve_user(user_name) 

    answer_key = derive_key(answer, iterations=user.answer_key_iterations, salt=user.answer_key_salt) 

    # The answer key decrypts the data key 
    data_key = decrypt(user.answer_encrypted_data_key, key=answer_key, iv=user.answer_encrypted_data_key_iv) 

    # Instead of re-encrypting all the sensitive data, we simply re-encrypt the password key 
    new_password_key = derive_key(new_password, iterations=user.password_key_iterations, salt=user.password_key_salt) 

    user.password_encrypted_data_key = encrypt(data_key, key=new_password_key, iv=user.password_encrypted_data_key_iv) 

    database.update_user(user) 

Hy vọng đầu tôi vẫn hoạt động rõ ràng đêm nay ...

+0

Cảm ơn Adam, đây chính xác là những gì tôi đang tìm kiếm. Tôi sẽ cố gắng thực hiện giải pháp này trong các ngày tiếp theo trong Java bằng cách sử dụng API Java Crypto và đăng kết quả ở đây. – Dominik

+0

Một điều không rõ ràng đối với tôi cho đến bây giờ: Đối với trường hợp sử dụng thông thường của webapp (đăng nhập và xem dữ liệu người dùng đã được mã hóa), người dùng không nên trả lời câu hỏi bảo mật. Có đúng không, rằng khóa bí mật mật khẩu, được sử dụng để mã hóa dữ liệu nhạy cảm, phải được bắt nguồn theo cách tương tự như đối với việc tạo khóa ban đầu của khóa đó. Và để tái sản xuất cùng một khóa, lặp lại và muối được lưu trữ trong Db quá. – Dominik

+0

@Dominik: Bạn đúng, đó là lý do tại sao chúng tôi lưu trữ các lần lặp lại và muối trong cơ sở dữ liệu. –

Các vấn đề liên quan