Blog

솔트(Salt)와 페퍼(Pepper): 비밀번호 해시를 더 안전하게 만드는 첨가물

솔트와 페퍼, 왜 필요할까?

비밀번호를 데이터베이스에 그대로 저장하는 일은 이제 거의 사라졌습니다. 대신, 비밀번호는 해시 함수라는 일종의 변환기를 거쳐 알아볼 수 없는 문자열로 바뀌어 저장되죠. 그런데 해시 함수만으로는 생각보다 취약점이 있습니다. 만약 두 사용자가 같은 비밀번호를 사용한다면, 해시 값도 똑같이 나옵니다. 공격자는 미리 계산해 놓은 거대한 해시 값 목록인 ‘레인보우 테이블’을 이용해 이 값을 역추적할 수 있지요. 여기서 비밀번호 보안의 첫 번째 첨가물인 ‘솔트’가 등장합니다.

솔트는 각 사용자의 비밀번호에 추가되는 고유한 무작위 문자열입니다. ‘qwerty’라는 비밀번호에 ‘x7j9!k’라는 솔트를 붙여 ‘qwertyx7j9!k’를 해시하는 거죠, 이렇게 하면 같은 비밀번호라도 솔트가 다르면 전혀 다른 해시 값이 생성됩니다. 공격자는 각각의 솔트에 대해 새로운 레인보우 테이블을 만들어야 하므로 공격 비용이 기하급수적으로 늘어나게 됩니다.

솔트는 일반적으로 사용자 레코드와 함께 데이터베이스에 평문으로 저장됩니다. 그 자체는 비밀이라기보다는, 해시 과정을 유일하게 만들어주는 재료에 가깝습니다. 모든 요리가 소금 간을 필요로 하듯, 현대적인 비밀번호 저장에는 이 솔트가 필수적인 첫 번째 단계라고 볼 수 있습니다.

나무 테이블 위에 놓인 소금병과 후추 그라인더의 클로즈업으로, 광택 표면에 은은하게 비친 물음표가 보인다.

솔트만으로 충분하지 않은 이유

솔트는 레인보우 테이블 공격을 매우 효과적으로 막아냅니다. 하지만 데이터베이스 전체가 유출되는 최악의 상황을 가정해 봅시다. 공격자는 손에 넣은 솔트와 해시 값을 가지고 무차별 대입 공격을 시도할 수 있습니다. 컴퓨팅 파워가 강력해진 현대에는, 특히 짧고 단순한 비밀번호는 이런 공격에 비교적 쉽게 노출될 위험이 있습니다.

또 다른 시나리오도 있습니다. 만약 애플리케이션 코드 자체에 보안 취약점이 있어 해시 함수의 입력값을 조작할 수 있다면, 솔트가 노출된 상태에서의 공격 가능성은 더 커집니다. 이는 마치 문에 확실한 자물쇠를 채웠지만, 자물쇠를 조작하는 도구가 노출될 위험이 있는 상황과 비슷합니다. 여기서 우리는 두 번째 보안층, 즉 ‘페퍼’의 필요성을 고려하게 됩니다.

페퍼는 솔트와 개념은 비슷하지만 결정적인 차이가 있습니다. 그것은 데이터베이스에 저장되지 않는 비밀 값이라는 점입니다. 애플리케이션의 설정 파일이나 환경 변수 같은 별도의 안전한 곳에 보관되죠. 이 차이는 보안 계층을 하나 더 추가하는 효과를 만들어냅니다.

페퍼의 역할과 작동 방식

페퍼는 모든 사용자의 비밀번호 해시에 공통으로 적용되는 비밀 키입니다. 비밀번호 검증 과정은 이렇게 진행됩니다. 사용자가 입력한 비밀번호에 먼저 고유한 솔트를 붙이고, 그다음 보이지 않는 페퍼를 추가한 후, 이 전체를 해시 함수에 통과시킵니다. 그 결과를 데이터베이스에 저장된 해시 값과 비교하는 거죠.

