
얼마 전 운영 중인 사이트에서 검색어가 결과 화면에 그대로 찍히는 걸 봤다. 검색 기능은 멀쩡했지만, 입력한 문자가 HTML로 해석된다면 이야기가 달라진다. XSS는 공격자가 넣은 스크립트가 다른 사람의 브라우저에서 실행되는 문제다. 서버가 뚫리지 않아도 로그인한 사용자의 화면에서 원치 않는 동작이 일어날 수 있다.
xss 공격 종류는 보통 반사형, 저장형, DOM 기반으로 나눈다. 이름보다 중요한 건 공격자가 넣은 값이 어디에 머물고, 어느 단계에서 화면의 코드로 바뀌는지다. 이 경로를 알아야 로그를 볼지, 데이터베이스를 볼지, 브라우저 코드를 볼지 결정할 수 있다.
검색 결과에 바로 나타나는 반사형 XSS
반사형은 요청에 담긴 값이 서버 응답에 곧바로 실리는 방식이다. 검색어, 오류 안내 문구, 이동 후 표시할 메시지가 흔한 자리다. 예를 들어 검색어를 결과 페이지 제목에 넣으면서 특수문자를 처리하지 않으면, 브라우저가 그 값을 단순한 글자가 아닌 HTML 요소로 읽을 수 있다. 값은 데이터베이스에 남지 않아도 된다. 공격자가 만든 요청을 사용자가 열기만 하면 같은 응답이 만들어질 수 있다.
운영할 때는 검색 결과와 오류 페이지부터 본다. 정상 단어 대신 꺾쇠괄호나 따옴표를 넣었을 때 화면에 문자 그대로 표시되는지 확인한다. 접근 로그에 특이한 검색어가 반복되는 것도 단서다. 다만 로그에 시도가 보인다고 곧바로 실행에 성공했다는 뜻은 아니다. 실제 응답에서 해당 값이 어느 위치에 출력됐는지까지 봐야 한다.
게시물에 남아 다른 사용자에게 닿는 저장형 XSS
저장형은 공격자의 입력이 게시글, 댓글, 프로필, 문의 내용 등에 저장된 뒤 다른 사람에게 제공되는 방식이다. 관리자가 문의함을 열 때만 문제가 드러나는 사례도 있다. 공개 게시판이라면 한 번 저장된 내용이 여러 방문자에게 반복해서 전달된다. 반사형보다 피해 범위가 커지기 쉬운 이유다.
여기서 자주 놓치는 부분은 관리자 화면이다. 공개 페이지에서는 내용을 안전하게 표시해도, 운영 도구에서 원문을 그대로 HTML에 끼워 넣으면 관리자가 내용을 읽는 순간 스크립트가 실행될 수 있다. 백업으로 데이터를 복원하거나 서버를 이전해도 위험한 게시물이 함께 옮겨 간다. 복원 후 기능 점검에 댓글과 문의함 표시를 포함하는 편이 좋다.
HTML 작성을 허용하는 게시판이라면 단순히 꺾쇠괄호만 지워서는 부족하다. 허용할 요소와 속성을 제한하는 검증된 HTML 정화 기능이 필요하다. 일반 텍스트만 받는 칸은 HTML로 해석하지 않도록 출력하는 쪽이 단순하고 안전하다.
브라우저 안에서 값이 바뀌는 DOM 기반 XSS
DOM 기반은 페이지를 받은 뒤 브라우저의 자바스크립트가 입력값을 읽고 화면을 바꾸는 과정에서 생긴다. 주소의 일부나 브라우저 저장소의 값을 읽어 화면에 넣는 기능을 떠올리면 쉽다. 서버가 보낸 원래 HTML은 안전해 보여도, 브라우저 코드가 그 값을 HTML로 해석하는 위치에 넣으면 문제가 생긴다.
이 유형은 서버 접근 로그만 보고 놓치기 쉽다. 특히 주소에서 샵 기호 뒤에 붙는 값은 보통 서버 요청에 전달되지 않는다. 조사할 때는 브라우저 개발자 도구에서 화면 요소가 어떻게 만들어졌는지 보고, 값을 읽는 코드와 화면에 넣는 코드를 함께 따라가야 한다. 일반 텍스트를 보여줄 목적이라면 HTML을 해석하는 방식 대신 텍스트로 넣는 방식을 쓴다.
세 종류에 공통으로 적용할 방어 순서
첫째는 출력 위치에 맞는 처리다. HTML 본문에 넣는 값, HTML 속성에 넣는 값, 자바스크립트 문자열에 넣는 값은 필요한 처리가 서로 다르다. 그래서 입력을 받을 때 특수문자를 일괄 삭제하는 방법만으로는 부족하다. 가능하면 사용자 값을 자바스크립트 코드나 HTML 속성 안에 직접 조합하지 않는 구조가 낫다.
둘째는 저장된 데이터와 운영 화면까지 확인하는 것이다. 수정한 화면만 고치고 과거 게시물을 그대로 두면 저장형 문제가 남는다. 취약한 출력 위치를 고친 뒤 기존 데이터가 어디에 다시 표시되는지 확인해야 한다. 검색, 게시판, 관리자 화면처럼 같은 값을 서로 다르게 출력하는 곳이 특히 중요하다.
셋째는 피해를 줄이는 설정이다. 콘텐츠 보안 정책은 브라우저가 실행할 스크립트의 범위를 제한하는 데 도움이 된다. 쿠키의 HttpOnly 설정은 스크립트가 해당 쿠키 값을 읽지 못하게 한다. 다만 이런 설정이 취약한 화면 자체를 고치지는 않는다. 웹 방화벽도 알려진 입력을 걸러낼 수 있지만, 화면에서 값이 어떻게 해석되는지까지 대신 판단해 주지는 못한다.
서버 장애를 볼 때는 요청, 응답, 저장 데이터를 순서대로 좁혀 간다. XSS도 비슷하다. 값이 요청에서 바로 돌아왔는지, 저장됐다가 다시 나왔는지, 브라우저 안에서 바뀌었는지를 먼저 가르면 조사할 곳이 선명해진다. 비싼 보안 장비를 추가하기 전에 출력 위치와 관리자 화면부터 확인하는 게 내 경험상 더 빨리 문제를 줄였다.
