# password.es에서 무엇이 잘못됐었고, 이제는 아닌지

> 하루 종일 어떻게 해야 하는지 말하는 사이트라면, 스스로 그렇게 하지 않았던 때도 이야기해야 한다. 우리 집에서 찾아낸 것들을 숫자와 함께 적는다.

2026-08-31 · David Carrero · password.es
Original: https://password.es/ko/blog/password-es-eseo-mueosi-jalmotdoeeossna/

---

이 블로그에는 어떻게 해야 하는지 설명하는 글이 열두 편 있다. 길이가 기호보다 더
중요하다는 것, 관리자는 편의가 아니라 그 규칙을 지킬 수 있는 유일한 방법이라는 것,
백분율은 아무것도 말해주지 않는다는 것.

그렇게 쓰기는 쉽다. 불편해지는 건 같은 잣대를 자기 사이트에 들이댈 때다. **들이대
봤더니 세 가지가 잘못돼 있었다.** 그것들을 들통나게 한 숫자와 함께 통째로 여기 적
는다. 자기 성공만 발표하는 보안 사이트는 값어치가 꽤 떨어지기 때문이다.

## 검사기는 입력하는 동안 얼어붙었다

검사기는 [zxcvbn](https://github.com/dropbox/zxcvbn)으로 비밀번호를 분석한다. 대문
자를 세는 대신 패턴을 찾는 라이브러리다. 아주 잘 작동하고, 대가가 있다. **한 번에
전부, 페이지를 그리는 것과 같은 스레드에서 돈다.** 도는 동안 브라우저는 다른 아무
것도 하지 못한다.

그 대가는 길이에 따라 빠르게 커진다. 사이트가 내려주는 것과 같은 번들로, 사람들이
실제로 치는 종류의 비밀번호—단어, 연도, `a` 대신 `@`—로 측정했다.

| 길이 | 한 번 실행 |
|---|---|
| 16자 | 45 ms |
| 20자 | 121 ms |
| 32자 | 273 ms |
| 64자 | 843 ms |
| 128자 | 3,621 ms |

여기까지는 정상이다. 문제는 그걸 몇 번 실행했느냐였다.

입력란에는 `input`과 `keyup`이라는 **두 개**의 서로 다른 감시자가 붙어 있었다. 보통
키는 둘 다 발생시킨다. 그래서 글자 하나를 누를 때마다 전체 분석이 **두 번** 돌았다.
16자 비밀번호에서 90 ms, 32자에서는 0.5초.

게다가 막는 것이 아무것도 없었다. 타이머도, 길이 제한도, 별도 스레드도. 스무 자쯤을
넘어서면 브라우저가 분석하는 시간이 당신이 다음 키를 누르는 시간보다 길어졌다. 입력
이 다시는 비워지지 않는 대기열에 쌓였고, **입력하는 동안 페이지가 응답하지 않게 됐
다**. 긴 비밀번호를 붙여넣으면 몇 초씩 얼어붙었다.

수정은 작은 변경 두 가지다. `keyup` 감시자를 떼어냈다. `input`만으로 타이핑, 붙여
넣기, 삭제, 관리자의 자동 채우기가 모두 잡히기 때문이다. 보이는 것은 하나도 바뀌지
않은 채 작업의 절반이 사라진다. 그리고 150밀리초의 대기를 넣었다. 치는 동안에는 아
무것도 분석하지 않고, 멈추는 순간 분석한다. 그걸 알아챌 만큼 느리게 치는 사람은 없
다.

결과는 똑같다. `Barcelona1992$`는 여전히 같은 말을 한다. 「barcelona」는 사전에 있
고, 1992는 연도처럼 보이며, 1초도 안 돼 무너진다고.

## 설명문이 열다섯 개 언어에서 문장 중간에 잘려 있었다

이쪽이 더 어이없고 더 창피하다.

검색 결과에서 제목 아래 나오는 문장—메타 설명—이 검사기의 **열여덟 개 언어 중 열다
섯 개에서 잘려 있었다**. 살아남은 건 스페인어, 한국어, 일본어뿐. 누군가 어느 시점에
길이를 이유로 잘랐고, 단어 중간에서 끊었고, 그 뒤로 아무도 보지 않았다.

세 언어에서는 자른 자리가 특수 문자 한가운데에 떨어졌다. 그러니까 구글에 내보내던
것은 이랬다.

| 언어 | 우리가 내보내던 것 | 원래 문장 |
|---|---|---|
| 포르투갈어 | `…datas e padr&o` | `padrões` |
| 터키어 | `…arıyor ve ger&cced` | `gerçekten` |
| 베트남어 | `…chúng tôi tra mật kh&#` | `mật khẩu` |

눈에 띄는 건 위치다. **사이트 전체에서 가장 잘 작동하는 페이지**였다. 검사기는 생성
기보다 훨씬 잘 전환되는데, 깨진 설명문을 달고 그러고 있었다. 열다섯 개 모두 채웠다.

겸사겸사 한국어·일본어·베트남어에서는 브랜드를 제목 앞에서 뒤로 옮겼다. 검색 결과에
서는 도메인이 따로 보이니, 「password.es ·」로 시작하는 건 가장 값나가는 자리를 낭
비하는 것일 뿐이었다.

## 1억분의 1의 편향

생성기는 각 문자를 브라우저의 좋은 난수원인 `crypto.getRandomValues()`로 고른다. 그
런데 그 거대한 수를 「알파벳의 한 위치」로 바꾸면서 나머지 연산을 썼고, 거기에 고전
적인 함정이 있다. 2³²은 83의 배수가 아니므로 **알파벳의 앞쪽 위치가 뒤쪽보다 아주
조금 더 자주 나왔다**.

얼마나 조금인가. 최대 89자 알파벳에서 1억분의 1 정도다. 아무것도 바꾸지 않는다. 당신
의 비밀번호를 눈에 띌 만큼 약하게 만들지 않는다. 근처에도 못 간다.

그래도 고쳤다. 세 줄이다. 가능한 4,294,967,296개 값 가운데 남는 45개를 버리고 다시
뽑는다. 비밀번호를 다루는 사이트에서, 없애는 비용이 그 정도인데 「무시할 만하다」는
편향을 남겨둘 좋은 이유가 되지 못한다.

## 빠져 있던 것—약속을 브라우저가 지키게 하는 일

[당신의 비밀번호는 여기서 나가지 않는다](/ko/blog/wae-bimilbeonhoreul-eodiedo-bonaeji-anhneunga/)
고 설명하는 글이 통째로 하나 있다. 사실이었지만, 그래도 우리 쪽의 문장 하나였을 뿐
이다. 주입된 스크립트가 다른 짓을 하는 걸 기술적으로 막는 건 없었다.

이제 모든 페이지가 콘텐츠 보안 정책을 싣고 있다. 중요한 두 부분:

- **빌드할 때 페이지에 없던 스크립트는 실행되지 않는다.** 정당한 스크립트는 각각 지
  문으로 서명돼 있다. 주입된 것은 일치하지 않고 브라우저가 실행을 거부한다.
- **`connect-src 'none'`.** 이 사이트는 자바스크립트에서 단 한 번의 네트워크 요청도
  하지 않으며, 이제 그것을 브라우저가 강제한다. 언젠가 무언가가 당신이 친 것을 어딘
  가로 보내려 해도 나가지 못한다.

약속은 선언이기를 그만두고 규칙이 되었다.

## 여전히 잘못된 것들

**8월 31일 갱신.** 이 절의 세 가지는 이 글이 올라간 바로 그날 고쳤습니다. 열다섯 개 언어는 이제 진짜 엔진을 설명하고, 두 척도는 하나가 되었으며, 검사기의 자주 묻는 질문이 기호 문제를 설명합니다. 그래도 적어 둔 채로 둡니다. 무엇이 잘못됐었는지의 기록이, 흠 없는 사이트의 사진보다 값어치가 있기 때문입니다.

이 글이 제자리를 얻는 대목이 여기다.

**열다섯 개 언어가 아직도 검사기를 옛 엔진으로 설명한다.** 그 페이지들의 문장은
PasswordMeter의 고전적 로직을 재현한다고 말한다. 이제 아니다. 그 엔진은 `Password1!`
을 「매우 강함」이라 하고 다섯 단어짜리 문장을 떨어뜨렸기 때문에 교체됐다. 해야 할
말의 정확히 반대다. 스페인어·한국어·일본어는 갱신됐고, 나머지 열다섯은 아니다.

**같은 집에 두 개의 척도가 있다.** 첫 페이지는 비트로 강도를 정하고, 검사기는 깨는
데 걸리는 시간으로 정한다. 서로 다른 기준인데 어디에도 알리지 않는다.

**생성기와 검사기가 같은 기호를 세지 않는다.** 하나는 27개를 쓰고, 다른 하나는 33개
를 가정한다. 차이는 작지만 있다.

셋 다 심각하지는 않다. 셋 다 부채이고, 다른 사람이 찾아내기를 기다리느니 여기 적혀
있는 편을 택한다.

## 이번 버전의 변경 기록

저장소가 공개가 아니므로 변경 기록을 여기에 통째로 적는다. **0.2.0**, 2026년 8월
31일.

**추가.** 페이지마다의 콘텐츠 보안 정책, 각 스크립트의 지문과 함께. 저장된 비밀번호
가 어디에 있는지에 대한 글, 열여덟 개 언어로. 무엇을 쓸지 정하고, 비면 아무것도 발
행하지 말라고 명하는 편집 대기열.

**수정.** 검사기의 설명문. 열다섯 개 언어에서 문장 중간에 잘렸고 그중 셋은 문자가
반으로 쪼개져 있었다. 입력 중 탭이 얼어붙던 문제. 추첨의 편향. 그리고 앞 글에서 빠
져 있던 사진.

**변경.** 한국어·일본어·베트남어에서의 검사기 제목. 배포는 이제 빌드된 사이트가 이
미 존재할 때 발동한다. 1분 전이 아니라. 그리고 베트남어·힌디어·아랍어 번역은 여전
히 가장 약하지만 발행을 막지는 않게 되었다.

---

*이 글의 숫자는 추정이 아니라 실측이다. zxcvbn의 시간은 사이트가 내려주는 것과 같은
번들과 단어·치환이 들어간 비밀번호로 측정했고, 추첨의 편향은 32비트 정수의
4,294,967,296개 값 위에서 계산했다. 검사기의 엔진은 오픈 소스
[zxcvbn](https://github.com/dropbox/zxcvbn)이다.*
