Created: Updated:

로그인 페이지에서 패스워드를 input 박스에 입력했을 때 브라우저의 메모리 덤프를 확인하면 입력한 패스워드가 평문으로 노출된다. 로그인을 하고 나면 메모리에 남지 않지만 로그인하기 전 input 박스에 입력한 상태에서는 메모리에 남는다. 어떻게 하면 로그인 버튼을 누르기 전에도 패스워드를 입력할 때 메모리에 패스워드를 평문으로 노출되지 않도록 할 수 있을까?

input 박스의 type을 password로 하여도 브라우저에서는 *표시로 확인할 수 없지만 브라우저 메모리 덤프를 확인하면 패스워드가 평문으로 노출되어 있다.

<form>
  <label for="password">패스워드:</label>
  <input type="password" id="password" name="password">
  <input type="submit" value="제출">
</form>

1. 과정

1) 첫 번째 시도

배열로 저장할 경우 stack 영역이 아닌 heap 영역에 데이터가 들어가기 때문에 브라우저 메모리 덤프에서 확인할 수 없지 않을까 하여 input 박스에 데이터를 평문으로 넣지 않고 배열을 통해서 받도록 해보았다.

const passwords = []

document.getElementById("passwordForm").addEventListener("keydown", function(event) {
  const key = event.key
  const allowedKeysPattern = /^[a-zA-Z0-9!@#$%^&*()_+\-=[\]{}|\\:;"'<>,.?/~`]+$/

  if (key === "Backspace") {
    passwords.pop()
  } else if (allowedKeysPattern.test(key)) {
    passwords.push(key)
  } else {
    event.preventDefault()
  }

  document.getElementById("password").value = "*".repeat(passwords.length)
})

패스워드를 배열에 담아야 하므로 복사, 붙여넣기, 방향키 등 처리하기 힘든 부분은 막고 패스워드에 입력할 수 있는 키보드 입력만 받았다. 하지만 메모리 덤프를 확인하면 배열에 있는 패스워드가 평문으로 노출되었다.

2) 두 번째 시도

브라우저 메모리 덤프에 stack 영역과 heap 영역이 모두 확인할 수 있다고 한다면 배열로 저장할 때 평문으로 확인할 수 없도록 Base64 인코딩해서 배열에 저장해 보았다.

const passwords = []

document.getElementById("passwordForm").addEventListener("keydown", function(event) {
  const key = event.key
  const allowedKeysPattern = /^[a-zA-Z0-9!@#$%^&*()_+\-=[\]{}|\\:;"'<>,.?/~`]+$/

  if (key === "Backspace") {
    passwords.pop()
  } else if (allowedKeysPattern.test(key)) {
	  passwords.push(btoa(passwordValue))
  } else {
    event.preventDefault()
  }

  document.getElementById("password").value = "*".repeat(passwords.length)
})

하지만 메모리 덤프를 확인하면 배열에 있는 패스워드가 평문으로 노출되었다. 뿐만 아니라 암호화를 하던 다른 문자로 변환하여도 메모리 덤프를 확인하면 여전히 패스워드가 평문으로 노출되었다.

심지어 input 박스에 입력을 받지 않더라도 키보드로 패스워드를 입력하면 메모리 덤프에 패스워드가 평문으로 노출되었다.

3. 의문점

stack 영역과 heap 영역 어디에도 평문으로 데이터를 넣지 않았는데도 브라우저의 메모리 덤프를 확인하면 평문으로 노출되었다.

여러가지 테스트 결과 키보드를 누르기만 하면 해당 데이터가 메모리 덤프에 평문으로 노출되는 것을 알았다.

4. 원인

input 박스의 키보드 이벤트를 막을지라도, 키보드 입력은 운영 체제에서 처리되기 때문에, 운영 체제에서 키보드 입력을 받아들이고 있기 때문에 브라우저 메모리 덤프에서도 키보드로 누른 문자가 확인된다.

키보드 입력이 브라우저의 메모리 덤프에 남는 과정은 다음과 같다.

  1. 운영 체제가 키보드 입력을 받는다.
  2. 운영 체제가 키보드 입력에 대한 정보를 브라우저에 전달한다.
  3. 브라우저가 키보드 입력을 받아 그 값을 메모리 덤프에 저장한다.

따라서 브라우저에서 키보드 입력이 발생하면 그 값이 기록된 메모리 덤프에서 확인될 수 있다. 키보드 보안 관련 프로그램 설치하는 이유를 깨달을 수 있었다. 키보드 입력을 하는 순간 입력한 정보가 브라우저 메모리에 남기 때문이다. 그냥 브라우저를 키고 input 박스가 아닌 아무 곳에서나 키보드 입력 후 메모리 덤프를 확인해 보면 알 수 있다.

5. 해결

결국 임시방안으로 input 박스에 패스워드를 키보드로 입력받지 않는 방법밖에 없다. 브라우저에서는 키보드 입력을 통해 메모리에 남는 것을 막을 수 없고 운영 체제에서 막아야 하므로 완벽하게 보안 측면에서 해결하는 방법은 없다. 최대한 해결하기 위해 키보드 입력을 막고 사용자가 키보드 입력을 하지 않고 가상 키보드를 사용해서 패스워드를 입력한다는 가정하에 해결하였다.

document.getElementById("password").addEventListener("keydown", function(event) {
  event.preventDefault()
  alert('가상 키보드를 사용하세요')
})

simple-keyboard 라이브러리를 사용해서 입력 시 Base64로 인코딩하여 배열에 넣고 로그인 시 배열을 넘겨 서버에서 배열에 있는 데이터를 디코딩 후 조합하여 패스워드를 확인하도록 했다.

댓글남기기