DRF-Django가 Password를 생성하는 방법

jeong_hyeon·2022년 6월 1일
0

Django_DRF

목록 보기
8/12
post-custom-banner

Django 프로젝트를 생성하면 settings.py에는SECRET_KEY와 이와 관련된 보안 경고 주석이 있다

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '<your secret key>'

staging 환경을 새로 구성하면서 보안 조치를 위해서 기존 환경과 다른 SECRET_KEY 값을 넣어주었고, 그에 따라 기존 DB를 복사해서 넣었을 때 예전 패스워드를 사용하지 못할 것으로 예상했습니다.

하지만 그렇지 않았습니다. 기존 패스워드를 입력해도 로그인이 진행 되었다.

그렇다면 SECRET_KEY는 패스워드 생성과 관계가 없는것일까? 가 궁금해져서 포스팅을 하게 되었습니다.

Where does Django use the SECRET key?

공식문서 의 설명에 따르면 SECRET_KEY 는

  • django.contrib.sessions.backends.cache 이외의 session backend를 사용하고 있거나, 기본 get_session_auth_hash()를 사용하는 모든 sessions
  • CookieStorage 혹은 FallbackStorage 를 사용하는 모든 messages
  • 모든 PasswordResetView
  • 다른 키가 제공되지 않는 암호화된 서명

에서 사용한다고 합니다.

역시.. Password를 생성하는데 사용한다고 적혀 있지 않습니다.

Django가 패스워드를 생성하는 방법

공식문서에 따르면 장고의 패스워드는 기본적으로 PBKDF2 알고리즘을 사용하고
(NIST의 권장사항)

<algorithm>$<iterations>$<salt>$<hash>

위의 형태와 같이 저장된다고 합니다.

User를 생성해서 DB의 User 테이블에 저장된 password 를 보면

pbkdf2_sha256$320000$mP81so9CdBlV6R52gXDgHS$RKEHamBwjVjmpqdkvQf1tGu/mntLnfMl3y9u4bYE3m4=

이렇게 생겼습니다.

pbkdf2_sha256 알고리즘을 사용했고 320000 번 iteration 해서 패스워드를 생성했습니다. 별 다른 설정 없이도 NIST의 권장사항을 잘 준수했습니다.

다시한번 공식 문서를 보면

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.ScryptPasswordHasher',
]

알고리즘을 생성하는 Hasher 는 이렇게 기본 설정이 되어 있고, 설정 값의 처음에 적혀 있는 것을 사용한다고 합니다. 여기서는 PBKDF2PasswordHasher를 사용하게 됩니다.

그래서 PBKDF2PasswordHasher의 코드를 보면

class PBKDF2PasswordHasher(BasePasswordHasher):
    """
    Secure password hashing using the PBKDF2 algorithm (recommended)

    Configured to use PBKDF2 + HMAC + SHA256.
    The result is a 64 byte binary string.  Iterations may be changed
    safely but you must rename the algorithm if you change SHA256.
    """

    algorithm = "pbkdf2_sha256"
    iterations = 320000
    digest = hashlib.sha256

    def encode(self, password, salt, iterations=None):
        self._check_encode_args(password, salt)
        iterations = iterations or self.iterations
        hash = pbkdf2(password, salt, iterations, digest=self.digest)
        hash = base64.b64encode(hash).decode("ascii").strip()
        return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)

iterations 의 기본값은 32000 번이고, algorithm 은 pbkdf2_sha256 입니다.

hash 는 password, salt, iterations, digest를 이용해서 생성합니다.

그리고 salt 는 PBKDF2PasswordHasher 에 정의되어 있지 않아서 부모 클래스인 BasePasswordHasher 를 보면

class BasePasswordHasher:
    """
    Abstract base class for password hashers

    When creating your own hasher, you need to override algorithm,
    verify(), encode() and safe_summary().

    PasswordHasher objects are immutable.
    """

    
    ...

     def salt(self):
        """
        Generate a cryptographically secure nonce salt in ASCII with an entropy
        of at least `salt_entropy` bits.
        """
        # Each character in the salt provides
        # log_2(len(alphabet)) bits of entropy.
        char_count = math.ceil(self.salt_entropy / math.log2(len(RANDOM_STRING_CHARS)))
        return get_random_string(char_count, allowed_chars=RANDOM_STRING_CHARS)

랜덤 스트링 이라는것을 있습니다.

따라서 SECRET_KEY와 관련있는 부분은 전혀 없음을 알 수 있습니다.

결론

SECRET_KEY 의 값을 바꾸어도 기존의 패스워드를 그대로 사용할 수 있습니다.

PASSWORD_HASHERS 를 재정의하지 않는다면 Django의 버전을 바꾸어도, 프로젝트 명을 바꾸어도, repository를 바꾸어도 기존의 패스워드를 그대로 사용할 수 있습니다.

post-custom-banner

0개의 댓글