Foundry
보안
중급
핵심

XSS와 출력 이스케이프

입력을 믿으면 남의 스크립트가 내 도메인에서 돈다

XSS와 출력 이스케이프

세 가지 유형

유형유입 경로예시
StoredDB에 저장댓글에 심은 스크립트가 모든 방문자에게 실행
Reflected요청 파라미터검색어를 결과 화면에 그대로 출력
DOM 기반브라우저 내부innerHTML에 URL 조각을 대입

막는 지점은 입력이 아니라 출력이다

값이 놓이는 자리처리 방법
HTML 본문꺾쇠와 앰퍼샌드를 인코딩한다
HTML 속성인용부호로 감싸고 인코딩한다
JS 문자열유니코드 이스케이프를 쓴다
URL 파라미터퍼센트 인코딩을 쓴다
  • 블랙리스트 필터링은 우회 변형을 따라가지 못한다. 컨텍스트별 인코딩이 원칙
  • 자동 이스케이프를 끄는 API가 위험 지점: React dangerouslySetInnerHTML, Vue v-html

실무 포인트

  • HTML 입력을 허용해야 하면(에디터) 직접 필터링 대신 검증된 새니타이저를 쓴다
  • CSP를 2차 방어선으로: nonce 기반 script-src, unsafe-inline 제거
  • 세션 쿠키에 HttpOnly를 붙여 스크립트의 쿠키 접근을 차단한다
  • 방어를 프런트에만 두지 않는다. API 응답에 그대로 저장된 값은 다른 클라이언트에서 다시 터진다
면접에서 이렇게 나옵니다

Q.게시판에 HTML 서식을 허용해야 한다면 어떻게 안전하게 구현하겠습니까?

전부 막을 수 없으니 허용 목록 방식의 검증된 라이브러리로 걸러냅니다. 직접 만들지 않습니다.

방법판정
위험한 태그를 지우는 차단 목록불가. 우회 방법이 계속 나온다
허용 태그와 속성만 남기는 검증 라이브러리권장
마크다운만 허용하고 HTML 은 이스케이프더 안전. 표현력은 줄어든다

허용 목록을 쓸 때 놓치기 쉬운 부분입니다.

확인할 것내용
태그만 보면 안 된다onclick 같은 이벤트 속성도 제거해야 한다
링크의 스킴javascript: 와 data: 는 거부한다
스타일 속성CSS 로도 공격이 가능하다
저장할 때와 출력할 때저장 시 정제하고 출력 시 자리에 맞게 처리한다

여기에 CSP 를 함께 걸어 인라인 스크립트 실행을 막으면 한 겹이 더 생깁니다.

흔한 실수: 정규식으로 script 태그만 지우는 것. 대소문자 섞기, 속성 안 실행, SVG 안 스크립트 등으로 뚫립니다.

Q.입력값 검증만으로 XSS를 막기 어려운 이유는 무엇인가요?

위험한지 아닌지는 값이 어디에 쓰이는지에 달려 있기 때문입니다. 입력 시점에는 그것을 모릅니다.

같은 값 javascript:alert(1) 이 어디에 놓이는지에 따라 달라집니다.

놓이는 자리결과
HTML 본문그냥 글자다. 위험하지 않다
링크의 href클릭 시 실행된다. 위험하다

그래서 원칙은 원본을 저장하고 출력 시점에 그 자리에 맞게 처리하는 것입니다.

값이 놓이는 자리처리
HTML 본문꺾쇠와 앰퍼샌드 인코딩
HTML 속성인용부호로 감싸고 인코딩
URL 자리허용 스킴만 통과
스크립트 안HTML 에 심지 않고 데이터로 넘긴다

입력 검증도 여전히 필요합니다. 길이와 형식을 제한해 공격 표면을 줄이는 역할입니다. 다만 그것만으로는 부족합니다.

흔한 실수: 입력 시점에 이스케이프해 저장하는 것. 그 값이 JSON 이나 로그로 나갈 때 이중 인코딩되어 화면이 깨집니다.

Q.CSP를 도입했는데도 XSS가 발생할 수 있는 경우는?

CSP 는 실행 경로를 제한하는 장치이고, 정책에 구멍이 있으면 통과됩니다.

구멍왜 뚫리나
unsafe-inline 허용인라인 스크립트가 실행된다. CSP 의 핵심 효과가 사라진다
unsafe-eval 허용문자열을 코드로 실행할 수 있다
넓은 허용 출처허용한 CDN 에 공격자가 올릴 수 있는 경로가 있으면 통과
JSONP 엔드포인트허용 출처가 임의 콜백을 반환해 준다
스크립트가 아닌 경로데이터 유출은 이미지나 폼 전송으로도 가능하다

그리고 CSP 는 DOM 조작으로 생기는 XSS 를 다 막지 못합니다. 이미 로드된 우리 스크립트가 사용자 입력을 innerHTML 로 넣으면 정책 안에서 벌어집니다.

흔한 실수: CSP 를 도입했으니 이스케이프를 안 해도 된다고 보는 것. CSP 는 마지막 방어선이고, 출력 이스케이프가 1차 방어입니다. 순서를 바꿀 수 없습니다.

Q.저장형 XSS와 반사형 XSS의 대응 우선순위를 어떻게 정하겠습니까?

저장형을 먼저 막습니다. 피해 범위와 지속성이 다릅니다.

항목저장형반사형
어디에 있나DB 에 남는다요청 파라미터에만 있다
피해 범위그 페이지를 보는 모든 사용자조작된 링크를 누른 사용자
공격 조건한 번 심으면 계속사용자를 유인해야 한다
정리 비용이미 저장된 데이터를 찾아 지워야 한다코드만 고치면 끝

저장형은 이미 심어진 것을 찾아 지우는 작업이 따라옵니다. 그래서 발견 즉시 유입을 막고, 기존 데이터를 훑어 정리하고, 그 사이 노출된 세션을 무효화할지 판단합니다.

대응 순서
  1. 저장 경로를 막는다 (유입 차단)
  2. 저장된 데이터를 조사하고 정리한다
  3. 출력 이스케이프를 전 경로에 적용한다
  4. 반사형 경로를 정리한다

흔한 실수: 코드만 고치고 끝내는 것. 저장형은 데이터에 남아 있어 코드 수정만으로 사라지지 않습니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

보안 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.