보안 · 2026년 9월 9일

개발자를 위한 비밀번호 보안 — 무엇을 저장하고, 무엇을 해시하고, 무엇을 절대 하지 말아야 하는지

데이터베이스의 평문 비밀번호는 발생을 기다리는 침해입니다. 현대적인 비밀번호 저장이 어떻게 생겼는지, 'SHA-256으로 해시하기만 하면 된다'가 왜 충분하지 않은지, 대신 무엇을 해야 하는지 설명합니다.

모든 데이터 침해 공개는 같은 곡선을 따릅니다: “우리는 보안을 진지하게 받아들입니다.” 그런 다음 세부 사항을 읽고 비밀번호가 평문으로 저장되어 있거나, MD5로 해시되어 있거나, 동일한 데이터베이스에 있던 키로 암호화되어 있음을 발견합니다. 비밀번호 저장 문제가 해결되었습니다 — 알고리즘이 존재하고, 라이브러리가 존재하고, 문서가 존재하는데, 개발자들은 여전히 잘못하고 있습니다.

이 게시물은 무엇을 저장해야 하는지, 무엇을 저장하면 안 되는지, 해시와 암호화의 차이, 그리고 SHA-256(password)가 비밀번호 해시가 아닌 이유를 다룹니다.

규칙: 절대 평문을 저장하지 마세요

데이터베이스가 손상되고 평문 비밀번호가 있는 경우, 다른 사이트에서 해당 비밀번호를 재사용하는 모든 사용자가 이제 손상됩니다. 귀하의 사이트뿐만 아니라 — 그들의 이메일, 은행, 소셜 미디어. 평문 비밀번호 저장은 보안 문제가 아닙니다. 도덕적 문제입니다.

해시를 저장하세요. 항상.

해시 vs 암호화

해시는 일방향 변환입니다. 되돌릴 수 없습니다. hashpassword가 주어지면 password를 복구할 수 없습니다. 확인만 할 수 있습니다: 입력을 해시하고 비교합니다.

암호화는 양방향 변환입니다. encrypt(password, key)가 주어지면 키로 password를 복구할 수 있습니다. 키가 손상되면 모든 비밀번호가 손상됩니다.

비밀번호를 해시하세요. 절대 암호화하지 마세요. 평문을 복구해야 하는 경우(필요하지 않습니다), 잘못된 문제를 해결하고 있습니다.

SHA-256이 충분하지 않은 이유

일반적인 실수: hash = SHA-256(password). 빠르고, 잘 알려져 있고, 비밀번호에는 잘못되었습니다.

문제는 속도입니다. 최신 GPU에서 SHA-256은 초당 수십억 개의 해시를 처리합니다. SHA-256 비밀번호 해시 데이터베이스를 가진 공격자는 몇 분 만에 모든 일반 비밀번호, 모든 사전 단어, 모든 순열을 시도할 수 있습니다.

비밀번호 해시 알고리즘은 의도적으로 느립니다. 무차별 대입 공격을 실용하지 않게 만들기 위해 키 스트레칭 — 해시 함수의 수천 번 반복 — 을 사용합니다:

  • bcrypt — 1999년 이후 표준. 조정 가능한 비용 계수. 비밀번호 + 소금 → 해시. 모든 주요 언어에 라이브러리가 있습니다.
  • scrypt — 메모리 하드. 계산에 대량의 RAM을 필요로 하여 GPU 공격을 더 어렵게 만듭니다.
  • Argon2 — 비밀번호 해시 경쟁(2015) 승자. 메모리 하드, 구성 가능한 시간 및 메모리 비용. 현재 모범 사례.
  • PBKDF2 — NIST 권장, 널리 사용 가능, HMAC 반복 사용. Argon2만큼 메모리 하드하지는 않지만 잘 테스트됨.

bcrypt, scrypt 또는 Argon2를 사용하세요. 고유한 스키마를 발명하지 마세요. SHA-256으로 한 번 해시하고 끝이라고 하지 마세요.

소금: 왜 모든 비밀번호에 고유한 것이 필요한지

소금은 해시 전에 비밀번호에 추가되는 무작위 데이터입니다. 소금이 없으면 같은 비밀번호를 가진 두 사용자는 같은 해시를 생성합니다. 공격자는 일반 비밀번호의 해시를 사전 계산(레이니어 테이블)하고 데이터베이스와 비교할 수 있습니다.

사용자별 고유 소금이 있으면 비밀번호가 같아도 해시가 다릅니다. 레이니어 테이블은 작동하지 않습니다. 공격자는 각 비밀번호를 개별적으로 무차별 대입해야 합니다.

최신 비밀번호 해시 함수(bcrypt, Argon2)는 자동으로 무작위 소금을 생성합니다. 소금을 개별로 관리할 필요가 없습니다. 비밀번호를 해시 함수에 전달하면 나머지를 처리합니다.

무엇을 저장해야 하는지

각 사용자에 대해 다음을 저장합니다:

  • 비밀번호 해시 (bcrypt/Argon2 출력, 소금과 비용 매개변수 포함)
  • 해시 알고리즘 (bcrypt, argon2 등)
  • 비용 매개변수 (bcrypt 라운드, Argon2 메모리/시간)

