🕵️ 친구와 암호 대화
한국어

암호에서 난수가 중요한 이유: 키, IV, 솔트

암호 이야기를 하다 보면 "무작위", "난수"라는 말이 계속 나옵니다. 키도 무작위로 만들고, IV도 무작위로 뽑고, 솔트도 무작위여야 한다고 합니다. 그런데 컴퓨터는 정해진 규칙대로만 움직이는 기계인데 무작위라는 것이 정말 가능할까요. 그리고 "그냥 아무 숫자나 쓰면 되지 않나" 하는 생각은 왜 위험할까요. 이 글에서는 난수가 왜 암호의 생명줄인지, 키·IV·솔트가 각각 무엇이며 어떻게 만들어야 하는지, 그리고 이 서비스가 그것을 어떻게 다루는지 차근차근 살펴봅니다.

예측할 수 있으면 암호가 아닙니다

현대 암호의 기본 원칙은 "알고리즘은 공개되어도 안전해야 하고, 비밀은 오직 키에 있어야 한다"는 것입니다. 그렇다면 키가 예측 가능하다면 어떻게 될까요. 공격자가 키를 직접 알지 못하더라도, 키가 만들어지는 방식을 추측할 수 있다면 모든 경우를 시도할 필요 없이 몇 가지 후보만 시험하면 됩니다.

예를 들어 "현재 시각"을 시드로 삼아 키를 만드는 프로그램을 생각해 보세요. 키 자체는 256비트짜리로 보이지만, 시드가 되는 시각이 "오늘 오후 3시 전후 몇 분"으로 좁혀진다면 가능한 키는 몇만 개 수준으로 줄어듭니다. 겉으로 보이는 길이와 실제 안전성이 전혀 다른 것입니다. 암호 분야에서 "키 길이"보다 "키가 얼마나 예측 불가능한가"가 더 중요하다는 말은 이 때문입니다. 키 길이가 가진 의미는 키 길이와 무차별 대입: 암호는 얼마나 버틸까에서 이어서 볼 수 있습니다.

컴퓨터는 어떻게 무작위 숫자를 만들까

컴퓨터의 일반적인 난수 함수는 "의사 난수 생성기"입니다. 시작값(시드)을 넣으면 그다음 숫자를 계산 규칙에 따라 줄줄이 만들어 내는 방식으로, 숫자 흐름은 무작위처럼 보이지만 같은 시드를 넣으면 똑같은 숫자가 똑같은 순서로 나옵니다. 게임에서 무작위 아이템을 뽑거나 주사위를 굴릴 때는 이 정도로 충분합니다. 하지만 암호에는 부족합니다. 앞의 몇 개 숫자를 관찰해 시드나 내부 상태를 알아낼 수 있다면, 뒤에 나올 숫자를 모두 예측할 수 있기 때문입니다.

그래서 암호용으로는 "암호학적으로 안전한 난수 생성기(CSPRNG)"를 씁니다. 운영체제가 키보드 입력 간격, 하드웨어 소음, 장치 타이밍 같은 예측하기 어려운 정보(엔트로피)를 모아 상태를 초기화하고, 공격자가 앞선 출력을 아무리 많이 보더라도 다음 출력을 예측할 수 없도록 설계된 방식입니다. 브라우저에서는 crypto.getRandomValues가 바로 이 역할을 합니다. 반면 자바스크립트의 Math.random은 암호용으로 설계된 함수가 아니므로 키나 IV를 만드는 데 써서는 안 됩니다. 이 차이는 브라우저 Web Crypto API로 암호화하기에서도 짚어 둔 부분입니다.

키, IV, 솔트: 비슷해 보이지만 역할이 다릅니다

세 가지 모두 "무작위 값"이지만, 비밀로 해야 하는지, 재사용해도 되는지는 서로 다릅니다. 헷갈리기 쉬운 부분이라 표 대신 하나씩 풀어 보겠습니다.

키: 비밀이어야 하고, 길고 무작위여야 합니다

키는 암호화와 복호화에 쓰이는 비밀 그 자체입니다. AES-256이라면 32바이트(256비트)이고, 이 값이 새거나 예측되면 모든 보호가 무너집니다. 그래서 반드시 CSPRNG로 뽑아야 하고, 사람이 임의로 "생각해 낸" 값을 쓰면 안 됩니다. 사람이 떠올린 "무작위한" 문자열은 생각보다 패턴이 많아서 컴퓨터가 뽑은 무작위 값보다 훨씬 약합니다.

IV(nonce): 비밀일 필요는 없지만, 같은 키로 중복되면 안 됩니다

IV는 같은 키로 여러 메시지를 암호화할 때 각 암호화를 서로 다르게 만들어 주는 값입니다. nonce는 "한 번만 쓰는 숫자(number used once)"라는 뜻의 줄임말입니다. IV는 암호문과 함께 공개적으로 보내도 되지만, 같은 키로 같은 IV를 두 번 쓰는 것은 치명적입니다. AES-GCM에서는 같은 키와 같은 IV로 두 메시지를 암호화하면 두 암호문을 비교해 원문 사이의 관계를 드러낼 수 있고, 위변조 검사를 무력화할 단서까지 줄 수 있습니다. 구체적인 구조는 AES-GCM 쉽게 풀어쓰기: 왜 요즘 암호화는 이걸 쓸까에서 설명했습니다.

솔트: 비밀번호에서 키를 만들 때 쓰는 "양념"

솔트는 비밀번호를 키로 바꾸는 과정에 섞어 넣는 무작위 값입니다. 역시 비밀일 필요는 없습니다. 솔트의 목적은 같은 비밀번호라도 사용할 때마다 전혀 다른 결과가 나오게 하여, 공격자가 미리 계산해 둔 표(레인보우 테이블 등)를 쓰지 못하게 하고, 여러 사람의 비밀번호를 한꺼번에 공격하기 어렵게 만드는 것입니다. 이 과정은 비밀번호에서 키를 만드는 PBKDF2와 키 유도에서 다룹니다.

