deep-dive2025-05-06·8·86/348

Express.js 쿠키 설정 (cookie-parser) 완벽 가이드

Express.js에서 cookie-parser 미들웨어를 사용하여 쿠키를 효과적으로 관리하는 방법을 다룹니다.

Introduction

쿠키는 웹 애플리케이션에서 사용자 상태를 관리하는 중요한 요소입니다. Express.js에서 cookie-parser 미들웨어를 사용하면 쿠키를 쉽게 파싱하고 설정할 수 있습니다.

Environment

npm list express cookie-parser
# express@4.18.2
# cookie-parser@1.4.6

node --version
# v20.11.0

Problem

쿠키 설정이 올바르지 않아 보안 문제가 발생했습니다:

// 보안에 문제가 있는 코드
app.get('/login', (req, res) => {
    const token = generateToken(user);

    // HttpOnly 미설정으로 XSS 공격에 취약
    res.cookie('token', token);

    // Secure 미설정으로 HTTP에서도 전송
    // SameSite 미설정으로 CSRF 공격에 취약
});

Solution

1. cookie-parser 기본 설정

const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();

// cookie-parser 미들웨어
app.use(cookieParser());

// 비밀 키가 있는 서명된 쿠키
app.use(cookieParser('my-secret-key'));

// 라우트에서 쿠키 사용
app.get('/set-cookie', (req, res) => {
    res.cookie('name', 'Joel', {
        maxAge: 24 * 60 * 60 * 1000, // 1일
    });
    res.send('Cookie set');
});

app.get('/get-cookie', (req, res) => {
    const name = req.cookies.name;
    res.json({ name });
});

app.get('/get-signed-cookie', (req, res) => {
    const value = req.signedCookies.name;
    res.json({ value });
});

2. 보안 쿠키 설정

const cookieOptions = {
    // 보안 옵션
    httpOnly: true,      // JavaScript 접근 불가
    secure: true,        // HTTPS에서만 전송
    sameSite: 'strict',  // CSRF 공격 방지
    maxAge: 24 * 60 * 60 * 1000, // 1일
    path: '/',           // 쿠키 경로
    domain: '.myapp.com', // 도메인
};

// JWT 토큰 쿠키
app.post('/login', async (req, res) => {
    const { email, password } = req.body;
    const user = await authenticateUser(email, password);

    if (!user) {
        return res.status(401).json({ error: 'Invalid credentials' });
    }

    const accessToken = generateAccessToken(user);
    const refreshToken = generateRefreshToken(user);

    // Access Token은 HttpOnly 쿠키로
    res.cookie('accessToken', accessToken, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 15 * 60 * 1000, // 15분
    });

    // Refresh Token도 HttpOnly 쿠키로
    res.cookie('refreshToken', refreshToken, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 7 * 24 * 60 * 60 * 1000, // 7일
        path: '/api/auth', // 특정 경로에서만 접근 가능
    });

    res.json({ user: { id: user.id, email: user.email } });
});

3. 쿠키 기반 세션

const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');

// Redis 클라이언트
const redisClient = createClient();
redisClient.connect();

// 세션 설정
app.use(session({
    store: new RedisStore({ client: redisClient }),
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: process.env.NODE_ENV === 'production',
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000,
        sameSite: 'strict',
    },
}));

// 세션 미들웨어
app.use((req, res, next) => {
    // 세션 타임아웃 설정
    if (req.session) {
        req.session.now = Date.now();
    }
    next();
});

4. 쿠키 유틸리티 함수

class CookieManager {
    constructor(res) {
        this.res = res;
    }

    set(name, value, options = {}) {
        const defaultOptions = {
            httpOnly: true,
            secure: process.env.NODE_ENV === 'production',
            sameSite: 'strict',
            path: '/',
        };

        this.res.cookie(name, value, { ...defaultOptions, ...options });
        return this;
    }

    get(name) {
        return this.req?.cookies?.[name];
    }

    remove(name, options = {}) {
        this.res.clearCookie(name, {
            path: '/',
            ...options,
        });
        return this;
    }

    setJSON(name, value, options = {}) {
        return this.set(name, JSON.stringify(value), {
            ...options,
            signed: false,
        });
    }

    getJSON(name) {
        const value = this.get(name);
        try {
            return JSON.parse(value);
        } catch {
            return null;
        }
    }
}

// Express 미들웨어
function cookieManager(req, res, next) {
    req.cookieManager = new CookieManager(req, res);
    res.cookieManager = new CookieManager(req, res);
    next();
}

app.use(cookieManager);

5. 쿠키 정책 설정

// CORS와 쿠키 설정
const cors = require('cors');

app.use(cors({
    origin: process.env.ALLOWED_ORIGINS.split(','),
    credentials: true, // 쿠키 전송 허용
    exposedHeaders: ['Set-Cookie'],
}));

// CSP 헤더에서 쿠키 정책 설정
app.use((req, res, next) => {
    res.setHeader('Set-Cookie', [
        'SameSite=Strict',
        'Secure',
        'HttpOnly',
    ].join('; '));
    next();
});

Lessons Learned

  1. 보안 설정 필수: HttpOnly, Secure, SameSite 옵션을 항상 설정하세요
  2. 서명된 쿠키: 중요한 데이터는 서명된 쿠키를 사용하여 변조를 방지하세요
  3. 도메인 설정: 프로덕션에서는 도메인을 명확히 설정하세요
  4. 만료 시간 관리: 쿠키 만료 시간을 적절히 설정하여 보안을 강화하세요
  5. Redis 세션: 세션 데이터는 Redis에 저장하여 확장성을 확보하세요

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.