API 보안: Helmet·CORS·CSRF 적용 방법
Node.js 기반 API 보안을 위해 Helmet, CORS, CSRF의 핵심 개념과 실제 설정 예제를 단계별로 정리한, 환경 구성과 코드 예제를 포함한 실용적 설정
목차
소개
API를 운영할 때 HTTP 헤더, 교차 출처 접근, 요청 위조 같은 기본 취약점을 놓치면 실무에서 큰 문제가 발생한다. 이 글에서는 Node.js와 Express 환경에서 Helmet, CORS, CSRF를 어떻게 적용하고 설정해야 하는지 쉽게 설명한다. 처음 접하는 개발자도 이해할 수 있도록 개념과 코드 예제를 함께 제공한다.
Helmet: HTTP 보안 헤더 강화
왜 필요한가
Helmet은 여러 보안 관련 HTTP 헤더를 한 번에 설정해 주는 미들웨어다. XSS, 클릭재킹, MIME 스니핑 등의 공격을 막는 데 도움을 준다.
설치 및 기본 사용
설치는 간단하다. Express 앱에 미들웨어로 추가하면 기본 헤더들이 적용된다.
npm install helmet
const express = require('express')
const helmet = require('helmet')
const app = express()
app.use(helmet())
app.get('/', (req, res) => {
res.send('ok')
})
app.listen(3000)
세부 설정
기본값으로 충분한 경우가 많지만, 일부 헤더는 서비스 특성에 맞게 조정해야 한다. 예를 들어 Content Security Policy(CSP)는 리소스 로딩을 제어한다.
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"]
}
}))
CORS: 출처 간 요청 제어
개념 요약
CORS는 브라우저가 다른 출처의 리소스에 접근할 수 있는지 제어하는 메커니즘이다. 서버에서 허용 출처와 허용 메서드 등을 설정해야 브라우저가 요청을 허용한다.
Express CORS 설정 예제
npm 패키지 cors를 사용하면 설정이 편리하다. 공개 API인지 특정 도메인만 허용할지 결정한다.
npm install cors
const cors = require('cors')
// 모든 출처 허용(개발 환경에서만 권장)
app.use(cors())
// 특정 출처만 허용
app.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}))
실무 팁
- 운영 환경에서는 와일드카드(*) 사용을 피한다.
- 인증이 필요한 API는 credentials와 허용 출처를 명시한다.
- 프리플라이트 요청(OPTIONS)에 대해 적절한 응답을 구성한다.
CSRF: 요청 위조 방지
왜 중요한가
CSRF는 인증된 사용자의 권한을 악용해 원치 않는 요청을 보내게 하는 공격이다. 특히 쿠키 기반 인증을 사용하는 API에서 주의해야 한다.
Node CSRF 보호 방법 (예제)
Express 환경에서는 csurf 미들웨어를 사용한다. 쿠키 파서를 함께 사용해 토큰을 발급하고 검증한다.
npm install csurf cookie-parser
const cookieParser = require('cookie-parser')
const csurf = require('csurf')
app.use(cookieParser())
app.use(csurf({ cookie: true }))
// 토큰 제공 엔드포인트
app.get('/form', (req, res) => {
res.json({ csrfToken: req.csrfToken() })
})
// 토큰 검증 예시
app.post('/process', (req, res) => {
res.send('처리 완료')
})
프론트엔드와의 연동
클라이언트는 서버가 제공한 토큰을 요청 헤더나 바디에 포함해 전송해야 한다. fetch 예시는 아래와 같다.
fetch('/process', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CSRF-Token': token
},
body: JSON.stringify(data),
credentials: 'include'
})
통합 적용과 우선순위
세 가지를 함께 적용할 때는 순서와 조합을 고려한다. 일반적으로 Helmet을 먼저 적용한다. 그다음 CORS 정책을 설정하고, 마지막으로 CSRF 보호를 추가한다. 미들웨어 순서가 중요하다.
예시 전체 구성
const express = require('express')
const helmet = require('helmet')
const cors = require('cors')
const cookieParser = require('cookie-parser')
const csurf = require('csurf')
const app = express()
app.use(helmet())
app.use(cors({ origin: 'https://app.example.com', credentials: true }))
app.use(cookieParser())
app.use(express.json())
app.use(csurf({ cookie: true }))
// 라우트 정의 ...
운영에서의 추가 고려사항
- 로그와 모니터링으로 비정상 요청 패턴을 탐지한다.
- 서브도메인, CDN 등 인프라 특성을 반영해 CSP 및 CORS를 조정한다.
- 테스트 환경에서 토큰 수명과 예외 처리를 검증한다.
맺음말
Helmet, CORS, CSRF는 각각 다른 레이어에서 API 보안을 강화한다. 함께 적용하면 취약점을 상당 부분 줄일 수 있다. 개념을 이해한 뒤 작은 예제부터 적용해 운영 환경에 맞게 조정하는 것이 중요하다.