웹 애플리케이션을 개발하다 보면 사용자 입력값에 특수문자나 유니코드 문자가 포함될 때 예상치 못한 문제가 발생하곤 합니다. 특히 보안과 관련하여 크로스 사이트 스크립팅(XSS) 공격을 방지하면서도 특수문자를 올바르게 처리해야 하는 상황에서는 더욱 주의가 필요합니다.
이번 글에서는 제가 최근 경험한 문제와 그 해결 과정을 공유하고자 합니다.
문제 상황
20년이 넘은 웹이 있는데 그곳에서 회사 스팸 필터링 시스템을 운영하고 있습니다. 이 시스템에서 스팸 단어를 등록하고 검색하는 과정에서 다음과 같은 문제가 발생했습니다
- 그리스 문자와 같은 유니코드 문자(예: "νιΡ")가 HTML 엔티티(예: "νιΡ")로 변환되어 저장되는 문제
- 특수문자가 포함된 스팸 단어(예: "[비+&씨]")가 검색되지 않는 문제
- 특수문자 입력 시 "요청 타겟에서 유효하지 않은 문자가 발견되었습니다" 오류 발생
문제의 원인
이 문제들의 원인을 분석해보니 다음과 같았습니다:
1. HTML 엔티티 변환 문제
웹 페이지에서 XSS 공격을 방지하기 위해 사용자 입력을 자동으로 HTML 엔티티로 변환하는 과정에서 유니코드 문자도 함께 변환되었습니다.
2. 특수문자 필터링
보안을 위해 사용자 입력에서 특수문자를 제거하는 함수가 있었고, 이로 인해 특수문자가 포함된 단어는 제대로 저장되지 않았습니다:
function chkChar(obj) {
var RegExp = /[\{\}\[\]\/?.,;:|\)*~`!^\-_+┼<>@\#$%&\'\"\\\(\=]/gi;
if (RegExp.test(obj.value)) {
// 특수문자 모두 제거
obj.value = obj.value.replace(RegExp, '');
}
}
3. URL 인코딩 부재
검색 기능에서 특수문자가 포함된 검색어를 URL 파라미터로 전송할 때 인코딩을 하지 않아 HTTP 요청이 올바르게 처리되지 않았습니다.
XssFilter 라이브러리를 활용한 해결책
문제 해결을 위해 NHN의 lucy-xss-filter 라이브러리를 도입했습니다. 이 라이브러리의 장점은 다음과 같습니다:
- XSS 공격은 방지하면서도 일반적인 특수문자는 보존
- HTML 엔티티로 변환된 문자를 원래 문자로 복원하는 기능 제공
적용 방법
1. 라이브러리 추가
Maven을 사용하는 경우:
<dependency>
<groupId>com.navercorp.lucy</groupId>
<artifactId>lucy-xss-servlet</artifactId>
<version>2.0.0</version>
</dependency>
2. JSP 페이지에 import 추가
<%@page import="com.nhncorp.lucy.security.xss.XssPreventer"%>
3. 사용자 입력 처리
<%
request.setCharacterEncoding("UTF-8");
String spamnumber = request.getParameter("spamnumber") == null ? "" :
XssPreventer.unescape(request.getParameter("spamnumber"));
%>
4. URL 인코딩 적용
function fnFind() {
// URL 파라미터 전송 시 인코딩 적용
var param = "&searchNum=" + encodeURIComponent(frm.sNum.value);
contents.location.href = "relay_kakaolist.jsp?cpid=" + cpid + param;
}
5. 서버 측 디코딩
<%
String searchNum = "";
if (request.getParameter("searchNum") != null) {
try {
searchNum = java.net.URLDecoder.decode(request.getParameter("searchNum"), "UTF-8");
searchNum = XssPreventer.unescape(searchNum);
} catch (Exception e) {
System.out.println("Decoding error: " + e.toString());
searchNum = request.getParameter("searchNum");
}
}
%>
6. 특수문자 필터링 수정
불필요한 특수문자 필터링을 제거하거나, 보안에 실제로 위험한 패턴만 필터링하도록 수정:
function chkChar(obj) {
// 위험한 스크립트 패턴만 필터링
var RegExp = /<script|alert\s*\(|document\.cookie|javascript:/gi;
if (RegExp.test(obj.value)) {
obj.value = obj.value.replace(RegExp, '');
}
}
추가 보안 조치: Tomcat 설정 수정
특수문자가 URL에 포함될 때 발생하는 오류를 해결하기 위해 Tomcat의 server.xml 파일에 다음 설정을 추가했습니다:
<Connector
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
relaxedQueryChars="[]|{}^\`<>%&"
relaxedPathChars="[]|{}^\`<>%&"
/>
이 설정은 URL 파라미터에 특수문자가 포함될 수 있도록 Tomcat의 문자 검증 규칙을 완화합니다.
결과
이러한 변경 사항을 적용한 후, 다음과 같은 개선이 이루어졌습니다:
- 유니코드 문자(νιΡ)가 HTML 엔티티가 아닌 원래 형태로 저장되고 표시됨
- 특수문자가 포함된 스팸 단어("[비+&씨]")가 정상적으로 검색됨
- URL에 특수문자가 포함되어도 오류가 발생하지 않음
결론: 보안과 사용성의 균형
이번 경험을 통해 보안과 사용성 사이의 균형이 얼마나 중요한지 다시 한번 깨달았습니다. XSS 공격을 방지하는 것은 매우 중요하지만, 그것이 사용자 경험을 저해해서는 안 됩니다.
NHN의 lucy-xss-filter와 같은 라이브러리는 이러한 균형을 맞추는 데 큰 도움이 됩니다. 이 라이브러리는 유해한 스크립트는 차단하면서도 일반적인 특수문자와 유니코드는 보존하므로, 보안과 사용성을 모두 챙길 수 있습니다.
또한, URL 인코딩과 서버 설정 최적화를 통해 특수문자가 포함된 데이터를 안전하게 처리할 수 있음을 확인했습니다.
웹 애플리케이션을 개발할 때는 항상 보안을 염두에 두되, 그것이 합법적인 사용자 요구사항을 제한하지 않도록 주의해야 함을 명심해야겠습니다.
'업무 기록 > ETC' 카테고리의 다른 글
대용량 데이터 처리 방식 과 흐름 (0) | 2025.02.23 |
---|---|
스레드 개수 설정 및 성능 최적화와 방법들 (1) | 2025.02.20 |
개발자와 비개발자와의 효과적인 협업 가이드: 소통의 벽을 허물다 (2) | 2024.11.22 |
Kotlin(코틀린) 코루틴으로 비동기 프로그래밍 (0) | 2024.11.20 |
L4 로드벨런싱과 암호화 및 VIP (0) | 2024.06.11 |