본문 바로가기

해시 함수와 비밀번호 저장: 사이트가 비밀번호를 직접 보관하지 않는 이유

📑 목차

    사이트는 내 비밀번호를 정말 알고 있을까

    웹사이트에 회원가입을 할 때마다 비밀번호를 입력하게 된다. 이때 많은 사람은 “사이트가 내 비밀번호를 그대로 알고 있다가, 다음에 로그인할 때 맞는지 비교하겠지”라고 막연히 생각한다. 하지만 제대로 된 서비스라면 이렇게 비밀번호를 직접 보관하지 않는다.

    대신 대부분의 서비스는 해시 함수(hash function)라는 기술을 이용해 비밀번호를 일방향으로 변환한 값만 데이터베이스에 저장한다. 또 그 과정에서 비밀번호 해시, 솔트(salt), 무차별 대입 공격(brute force) 같은 개념들이 함께 등장한다.

    이 글에서는 전산학을 전공하지 않은 사람도 이해할 수 있도록,

    • 해시 함수가 무엇인지,
    • 왜 비밀번호를 그대로 저장하지 않고 해시 값만 저장하는지,
    • 솔트와 같은 추가 기법이 실제로 어떤 보안 문제를 막는지,
    • 사용자가 비밀번호를 만들 때 무엇을 조심해야 하는지

    를 전산학 입문서처럼 차근차근 설명한다.
    이 글의 핵심 키워드는 해시 함수, 비밀번호 해시, 솔트(salt), 무차별 대입 공격, 비밀번호 저장 방식이다.


    해시 함수와 비밀번호 해시의 기본 개념

    1. 해시 함수란 무엇인가: 긴 정보를 짧은 지문으로 바꾸는 함수

    해시 함수(hash function)는 임의 길이의 입력 데이터를 고정된 길이의 짧은 값으로 바꾸는 함수이다.
    이때 만들어지는 짧은 값을 해시 값(hash value) 또는 다이제스트(digest)라고 부른다.

    물리적인 비유를 하나 들어 보면 이해하기 쉽다.

    • 사람마다 고유한 지문이 있는 것처럼,
    • 데이터마다 고유한 해시 값이 있다고 생각하면 된다.

    물론 실제로는 서로 다른 데이터가 같은 해시 값을 만들 가능성이 이론적으로는 존재한다. 이를 충돌(collision)이라고 한다.
    하지만 현대 암호학적 해시 함수(SHA-256 등)는

    • 현실적으로 충돌을 찾아내기가 매우 어렵게 설계되어 있다.

    암호학적 해시 함수에는 몇 가지 중요한 특징이 있다.

    1. 단방향성(one-way)
      • 해시 값만 가지고는 원래 입력값을 되돌리기 힘들다.
      • 즉, 해시 함수는 사실상 “한 방향으로만 간다”고 볼 수 있다.
    2. 입력에 대한 민감성
      • 입력이 아주 조금만 달라져도,
      • 출력 해시 값은 완전히 다른 값으로 바뀐다.
      • 비밀번호에서 글자 하나만 틀려도 해시 값이 크게 달라진다.
    3. 결정론적 특성
      • 같은 입력은 항상 같은 해시 값을 만든다.
      • 이 성질 덕분에 “비밀번호가 같은지”를 비교할 수 있다.

    이 세 가지 성질 때문에 해시 함수는 비밀번호 저장, 파일 무결성 검증, 블록체인 등 다양한 곳에서 사용된다.

    2. 사이트가 비밀번호를 “그대로” 저장하면 어떤 일이 벌어지는가

    만약 웹사이트가 사용자의 비밀번호를 그대로 평문(plain text)으로 데이터베이스에 저장한다면 어떤 위험이 있을까.

    1. 데이터베이스 유출 사고
      • 해킹, 내부자의 실수, 잘못된 설정 등으로 데이터베이스가 외부로 유출될 수 있다.
      • 이때 공격자는 사용자 이메일과 비밀번호를 한꺼번에 얻게 된다.
    2. 다른 사이트 계정까지 연쇄 피해
      • 많은 사용자는 여러 사이트에서 같은 비밀번호를 재사용한다.
      • 한 사이트에서 비밀번호가 유출되면,
        • 공격자는 그 비밀번호를 다른 사이트(메일, 쇼핑몰, SNS)에 그대로 시도할 수 있다.
    3. 내부 직원에 의한 오남용 가능성
      • 비밀번호를 그대로 저장하면
        • 내부 관리자도 사용자의 비밀번호를 볼 수 있다.
      • 이는 보안과 개인정보 보호 측면에서 큰 문제이다.

    이 때문에 제대로 된 서비스라면 비밀번호 원문을 데이터베이스에 직접 저장하지 않는다.
    대신 해시 값만 저장하고, 해시 함수를 이용해 비밀번호를 검증한다.

    3. 비밀번호 해시 저장 방식: 로그인 과정에서 어떤 일이 일어나는가

    비밀번호를 해시 함수로 저장하는 기본 흐름은 다음과 같다.

    1. 사용자가 회원가입 페이지에서 비밀번호를 입력한다.
    2. 서버는 비밀번호 원문을 그대로 저장하지 않고,
      • 먼저 암호학적 해시 함수(SHA-256, bcrypt, Argon2 등)를 적용한다.
      • 이때 나온 해시 값을 데이터베이스에 저장한다.

    이제 로그인 과정에서는 다음과 같은 일이 일어난다.

    1. 사용자가 로그인 화면에 아이디와 비밀번호를 입력한다.
    2. 서버는 입력된 비밀번호에 똑같은 해시 함수를 적용한다.
    3. 그 결과 나온 해시 값을, 데이터베이스에 저장된 해시 값과 비교한다.
    4. 두 해시 값이 같으면
      • “비밀번호가 동일하다”고 판단하고 로그인에 성공시킨다.
    5. 해시 값이 다르면
      • 비밀번호가 틀렸다고 판단한다.

    중요한 점은, 이 과정 어디에서도

    • 데이터베이스 안에 있는 값만으로는
    • 사용자의 원래 비밀번호를 알기 어렵도록 설계되어 있다는 점이다.

    해시 함수와 비밀번호 저장: 사이트가 비밀번호를 직접 보관하지 않는 이유
    비밀번호를 해시 함수로 변환해 데이터베이스에 저장하는 구조

    4. 해시만으로는 부족한 이유: 무차별 대입 공격과 레인보우 테이블

    그렇다면 해시 값만 저장하면 완전히 안전할까. 그렇지는 않다.

    공격자는 다음과 같은 방법을 사용할 수 있다.

    1. 무차별 대입 공격(브루트 포스, brute force)
      • 비밀번호 후보를 하나씩 만들어 보면서
      • 각각 해시 함수를 적용해 본다.
      • 공격자가 확보한 해시 값과 같은 값이 나오면
        • 그 비밀번호 후보가 실제 비밀번호일 가능성이 크다.
    2. 사전 공격(dictionary attack)
      • 사람들이 자주 쓰는 비밀번호 목록(예: 123456, password, qwerty)을 사전에 모은다.
      • 사전의 각 항목에 해시 함수를 적용한 값을 미리 계산해 놓는다.
      • 유출된 해시 값과 미리 계산해 둔 해시 값을 비교해 일치하는 비밀번호를 찾는다.
    3. 레인보우 테이블(rainbow table)
      • 특정 해시 함수에 대해
        • 흔히 사용되는 문자열들의 해시 값을 대량으로 미리 계산해 놓은 거대한 테이블이다.
      • 공격자는 이 테이블을 이용해 “해시 값 → 원래 비밀번호 후보”를 빠르게 역추론하려고 한다.

    이러한 공격에 특히 취약한 경우는

    • 비밀번호가 짧거나 단순할 때
    • 많은 사용자가 같은 비밀번호를 쓸 때
    • 해시 함수 적용만 하고 아무 추가 보호(솔트 등)를 하지 않을 때

    이다.

    그래서 비밀번호 저장 시에는 단순 해시 적용만으로는 부족하며,
    솔트(salt)와 같이 해시를 강화하는 기술이 함께 사용된다.


    솔트와 강화 해시, 실제 비밀번호 저장 구조

    1. 솔트(Salt)란 무엇인가: 해시에 섞는 추가 재료

    솔트(salt)는 말 그대로 비밀번호에 “소금을 조금 뿌려 섞는 것”에 비유할 수 있다.
    여기서 솔트는 비밀번호에 덧붙이는 추가 데이터이다.

    기본 개념은 다음과 같다.

    1. 사용자마다 무작위로 생성한 솔트 값을 하나 만든다.
    2. 비밀번호를 해시할 때,
      • “비밀번호 + 솔트”를 합쳐서 해시 함수에 넣는다.
    3. 데이터베이스에는
      • 솔트 값과
      • 그 솔트를 사용해 계산한 해시 값을 함께 저장한다.

    이렇게 하면 어떤 효과가 생길까.

    • 같은 비밀번호를 사용하더라도,
      • 솔트가 서로 다르면 해시 값이 완전히 달라진다.
    • 공격자가 레인보우 테이블을 만들어도
      • 솔트까지 고려해야 하므로,
      • 미리 만들어 둔 테이블을 그대로 쓰기 어려워진다.

    즉, 솔트의 핵심 목적은

    • 같은 비밀번호를 쓰는 여러 사용자의 해시 값이 서로 똑같이 보이는 문제를 막고,
    • 레인보우 테이블 기반 공격을 어렵게 만드는 것이다.

    2. 솔트를 이용한 로그인 검증 과정

    솔트를 사용하는 로그인 과정을 조금 더 구체적으로 살펴보면 다음과 같다.

    1. 회원가입 시
      • 서버는 사용자가 입력한 비밀번호를 받는다.
      • 무작위 솔트 값을 생성한다.
      • “비밀번호 + 솔트”를 합쳐 해시 함수를 적용한다.
      • 데이터베이스에는
        • 솔트 값과
        • “비밀번호 + 솔트”의 해시 값을 함께 저장한다.
    2. 로그인 시
      • 서버는 데이터베이스에서
        • 해당 사용자의 솔트와 해시 값을 가져온다.
      • 사용자가 입력한 비밀번호에
        • 저장된 솔트를 똑같이 붙여서 다시 해시 함수를 적용한다.
      • 계산 결과가 데이터베이스에 저장된 해시 값과 같다면
        • 비밀번호가 일치한다고 판단한다.

    중요한 점은, 설령 데이터베이스가 유출되더라도

    • 공격자는 “솔트 + 해시 값”만 보게 되며,
    • 여기에 대해 별도로 무차별 대입 공격을 해야 한다는 점이다.

    사용자별 솔트를 비밀번호에 결합해 해시를 계산하고, 로그인 시 동일한 솔트와 해시 과정을 거쳐 일치 여부를 확인하는 과정을 단계별로 나타낸 다이어그램
    솔트를 이용한 비밀번호 해시 저장과 로그인 검증 흐름

    3. 느리게 계산되는 해시: bcrypt, Argon2 같은 ‘강화 해시’

    일반 해시 함수(SHA-256 등)는 빠르게 계산되도록 설계되어 있다.
    하지만 비밀번호 해시에서는 일부러 느리게 만드는 것이 오히려 보안에 도움이 된다.

    이유는 다음과 같다.

    • 해커가 무차별 대입 공격을 시도할 때,
      • 비밀번호 후보 하나당 해시를 계산해야 한다.
    • 해시 계산이 느릴수록
      • 같은 시간에 시도할 수 있는 비밀번호 후보 개수가 줄어든다.

    그래서 비밀번호 저장에는 보통 bcrypt, scrypt, PBKDF2, Argon2처럼

    • “해시를 여러 번 반복 적용하거나, 메모리를 많이 쓰게 해서 일부러 느리게 만든 함수”를 사용한다.

    이런 함수들은 보통 다음과 같은 설정값을 가진다.

    • 반복 횟수 또는 비용(cost)
      • 값이 클수록 해시 계산이 더 느려지고,
      • 공격자가 시도할 수 있는 비밀번호 후보 수가 줄어든다.

    서비스는

    • 사용자 입장에서 로그인 속도가 너무 느려지지 않을 정도에서,
    • 가능한 한 높은 보안 수준을 유지하도록
    • 이 반복 횟수(또는 비용)를 적절히 설정한다.

    4. 사용자가 신경 써야 할 점: 강한 비밀번호와 재사용 금지

    해시, 솔트, 강화 해시 함수 등 서버 쪽 보안이 잘 되어 있다고 하더라도,
    사용자가 너무 단순한 비밀번호를 사용하면 여전히 위험하다.

    사용자가 신경 써야 할 실질적인 포인트는 다음과 같다.

    1. 비밀번호를 길고 복잡하게 만든다
      • 영어 대소문자, 숫자, 특수문자를 섞는 것이 좋다.
      • 길이가 길수록 무차별 대입 공격에 강해진다.
    2. 다른 사이트에서 같은 비밀번호를 재사용하지 않는다
      • 한 사이트에서 비밀번호가 유출되면,
        • 공격자는 같은 비밀번호를 다른 서비스에도 그대로 시도한다.
      • 주요 서비스(메일, 금융, 주요 업무 계정)는 반드시 서로 다른 비밀번호를 써야 한다.
    3. 가능하면 비밀번호 관리자를 활용한다
      • 여러 사이트에 긴 비밀번호를 각각 설정하는 것은 사람이 기억하기 어렵다.
      • 비밀번호 관리 프로그램을 사용하면
        • 각 사이트마다 다른 비밀번호를 자동으로 생성하고 저장할 수 있다.
    4. 2단계 인증(다중 인증)을 활용한다
      • 비밀번호가 유출되더라도
        • 추가 인증(문자, OTP, 인증앱 등)이 있어야 실제 로그인이 가능하도록 설정하면 보안이 크게 강화된다.

    해시와 솔트를 알면 “비밀번호 보안” 뉴스가 다르게 보인다

    이 글에서는 해시 함수와 비밀번호 저장 방식을 중심으로,
    사이트가 왜 비밀번호를 직접 보관하지 않는지 전산학 입문 수준에서 정리했다. 핵심 내용을 다시 요약하면 다음과 같다.

    1. 해시 함수(hash function)는
      • 임의 길이의 데이터를 고정 길이의 값으로 바꾸는 함수이며,
      • 단방향성, 입력 변화에 대한 민감성, 결정론적 특성을 가진다.
    2. 제대로 된 서비스라면
      • 비밀번호를 평문으로 저장하지 않고,
      • 해시 값만 데이터베이스에 저장한다.
    3. 단순 해시만으로는
      • 무차별 대입 공격, 사전 공격, 레인보우 테이블 공격에 취약할 수 있다.
    4. 솔트(salt)를 사용하면
      • 같은 비밀번호라도 서로 다른 해시 값을 만들 수 있어
      • 레인보우 테이블 공격을 어렵게 만들 수 있다.
    5. bcrypt, Argon2 등 강화된 비밀번호 해시 함수를 사용하면
      • 해시 계산을 의도적으로 느리게 하여
      • 공격자가 시도할 수 있는 비밀번호 후보 수를 제한할 수 있다.
    6. 사용자는
      • 길고 복잡한 비밀번호를 사용하고,
      • 사이트마다 비밀번호를 재사용하지 않으며,
      • 가능하면 비밀번호 관리자와 2단계 인증을 활용하는 것이 좋다.

    이제 “사이트는 내 비밀번호를 어떻게 보관하나”,
    “해시 함수와 솔트는 왜 필요한가”라는 질문에 대해

    • 비밀번호를 그대로 저장하지 않고,
    • 해시와 솔트, 강화 해시 함수를 조합해
    • 유출 사고가 나더라도 피해를 줄이도록 설계한다는 점을 설명할 수 있을 것이다.

    다음에 이어서 살펴볼 만한 주제로는 예를 들어 다음과 같은 것이 있다.

    • “세션과 쿠키: 로그인 상태가 브라우저에서 유지되는 원리”
    • “OAuth와 소셜 로그인: 여러 서비스에서 하나의 계정을 공유하는 인증 구조”

    이런 주제까지 함께 이해하면, 실제 웹 서비스에서 인증과 비밀번호 보안 전체 흐름을 보다 입체적으로 바라볼 수 있다.