Web Security

XSS 방지책 (Express.js)

꼰대코더 2025. 12. 2. 15:12

🛡️ Express.js (API Only) XSS 방지 대책

1. 📝 입력 데이터 정제 (Input Sanitization)

사용자 입력(JSON, 쿼리 파라미터 등)을 데이터베이스에 저장하거나 응답으로 보내기 전에 악성 HTML이나 스크립트 코드를 제거합니다.

✅ 샘플 코드: dompurify와 jsdom을 사용한 정제

Express 환경에서는 브라우저 환경에서 사용되는 **dompurify**를 서버 측에서 실행하기 위해 **jsdom**과 함께 사용하거나, **sanitize-html**과 같은 라이브러리를 사용합니다. 여기서는 널리 사용되는 **sanitize-html**을 사용합니다.

Bash
 
# 💡 라이브러리 설치
npm install sanitize-html express
JavaScript
 
const express = require('express');
const sanitizeHtml = require('sanitize-html');
const app = express();

app.use(express.json()); // JSON 요청 본문을 파싱

// API 서버이므로 HTML 태그를 허용하지 않는 것이 가장 안전합니다.
const sanitizeOptions = {
    allowedTags: [],       // 허용 태그 없음
    allowedAttributes: {}  // 허용 속성 없음
};

app.post('/api/posts', (req, res) => {
    const rawContent = req.body.content;

    // ❌ 정제 없이 직접 사용 (위험!)
    // const content = rawContent;

    // ✅ sanitize-html을 사용하여 모든 HTML 태그를 제거하고 안전한 텍스트로 만듦
    const sanitizedContent = sanitizeHtml(rawContent, sanitizeOptions);

    // DB 저장 로직: sanitizedContent를 저장
    // ...

    res.status(201).json({
        message: "Post created successfully",
        content: sanitizedContent
    });
});

app.listen(3000, () => console.log('Server running on port 3000'));

설명: sanitize-html을 사용하여 사용자 입력에서 모든 HTML 태그를 제거합니다. 이로써 프론트엔드에서 데이터를 렌더링할 때 <script> 같은 태그가 실행되는 것을 원천적으로 차단합니다.


2. 🛡️ 보안 HTTP 헤더 설정 (Security Headers)

API 응답에 보안 헤더를 추가하여, 설령 공격 코드가 삽입되더라도 브라우저의 보안 정책을 통해 실행을 억제합니다.

✅ 샘플 코드: helmet 미들웨어를 사용한 자동 설정

Express에서 보안 헤더를 쉽게 설정하기 위해 helmet 라이브웨어를 사용하는 것이 업계 표준입니다.

Bash
 
# 💡 라이브러리 설치
npm install helmet
JavaScript
 
const express = require('express');
const helmet = require('helmet');
const app = express();

// 1. ✅ Helmet 미들웨어를 사용하여 기본적인 보안 헤더를 설정합니다.
// X-Content-Type-Options, X-Frame-Options 등 여러 헤더를 자동으로 추가합니다.
app.use(helmet());

// 2. Content Security Policy (CSP) 설정
// API 서버이므로 'self'로 엄격하게 설정하는 것이 좋습니다.
app.use(
    helmet.contentSecurityPolicy({
        directives: {
            defaultSrc: ["'self'"],
            objectSrc: ["'none'"], // <object>, <embed> 태그 사용 금지
            scriptSrc: ["'self'"]  // 스크립트 로딩을 현재 도메인으로 제한
        },
    })
);

app.get('/api/users', (req, res) => {
    // 이 응답에는 위에 설정된 강력한 보안 헤더가 자동으로 포함됩니다.
    res.json({ users: [{ id: 1, name: 'Alice' }] });
});

app.listen(3000, () => console.log('Server running with Helmet on port 3000'));

설명:

  • helmet(): Flask에서 수동으로 추가했던 X-Content-Type-Options: nosniff, X-Frame-Options: DENY 등 다수의 보안 헤더를 한 줄로 적용합니다.
  • helmet.contentSecurityPolicy(...): CSP는 가장 중요한 방어막입니다. defaultSrc: ["'self'"]를 통해 브라우저는 현재 도메인에서 로드된 리소스만 신뢰하게 되어 외부에서 주입된 악성 스크립트 실행을 차단합니다.

3. 🍪 HttpOnly 쿠키 사용

인증 및 세션 관리에 사용되는 쿠키는 XSS 공격을 통해 탈취되는 것을 막기 위해 HttpOnly 옵션을 설정해야 합니다.

✅ 샘플 코드: 쿠키 설정 시 httpOnly: true 옵션 사용

Express에서는 res.cookie() 메서드를 사용할 때 옵션을 지정할 수 있습니다.

JavaScript
 
// Express에서는 'cookie-parser' 라이브러리가 세션 관리에 사용되기도 합니다.

app.post('/api/login', (req, res) => {
    // ... 인증 로직 ...
    const sessionToken = 'secure_random_token_value';

    // ✅ HttpOnly 설정: 클라이언트 JavaScript가 쿠키에 접근 불가능
    res.cookie('session_token', sessionToken, {
        httpOnly: true, // XSS를 통한 쿠키 탈취 방지
        secure: true,   // HTTPS 환경에서만 전송
        sameSite: 'Lax', // CSRF 방지에도 도움
        maxAge: 3600000 // 1시간 유효
    });

    res.json({ message: "Login successful" });
});

이 세 가지 방법을 통해 Express API 서버는 데이터 정제, 강력한 보안 헤더, 안전한 세션 관리를 통해 XSS 공격을 효과적으로 방어할 수 있습니다.