저장하지 마세요:

  • 평문 비밀번호
  • 소금 별도 (해시 함수 출력에 포함됨)
  • 비밀번호를 복호화할 수 있는 “암호화 키”
  • 비밀번호 힌트 (정보를 유출함)

bcrypt의 해시 출력은 다음과 같습니다: $2b$12$LJ3m4ys3GzF4VQ.YOUR.HASH.HERE. $2b$는 알고리즘을 식별하고, 12는 비용 계수이며, 나머지는 소금 + 해시입니다. 로그인 시도를 확인하려면 동일한 매개변수로 제출된 비밀번호를 해시하고 비교합니다.

비용 계수

bcrypt의 비용 계수는 해시 속도를 결정합니다. 각 증가는 계산 시간을 두 배로 늘립니다:

  • 비용 10: 해시당 ~10ms (사용자에게는 빠르고, 공격자에게도 빠름)
  • 비용 12: 해시당 ~40ms (합리적)
  • 비용 14: 해시당 ~160ms (공격자에게는 느리고, 사용자에게는 눈에 띔)
  • 비용 16: 해시당 ~640ms (대부분의 로그인 흐름에는 너무 느림)

2026년 최적 지점: bcrypt 비용 12-14, 64MB+ 메모리와 3+ 반복을 가진 Argon2. 목표는 각 해시 시도를 느리게 하여 수십억 비밀번호 무차별 대입에 수년이 걸리지만, 사용자가 로그인 시 지연을 느끼지 않을 정도로 빠르게 하는 것입니다.

속도 제한

해시만으로는 충분하지 않습니다. 공격자가 로그인 엔드포인트에 초당 수백만 비밀번호를 시도할 수 있다면 bcrypt도 구제하지 못합니다.

로그인 시도를 제한하세요. 동일한 IP 또는 계정에서 5-10회 실패 후 계정을 일시적으로 잠그거나 CAPTCHA를 요구하세요. 이렇게 하면 무차별 대입 공격을 최소한으로 제한합니다.

사용자명 또는 비밀번호가 잘못되었는지 공개하지 마세요. “잘못된 사용자명 또는 비밀번호”는 “해당 이메일로 된 계정이 없습니다”보다 안전합니다. 후자는 공격자에게 어떤 사용자명이 존재하는지 알려줍니다.

지속적인 실패 후 계정 잠금 추가 10-20회 실패 후 15분 동안 계정을 잠그거나 잠금 해제에 이메일 확인을 요구하세요.

비밀번호 생성

비밀번호를 생성할 때(사용자, API 키, 토큰용):

  • 최소 12자 최신 하드웨어에 대해 8자는 충분하지 않습니다.
  • 대문자, 소문자, 숫자, 기호 포함 하지만 길이가 복잡성보다 중요합니다.
  • CSPRNG 사용 (암호학적으로 안전한 의사 난수 생성기). Math.random()은 안전하지 않습니다.
  • 예측 가능한 패턴을 절대 생성하지 마세요 Password1!은 복잡해 보이지만 쉽게 추측할 수 있습니다.

최고의 비밀번호는 CSPRNG에서 생성된 무작위 문자열입니다. 비밀번호 관리자를 사용하여 저장하세요. 사용자에게 복잡한 비밀번호를 기억하게 하지 마세요 — 생성된 비밀번호를 제공하고 관리자가 처리하게 하세요.

일반적인 실수

  • SHA-256 또는 MD5로 한 번 해시 — 너무 빠르고, 소금 없음, 쉽게 무차별 대입 가능.
  • 정적 소금 사용 — 모든 사용자가 같은 소금을 공유하면 레이니어 테이블이 작동함.
  • 소금 별도 저장 — 소금이 다른 테이블에 있으면 데이터베이스를 손상시킨 공격자가 둘 다 얻음.
  • 해시 대신 암호화 — 비밀번호를 복호화할 수 있으면 키를 가진 공격자도 가능.
  • 속도 제한 없음 — bcrypt도 무제한 로그인 시도에 취약.
  • 사용자명 존재 공개 — 공격자가 유효한 계정을 열거하는 데 도움.
  • 비밀번호 생성에 Math.random() 사용 — 예측 가능하고, 암호학적으로 안전하지 않음.

빠른 참조 | 하세요 | 하지 마세요 | |——|–––––| | bcrypt, Argon2 또는 scrypt 사용 | SHA-256 또는 MD5 사용 | | 라이브러리가 소금 처리를 하게 함 | 소금을 직접 관리 | | 로그인 시도를 제한 | 무제한 비밀번호 시도 허용 | | CSPRNG로 비밀번호 생성 | Math.random() 사용 | | 해시만 저장 | 평문 또는 암호화된 비밀번호 저장 | | “잘못된 자격 증명”이라고 말 | “잘못된 비밀번호”라고 말 |

시도해보기

안전한 무작위 비밀번호를 생성하거나 테스트용 문자열을 해시해야 하는 경우 브라우저 기반 도구를 사용하여 작업이 로컬에서 이루어지도록 하세요. 비밀번호는 간단한 생성 작업을 위해 서버를 만져서는 안 됩니다.