취약점 유형❗ XSS (Cross Site Scripting)
설명: 사용자 입력을 DOM에 직접 렌더링하면 악성 스크립트가 실행될 수 있음
방어전략:
✅ dangerouslySetInnerHTML 지양 or DOMPurify 사용
✅ 사용자 입력 escape
✅ Content-Security-Policy(CSP) 설정
취약점 유형❗ LocalStorage/SessionStorage에 민감정보 저장
설명: JS로 접근 가능한 저장소 → XSS에 노출되면 토큰 탈취 가능
방어전략:
✅ accessToken은 React 메모리나 HttpOnly 쿠키에 저장
❌ localStorage에 저장하지 않기
취약점 유형❗ CSRF (Cross-Site Request Forgery)
설명: 다른 사이트에서 요청을 보내도 브라우저가 쿠키를 자동으로 전송함
방어전략:
✅ SameSite=Strict 쿠키 설정
✅ 민감 요청은 반드시 POST로 처리
✅ Origin/Referer 헤더 체크
취약점 유형❗ Open Redirect
설명: ?redirect=/evil.com 등의 URL 파라미터를 통해 외부로 유도 가능
방어전략:
✅ redirect 대상은 whitelist 처리 (내부 경로만 허용)
✅ 외부 링크는 직접 입력 or 경고창 제공
취약점 유형❗ 콘솔 로그에 민감 정보 출력
설명: console.log(token) 같은 코드가 남아 있으면 브라우저 DevTools에서 노출 가능
방어전략:
✅ production 모드에서는 console.log 제거
✅ 로그 정리 및 환경별 분기 처리
취약점 유형❗ 인증 우회 (UI만 숨김)
설명: 버튼을 숨겨도 API가 열려 있으면 공격자는 직접 호출 가능
방어전략:
✅ 백엔드에서 항상 인증/인가 검증
❌ "admin 버튼 숨김"만으로 보호하지 않기
취약점 유형❗ axios 요청에 민감 정보 포함
설명: 토큰, 이메일, 비밀번호 등을 URL 쿼리로 보내면 로그/히스토리에 남음
방어전략:
✅ 민감 정보는 POST 요청 body에 포함
❌ GET /api?password=1234 방식 금지
취약점 유형❗ 외부 라이브러리 취약점
설명: 오래된 의존성이나 악성 패키지 사용 시 위험
방어전략:
✅ npm audit, yarn audit, snyk 등으로 점검
✅ 공식 패키지만 사용, 버전 주기적 업데이트
취약점 유형❗ 사용자 입력 검증 부족
설명: 클라이언트에서 필드 검증을 안 하면 백엔드에 잘못된 값 전달 가능
방어전략:
✅ zod, yup, react-hook-form 등으로 프론트에서 입력 검증
✅ 최소한의 유효성 검사는 프론트에서도 수행
취약점 유형❗ URL에 민감 정보 노출
설명: reset-password?token=... 같은 링크가 유출되면 위험
방어전략:
✅ URL에 토큰 담는 경우는 최소화 + 유효기간 짧게
✅ 가능하면 body나 쿠키에 담기
취약점 유형❗ 앱 크래시 유발 (비정상 입력으로)
설명: 타입 불일치 등으로 앱이 죽을 수 있음
방어전략:
✅ TypeScript로 타입 안정성 확보
✅ ErrorBoundary로 앱 전체 크래시 방지
React 개발 시 보안 좋은 습관
보안 개발 린트 사용 | eslint-plugin-security 설치 |
HTTPS 강제 | window.location.protocol !== 'https' → 리디렉션 |
CSP 설정 | meta 태그 or Nginx 등에서 Content-Security-Policy 적용 |
정기 보안 테스트 | OWASP ZAP 등으로 스캔 |
토큰 만료 타이머 | accessToken 자동 만료 감지 & 리프레시 흐름 구현 |
devtools 접근 제한 (선택사항) | dev 모드에서만 콘솔 허용 (if (process.env.NODE_ENV === "development")) |
위험한 코드 vs 안전한 코드
❌ 위험한 코드
<div dangerouslySetInnerHTML={{ __html: userInput }} />
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
React 보안 마인드셋
프론트는 사용자가 “보는 화면”이 아니라,
공격자가 "조작할 수 있는 프로그램"이라고 생각해야 해요.
- 항상 입력값은 조작될 수 있다고 가정
- 백엔드는 항상 믿을 수 있어야 하고, 프론트는 철저히 방어적으로 개발
React + Rich Text Editor (RTE) XSS 방어 구조
시나리오
- 사용자가 글쓰기 에디터에 <b>, <h1>, <script> 등 HTML을 입력함
- 서버에 저장하고, 다른 사용자가 그 HTML을 그대로 출력
- <script> 같은 악성 코드 실행 위험 → XSS!
사용 라이브러리
- 에디터: react-quill, draft-js, toast-ui editor, tiptap, Slate 등
- XSS 방어: DOMPurify (프론트), jsoup (서버)
프론트엔드 구조 (React)
import DOMPurify from 'dompurify';
const SafeHTMLViewer = ({ html }: { html: string }) => {
return (
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(html), // 중요!
}}
/>
);
};
왜 sanitize?
- <script>, <img onerror>, onmouseover 같은 이벤트 기반 XSS를 자동 제거
서버 측도 더 안전하게 하려면?
Spring Boot + jsoup 예제
public class HtmlSanitizer {
public static String sanitize(String html) {
return Jsoup.clean(html, Safelist.basic());
// basic()은 b, i, u, a, ul 등만 허용
}
}
사용 예
String safeHtml = HtmlSanitizer.sanitize(userInputHtml);
article.setContent(safeHtml);
프론트에서도 DOMPurify, 백엔드에서도 jsoup.clean()
→ 이중 방어
공격자 입장에서 자주 쓰는 XSS 페이로드
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<a href="javascript:alert(1)">click me</a>
<div onclick="alert(1)">click</div>
이런 코드들이 들어와도 DOMPurify와 jsoup은 전부 제거해줘.
Markdown 게시판 XSS 방어 구조
시나리오
- 사용자가 마크다운 작성
- 서버에 저장된 Markdown → HTML로 변환해서 출력
→ 변환된 HTML이 위험할 수 있음
서버에서 XSS 방지 처리 흐름
### 입력 (Markdown)
안녕하세요! <script>alert('XSS')</script>
변환 + 정화
- Markdown → HTML (예: flexmark-java, markdown-it)
- 변환된 HTML을 jsoup.clean()으로 sanitize
- 안전한 HTML만 저장하거나 반환
String html = markdownToHtml(markdown); // markdown-it 등으로 변환
String safeHtml = Jsoup.clean(html, Safelist.basicWithImages());
클라이언트에서 safe하게 렌더링
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />
보통은 서버에서 sanitize 후 전송하면 이건 생략 가능
요약 구조: Rich Text / Markdown 게시판 XSS 완전 방어 설계
[입력]
React 에디터 (Quill, Toast UI 등)
↓ (HTML or Markdown 전송)
[서버]
Markdown → HTML (필요 시)
→ Jsoup.clean(...)으로 sanitize
↓
[출력]
React: DOMPurify.sanitize(...) → dangerouslySetInnerHTML
입력 시 escape❌, 출력 시 sanitize✅
클라이언트와 서버에서 이중 정화(sanitize) 구조!
보안 추가 팁
- DOMPurify.sanitize(..., { USE_PROFILES: { html: true } }) 옵션도 고려
- 사용자 이미지 업로드 시 파일 확장자 + Content-Type 이중 체크
- MathJax, CodeBlock 등 마크다운 확장 기능은 화이트리스트를 따로 구성
'Computer > Computer Security' 카테고리의 다른 글
배포 환경 보안 체크리스트 (0) | 2025.04.01 |
---|---|
Spring Boot 보안 체크리스트 (0) | 2025.04.01 |
웹 개발자, 특히 풀스택 개발자라면 반드시 알고 있어야 할 사이버 공격 종류 (2) | 2025.04.01 |
네트워크 보안 (0) | 2025.03.28 |
서버 보안 (1) | 2025.03.26 |