본문 바로가기
Dev/Vue.js

[Vue] 웹뷰에서 중요정보 노출 방지 (+ 메모리 덤프 확인하는 법)

by YummYum 2024. 12. 14.

 

 

앱 보안 취약점 점검을 했을 때 [메모리 내 중요정보 노출 방지] 항목이 있습니다. 해당 항목은 중요 정보를 입력 또는 처리 과정 등에서 메모리 내 평문 노출 여부를 점검하는 것입니다. 이런 문제가 일어나지 않기 위해서는 불필요하게 중요 정보를 노출하지 않거나 입력 또는 과정에서 즉시 암호화가 필요합니다. 여기서 중요 정보는 주민등록번호, 운전면허번호, 핀번호(간편 결제번호), 비밀번호 등이 있습니다.

 

웹 보안 취약점 점검에서도 동일하게 웹 영역 내 노출 방지 부분이 있습니다. 해당 항목은 HTML, JS, DOM 등 웹을 표현하기 위한 영역 내 평문이 노출 여부를 점검하는 것입니다. 

 

중요정보 노출 방지하기 위해서 메모리 덤프를 수도 없이 수집하고 테스트한 결과를 공유하고자 합니다.

 


 

메모리 덤프 확인하는 법

취약점 조치를 하기 위해서는 제일 처음 크롬에서 메모리 덤프를 뜨는 법을 알아야 합니다. 앱에서 메모리를 덤프 파일을 만드는 방법이 있겠지만 앱 개발자에게 계속 요청할 수 없어 크롬에서 메모리 덤프를 떠서 테스트를 진행했습니다. 

 

1. Shift + Esc 단축키를 누르고 메모리 덤프를 확인하고 싶은 페이지의 프로세스 ID를 확인합니다.

 

2. Ctrl + Shift + Esc 단축키를 누르고 작업 관리자에서 프로세스 ID를 검색 후 마우스를 오른쪽을 클릭하면 [메모리 덤프 파일 만들기]가 나옵니다.  [메모리 덤프 파일 만들기] 클릭하면 파일이 생성되고 해당 파일의 경로가 나옵니다.

 

3. 에디터(HxD)를 이용해 해당 파일을 열고 평문이 나왔는지 확인합니다.

 


 

취약점 조치 방법

메모리에 평문이 노출되지 않기 위해서는 문자열을 바로 변수로 저장해서는 안됩니다. 변수에 할당하는 순간 메모리에 올라가 평문을 확인할 수 있습니다. input type을 password로 하면 되지 않을까 하지만... 역시나 메모리에 올라갑니다. (또한 document.getElementById("pw"). value로 조회하면 어떤 값인지 알 수 있습니다.)

 

정말 솔루션 말고 답이 없는가 싶지만 방법은 있습니다. 문자열을 바로 변수로 저장하지 않고 배열로 저장하면 간단하게 해결됩니다. 또한 배열에 입력한 값을 하나씩 저장을 하고 input의 value를 배열 길이만큼 *를 표시합니다. 

 

<template>
  <input
    id="pw"
    type="password"
    autoComplete="off"
    ref="input"
    @keydown="handleOnKeydown"
    @input.prevent="handleOnInput"
    @paste.prevent
  />
</template>
<script>
import { encryptor } from '@/utils/encryptor'

export default {
  name: 'ProtectedInput',
  expose: ['valueArray', 'getEncryptedValue'],
  data() {
    return { valueArray: [] }
  },
  methods: {
    getEncryptedValue() {
      return encryptor(this.valueArray.join(''))
    },
    handleOnKeydown(event) {
      const eventTarget = event.target
      const eventKey = event.key.toLowerCase()
      const inputLength = eventTarget.value.length

      // input의 뒤에서만 입력 가능
      this.$refs.input.setSelectionRange(inputLength, inputLength)

      if (eventKey === 'delete' || eventKey === 'backspace') {
        this.valueArray =
          this.valueArray.length <= 1 ? [] : this.valueArray.slice(0, this.valueArray.length - 1)
        this.$refs.input.value = '*'.repeat(this.valueArray.length)
        event.preventDefault()
      }
    },
    handleOnInput(event) {
      const eventData = event.data
      if (eventData) {
        this.valueArray = [...this.valueArray, eventData]
        this.$refs.input.value = '*'.repeat(this.valueArray.length)
      }
    },
  },
}
</script>

 

 

@paste.prevent로 복사 붙여놓기를 막았고, 무조건 뒤에서만 입력할 수 있게 하였습니다. (드래그 방지 차원입니다.)

 

입력값을 valueArray 배열로 저장합니다. keydown 이벤트가 일어난 후 input 이벤트가 일어납니다. keydown 이벤트에서 event.key 가 delete 이거나 backspace 일 경우 배열에서 값을 한 개를 없앤 배열을 valueArray 재할당합니다. 이렇게 한 이유는 valueArray가 expose가 되어 있는데 부모 컴포넌트에서 입력한 값에 대한 검증을 해야 하는 경우 입력될 때마다 감시하기 위해서입니다. (새로운 값이 저장되면서 watch가 감지할 수 있습니다.) 그리고 배열 크기만큼 input의 value를 * 표시하고 event.preventDefault()를 input으로 이벤트가 넘어가지 않게 막습니다. 그 외의 key 이벤트는 넘어가게 됩니다.

 

여기서 삭제를 제외하고 event.key를 바로 배열에 저장하면 되지 않을까라고 생각할 수 있습니다. 안드로이드에서는 event.key가 unidentified로 나와 어떤 키를 입력했는지 알 수 없습니다. (웹에서는 event.key가 잘 나옵니다.) 그래서 input 이벤트에서 event.data를 통해 입력한 값을 배열로 저장합니다.

 

getEncryptedValue라는 값은 valueArray를 join 하고 바로 암호화시키는 함수로 서버에 request 할 때 암호화된 값을 보냅니다. 

 

'Dev > Vue.js' 카테고리의 다른 글

[Vue] Storybook 사용하는 법  (1) 2025.01.15
[Vue] 타이머 구현하기  (0) 2025.01.08
[Vue] 이메일 유효성 검사  (0) 2024.12.21
[Vue] 중복 클릭 방지 방법  (0) 2024.12.07
[Vue] Vue3, Storybook8 프로젝트 설정  (2) 2024.11.28

댓글