페퍼의 핵심 가치는 데이터베이스가 유출되더라도 공격자가 완전한 해시 입력값을 재현할 수 없다는 데 있습니다. 공격자는 솔트는 알지만 페퍼는 모르기 때문입니다. 이는 공격자에게 또 하나의 큰 장벽이 됩니다. 무차별 대입 공격을 시도하려면 가능한 모든 페퍼 값을 추측해야 하는, 거의 불가능에 가까운 작업이 추가로 필요해집니다.

다만 페퍼는 관리상의 복잡성을 동반합니다. 페퍼를 분실하거나 변경하면 모든 사용자의 비밀번호가 일시에 무효화될 수 있습니다. 따라서 페퍼의 백업과 안전한 관리는 시스템 운영의 중요한 일부가 됩니다. 이는 보안과 운영 편의성 사이의 전형적인 절충 관계를 보여주는 사례입니다.

솔트 vs 페퍼: 명확한 차이점 정리

두 첨가물을 명확히 구분하는 것은 구현과 이해에 모두 중요합니다. 솔트는 사용자마다 다르고, 데이터베이스에 평문으로 저장되며, 주된 목적은 레인보우 테이블 공격을 무력화하는 것입니다. 반면 페퍼는 모든 사용자에게 동일하고, 데이터베이스 외부에 비밀로 저장되며, 주된 목적은 데이터베이스 유출 시 추가적인 보호 계층을 제공하는 것입니다.

이를 비유하자면, 솔트는 각 집마다 다른 문의 디자인을 만들어 도둑이 만능키를 쓰지 못하게 하는 것이라면, 페퍼는 모든 집 뒷마당에 또 하나의 비밀문을 숨겨두는 것과 같습니다. 도둑이 앞문의 설계도(솔트)를 알아도 비밀문의 위치(페퍼)를 모르면 안으로 들어갈 수 없죠.

현실에서의 구현을 보면, 솔트는 거의 모든 현대 시스템의 필수 요소입니다. 반면 페퍼는 더 높은 수준의 보안을 요구하는 특정 애플리케이션에서 선택적으로 적용되는 경우가 많습니다. 보안 요구사항과 운영 리스크를 저울질하여 도입 여부를 결정하게 됩니다.

넓은 하얀 탁자 위에 고립된 작은 소금병이 긴 그림자를 드리우며 부족함을 상징하는 모습이다.

실제 적용 시 고려할 점

솔트와 페퍼를 구현할 때는 기술적인 세부 사항에 주의를 기울여야 합니다. 먼저 솔트는 충분한 길이(현재 기준으로 최소 16바이트 이상)의 암호학적으로 안전한 난수 생성기로 만들어져야 합니다. 짧거나 예측 가능한 솔트는 그 의미를 상실하게 만들죠. 이 솔트는 해시 값과 함께 사용자 레코드에 명시적으로 저장됩니다.

페퍼를 사용하기로 결정했다면, 그 값을 안전하게 관리하는 전략이 필수입니다. 애플리케이션의 소스 코드에 하드코딩하는 것은 좋지 않은 방법입니다. 대신 환경 변수나 전용 비밀 관리 도구를 통해 주입받는 방식이 선호됩니다. 게다가, 페퍼를 주기적으로 변경하는 정책은 일반적으로 권장되지 않습니다. 변경 시 모든 사용자가 비밀번호를 재설정해야 하는 대규모 장애로 이어질 수 있기 때문입니다. 한편, 이러한 보안 관련 논의에서 자주 등장하는 전문 용어와 업계 특유의 표현을 이해하려면 훕스피크에 대해서도 알아두는 것이 도움이 됩니다.

