웹 프론트엔드 보안 정석: any 타입의 위험성과 XSS 이중 방어 원리
TypeScript any가 초래하는 XSS와 비즈니스 로직 왜곡의 위험성, 유니온 타입(string | number) 방어법, React 자동 이스케이프와 HTML 엔티티의 아스키 코드 차이까지 한눈에 정리합니다.
아키텍처 구조도
1. 직관적 비유: 묻지마 프리패스 상자 vs 최첨단 공항 검색대
코드에서 any를 사용하는 것은 공항 보안 검색대에서 엑스레이 전원을 끄고 "묻지마 프리패스 상자"를 아무 검사 없이 통과시키는 것과 같습니다.
핵심 전제 지식 2가지
내가 내 컴퓨터에서 장난치는 것은 나 혼자만의 일이지만, XSS는 택배 상자 안에 몰래 최면 가스를 넣어 다른 사람(일반 유저나 사이트 관리자)의 집으로 배송하는 것입니다. 택배를 열어본 관리자는 최면에 걸려 자기 손으로 지갑을 열어 최고 권한 토큰을 해커에게 넘겨줍니다.
2. any가 초래하는 3대 보안 위협과 실제 해킹 시나리오
✕위험: any 남발 시 발생하는 3대 참사
- ✕XSS 방어벽 붕괴: 악성 스크립트 객체가 들어와도 컴파일러가 입을 다물어 소독(Sanitize) 누락
- ✕비즈니스 로직 왜곡: 요금/할인 필드에 음수(-99999999)나 Infinity 주입 시 공짜 요금제 낚시 발생
- ✕DoS 서비스 거부: 예상치 못한 null 접근으로 인한 런타임 크래시(화면 하얗게 뻗는 Whiteout)
✓안전: 정석 타이핑 적용 시 방어 효과
- ✓컴파일 타임 차단: 예상치 못한 타입 주입 시 코드 빌드 단계에서 원천 차단
- ✓타입 좁히기 강제: 계산 전에 typeof 검사를 거치도록 개발자에게 안전 코드 강제
- ✓무결성 보장: 비즈니스 계산식에 항상 안전한 범위의 정상 데이터만 공급
3. 숫자와 글자를 모두 써야 할 때: 유니온 타입과 타입 좁히기
유심비(7700원 숫자 또는 "7,700원" 문자)나 할인 기간(12개월 숫자 또는 "평생" 문자)처럼 여러 타입을 허용해야 할 때는 any 대신 유니온 타입(|)을 사용합니다.
// ✅ 정석 1: 유니온 타입 선언
export interface PlanSimulationInput {
discount_duration?: number | '평생' | 'infinite' | null;
usim_fee?: string | number | null;
}
// ✅ 정석 2: 타입 좁히기(Type Narrowing)로 안전 연산
function parseUsimFee(fee?: string | number | null): number {
if (typeof fee === 'number') {
return fee > 0 ? fee : 0;
}
if (typeof fee === 'string') {
const cleaned = parseInt(fee.replace(/[^0-9]/g, ''), 10);
return isNaN(cleaned) ? 0 : cleaned;
}
return 0; // null, undefined 방어
}4. React의 1차 방어선(자동 이스케이프)과 아스키 코드의 비밀
React의 일반 JSX {value} 출력은 특수문자(<)를 안전한 HTML 엔티티(<)로 자동 변환하여 브라우저의 실행 엔진을 무력화합니다.
원래 특수문자 <
- •아스키 코드: 60 (16진수 0x3C, 단 1바이트)
- •브라우저 해석: "태그 명령어 시작! 스크립트 실행 준비!"
- •DOM 노드: HTML Element Node (실행 위험)
변환된 엔티티 <
추천- ✓아스키 코드: 38(&) + 108(l) + 116(t) + 59(;) (총 4바이트)
- ✓브라우저 해석: "화면에 < 모양 폰트만 그리고 실행 금지!"
- ✓DOM 노드: Text Node (절대 실행 불가 안전)
React가 뚫리는 2대 예외 구멍 주의
5. 4단계 중앙 척추 대칭 의사결정 트리 (Triage Decision Tree)
입력 데이터 처리 4단계 트리아지 의사결정 트리
타입 단일화 판별
단일 타입으로 표현 가능하면 string, number 등으로 명확히 못 박고 any를 배제합니다.
유니온 vs unknown 격리
정해진 타입 후보는 유니온(|)으로 묶고, 알 수 없는 외부 데이터는 unknown으로 봉인합니다.
계산 로직 타입 좁히기
typeof 검증을 거쳐 숫자가 아닌 값이 계산식으로 유입되는 버그를 원천 차단합니다.
UI 렌더링 2중 방어
기본 JSX 이스케이프를 신뢰하되, 원시 HTML 렌더링 시에는 반드시 DOMPurify 소독을 적용합니다.