정리하면 세 값의 차이는 다음과 같습니다.

"무작위 12바이트면 겹치지 않을까?"에 대한 답

AES-GCM은 12바이트(96비트) IV가 표준적으로 쓰입니다. 12바이트로 만들 수 있는 값의 가짓수는 약 7.9 곱하기 10의 28제곱으로, 무작위로 하나를 뽑을 때 우연히 겹칠 확률은 매우 낮습니다. 하지만 "아주 낮다"가 "영원히 없다"는 뜻은 아닙니다. 한 키로 암호화하는 메시지 수가 늘수록 어느 두 개가 겹칠 확률도 조금씩 커집니다. 이는 생일 문제와 같은 원리로, 30명만 모여도 생일이 같은 사람이 있을 확률이 높아지는 현상과 닮았습니다.

그래서 NIST의 권고는 무작위 IV를 쓸 경우 한 키로 암호화하는 횟수에 상한(약 2의 32제곱 번, 즉 43억 번 안팎)을 두라는 것입니다. 개인 간 메시지처럼 한 키로 몇 건에서 몇십 건을 암호화하는 규모라면 이 한도에 비해 터무니없이 작아서 걱정할 필요가 없습니다. 반대로 한 키로 수십억 건을 처리하는 서버라면 키를 주기적으로 교체하거나 카운터 방식으로 IV를 관리해야 합니다. 상황에 맞춰 방식을 고른다는 점이 핵심입니다.

실제로 벌어진 난수 실수 유형

구체적인 사건 하나하나를 지어내어 말하기보다, 알려진 실수의 유형을 정리해 보겠습니다. 모두 암호 공학에서 반복적으로 지적되어 온 패턴입니다.

  1. 시드를 시각이나 프로세스 번호처럼 좁은 값으로 정하는 경우: 앞서 설명한 것처럼 가능한 경우의 수가 급격히 줄어듭니다.
  2. 난수 함수를 잘못 고른 경우: 게임용 난수 함수로 키나 토큰을 만들면 앞뒤 값을 예측당할 수 있습니다.
  3. IV를 고정값으로 두거나 매번 0부터 시작하는 경우: "어차피 공개해도 되는 값이니까"라며 같은 값을 쓰는 실수입니다. 공개 가능과 재사용 가능은 전혀 다른 문제입니다.
  4. 복제된 가상 머신이나 초기 부팅 직후의 환경에서 엔트로피가 부족한 경우: 난수 생성기가 충분히 섞이기 전에 값을 뽑으면 서로 다른 기기에서 같은 값이 나올 수 있습니다.
  5. "무작위처럼 보이는 문자열"을 직접 만든 경우: 사람이 만든 임의 문자열은 키보드 배열, 좋아하는 숫자 같은 편향을 갖습니다.

이 서비스는 난수를 어떻게 쓰나요

이 서비스는 브라우저의 crypto.getRandomValues를 사용합니다. 글을 암호화할 때마다 새로운 12바이트 IV를 이 함수로 뽑으므로, 같은 문장을 같은 번역기로 두 번 암호화해도 결과 비밀어는 매번 달라집니다. 이는 편의 기능이 아니라 보안상 필수 장치입니다. 같은 키로 만든 비밀어 두 개가 겹치는 IV를 갖는 일을 피하기 위한 구조이기 때문입니다.

비밀번호를 쓰는 경우에는 비밀번호에 솔트를 섞어 PBKDF2로 반복 연산한 결과가 키 재료로 쓰입니다. 또 하나 눈여겨볼 점은 추천 비밀번호를 만들 때의 처리입니다. 단어 목록에서 세 개를 골라 연결하는 방식인데, 목록에서 인덱스를 뽑을 때 단순히 "난수를 목록 길이로 나눈 나머지"를 쓰면 일부 단어가 조금 더 자주 뽑히는 치우침이 생깁니다. 이를 피하기 위해 범위를 넘어가는 값은 버리고 다시 뽑는 방식(거부 표본추출)을 사용해 모든 단어가 같은 확률로 뽑히도록 했습니다. 작은 차이 같지만, 예측 가능성을 줄이려는 노력은 이런 세부에서 갈립니다. 비밀번호를 직접 만드는 방법은 강한 비밀번호와 암호문을 만드는 법에서 볼 수 있고, 실제 동작은 번역기 열기에서 같은 문장을 두 번 암호화해 보며 확인하실 수 있습니다.

내가 직접 지킬 수 있는 원칙들

일반 사용자나 개발을 시작한 분이 기억해 두면 좋은 원칙은 많지 않습니다.

난수는 암호의 눈에 잘 띄지 않는 토대입니다. 아무리 튼튼한 알고리즘을 써도 키가 예측되거나 IV가 겹치면 보호는 무너집니다. 반대로 말하면, 난수를 제대로 쓰는 것만으로도 많은 실수를 막을 수 있습니다. 앞으로 "AES-256으로 암호화합니다" 같은 문구를 보시면, 그 뒤에 숨은 질문 하나를 같이 떠올려 보세요. "그 키는 어디서, 어떻게 만들어졌을까요?" 그 답이 신뢰할 만한 난수라면 비로소 알고리즘의 강점이 의미를 갖습니다.

잘못된 내용이나 보완할 점을 발견하셨다면 소개·문의의 이메일로 알려 주세요. 확인 후 글을 고치고 수정일을 갱신합니다.

함께 읽으면 좋은 글

다음 글: 키 길이와 무차별 대입: 암호는 얼마나 버틸까 → 번역기 열기