해시 함수의 선택도 중요합니다. 단순한 SHA-256보다는 bcrypt, Argon2, PBKDF2와 같이 의도적으로 느리고 컴퓨팅 자원을 많이 소모하도록 설계된 ‘적응형 해시 함수’를 사용하는 것이 현대적인 표준입니다. 이러한 함수들은 무차별 대입 공격의 비용을 극적으로 높여줍니다. 솔트와 페퍼는 이런 강력한 해시 함수와 함께 사용될 때 그 진가를 발휘합니다.

보안 계층으로서의 통합적 이해

솔트와 페퍼는 단독으로 사용되는 것이 아니라 여러 보안 조치와 함께 통합되어 방어 체계를 구성합니다. 첫째, 강력한 비밀번호 정책으로 사용자가 예측하기 어려운 비밀번호를 생성하도록 유도합니다. 둘째, 적응형 해시 함수를 통해 해싱 자체를 느리게 만듭니다. 셋째, 솔트를 적용해 각 해시를 고유하게 만듭니다. 넷째, (선택적으로) 페퍼를 추가해 데이터베이스 유출 시의 리스크를 더욱 줄입니다.

이러한 다층 방어는 공격자가 모든 장벽을 동시에 뚫어야만 성공할 수 있도록 합니다. 한 단계를 우회하더라도 다음 단계에서 막히는 구조이죠. 커뮤니티나 개발자 포럼에서 보안 논의를 따라가다 보면, 이 ‘심층 방어’ 원칙이 어떻게 구체적인 기술로 구현되는지 자연스럽게 접하게 됩니다.

결국 최고의 보안은 사용자와 시스템 양쪽에서 이루어집니다. 시스템은 솔트, 페퍼, 강력한 해시 함수로 기술적 보호 장치를 만들고, 사용자는 길고 복잡한 비밀번호나 패스프레이즈를 생성함으로써 이 시스템이 효과를 발휘할 수 있는 기반을 제공합니다. 두 요소가 상호 보완적으로 작동할 때 비로소 안전한 인증 흐름이 완성됩니다. 마지막으로 단순 노출 효과: 사이트 배너를 자주 볼수록 호감도가 올라간다라는 원리를 보안 교육에 적용할 수 있습니다. 보안 수칙을 반복적으로 노출하면 사용자들은 자연스럽게 이를 친숙하게 받아들이고, 강력한 비밀번호 생성과 같은 좋은 습관을 더 쉽게 실천하게 됩니다.

마무리: 안전한 저장을 위한 조합

솔트는 현대 비밀번호 저장의 기준이 되었습니다. 사용자별 고유한 값을 추가함으로써 레인보우 테이블 공격을 근본적으로 봉쇄하죠. 반면 페퍼는 데이터베이스 유출이라는 극단적인 시나리오에 대비한 선택적 보험입니다. 애플리케이션 비밀 값으로서 외부에 보관되어, 유출된 데이터만으로는 공격을 완성시키지 못하게 하는 추가 장벽을 만듭니다.

이 두 ‘첨가물’을 이해하는 핵심은 그들이 해결하려는 문제가 다르다는 점입니다. 솔트는 ‘같은 비밀번호에서 나오는 같은 해시’ 문제를 해결하고, 페퍼는 ‘유출된 데이터베이스로의 무차별 대입’ 문제에 대한 추가 방어선을 제공합니다. 따라서 페퍼는 솔트를 대체하는 것이 아니라, 솔트 기반 시스템 위에 선택적으로 얹는 보안 강화층이라고 보는 것이 적절합니다.

최종적으로, 단순한 해시에서 솔트 추가, 그리고 페퍼 도입까지의 과정은 보안이란 고정된 상태가 아니라 위협에 대응해 진화하는 과정임을 보여줍니다. 개발자나 시스템 설계자는 자신의 서비스가 다루는 정보의 가치와 위험 수준을 평가해, 이 조합들 중 어떤 것이 가장 적합한지 판단하게 됩니다. 기본에 충실한 솔트와 강력한 해시 함수의 적용이 우선이고, 그 위에 필요한 만큼의 보안 층을 더해 나가는 것이 현실적인 접근법입니다.