등록 된 사용자와 민감한 데이터 간의 링크 암호화

Nov 20 2020

데이터베이스에 대한 전체 액세스 권한이 있더라도 암호없이 사용자를 민감한 데이터에 연결하는 것은 비현실적으로 만들고 싶습니다.

또한 사용자가 여러 개의 민감한 데이터를 가지고있는 경우 서로 다른 부분을 함께 연결하는 것도 피하고 싶습니다.

의견과 일부 검색을 기반으로 질문을 업데이트했습니다.

내가 발견 몇 가지 비슷한 질문을하지만 아무도 내가 찾고 세부 사항을 제공하거나 약간 다른 전제 조건 (f.ex. 데이터 공유)을 갖고있는 것 같다 않습니다.

따라서 웹 사이트 사용자를 등록하고 표준 CRUD 작업으로 관리하는 것 외에도 사용자의 민감한 데이터 일부를 데이터베이스에 저장하고 있습니다. 그런 점 에서이 질문 은 비슷하며 답변은 나에게도 지침을 제공합니다. 특히 "암호를 몰라도 암호화 키를 얻는 데 사용할 수있는 것은 [내] 데이터베이스에 저장" 해서는 안됩니다 .

클라이언트 측은 간단한 html / css / js 페이지로 구성되어 있으며 현재 데스크톱 앱은 옵션이 아닙니다. 개별 데이터에 관심이 없도록 그룹 (데이터의 변수 기준)으로 만 데이터를 분석합니다. 그러나 사용자가 자신의 데이터를 볼 수 있도록하고, 예를 들어 원하는 경우 데이터를 삭제할 수 있도록하고 싶습니다.

내가 생각하는 것은 각 데이터에 대한 키를 생성하고, key–data_id 쌍을 데이터베이스로 암호화하고, 데이터를 저장하거나 사용자가 데이터를보고 싶을 때 암호화되지 않은 키가 필요할 때마다 해독하는 것입니다.

import json
from cryptography.fernet import Fernet


def get_key(password, data_id):

    # Check that the given password is valid
    if not self.check_password(password):
        raise KeyError('The given password is not valid')
    
    # Always use string representation of the data_id since json allows only string keys
    data_id_str = str(data_id)

    # Create a Fernet object with the password
    f = Fernet(password)
    
    # Set the encoding for the bytes <-> string conversion    
    encoding = 'utf-8'
    
    # Decrypt and load into a dict the existing keys
    if self.encrypted_keys:
    
        # Ensure that the encrypted keys are in bytes for the Fernet
        bytes_encrypted_keys = bytes(self.encrypted_keys)

        # Decrypt the encrypted keys and transform the bytes object into a string
        keys_string = f.decrypt(bytes_encrypted_key).decode(encoding)
        
        # Load the string into a dict
        keys_dict = json.loads(keys_string)
    
    # Create an empty dict if no keys defined
    else:
        keys_dict = {}
    
    # Try to get a key for the data_id
    try:
        key = keys_dict[data_id_str]
    
    # The key not found
    except KeyError:
        
        # Generate a new a URL-safe 32-byte key and decode as a string into the keys_dict 
        key = keys_dict.setdefault(
            data_id_str,
            Fernet.generate_key().decode(encoding),
        )
        
        # Turn the updated keys_dict into a string
        updated_keys_string = json.dumps(keys_dict)

        # Encode the string into bytes for the Fernet
        bytes_keys = updated_keys_string.encode(encoding)

        # Encrypt the updated keys
        self.encrypted_keys = f.encrypt(bytes_keys)

        # Save the encrypted keys into the database
        self.encrypted_keys.save()
    
    # Return the decrypted key for the data_id
    return key

이것이 합리적인 과정처럼 보입니까? 내가 놓친 명백한 결함이 있습니까? 과잉인가요? 고려해야 할 다른 사항이 있습니까?

여기서 약점은 암호의 강도라는 것을 알고 있습니다. 나는 표준 강도 검사로 그것을 관리하려고 노력할 것입니다.

또한 서버에 대한 액세스가 프로세스를 가로 채고 키를 손상시킬 수있는 기능을 제공한다는 것을 이해합니다. 물론 데스크톱 앱없이이를 방지 할 수있는 방법이 있다면 관심이있을 것입니다. 현재 적어도 데이터베이스를 보호하기를 바라고 있습니다.

충고 해 주셔서 감사합니다!

답변

user10216038 Nov 22 2020 at 03:44

내가 올바르게 이해하고 있다면 :

암호화 및 암호 해독 및 키 보호는 잊어 버리십시오. 그 어느 것도 필요하지 않습니다.

암호 ID가 유지되는 것과 동일한 방식으로 해시를 사용하십시오 .

해시는 사용자를 공개하지 않고 데이터의 고유 한 식별자가됩니다.

사용자가 제공 한 암호에서 적절하게 복잡한 해시를 파생합니다. 사용자는 나중에 해당 데이터에 대해 일치하는 식별자를 파생하기 위해 다시 해시 할 수 있도록 해당 암호를 제공 할 수 있습니다.

소란스럽지 않고 소란스럽지 않으며 저장된 암호도 없습니다.

-새로 추가 된 제약에 대한 편집-

또한 사용자가 여러 개의 민감한 데이터를 가지고있는 경우 서로 다른 부분을 함께 연결하는 것도 피하고 싶습니다.

임의의 Salt 를 사용하여 모든 데이터 Blob에 대해 다른 해시 ID를 생성합니다. 다른 제약 조건이 없으면 일치하는 항목을 찾기 위해 시스템의 모든 솔트에 대한 해시를 계산해야합니다. 수백 가지에 대해서는 사소한 일이지만 훨씬 더 큰 값의 경우 추가 제약 조건이 필요할 수 있습니다.