Node.js는 서버 사이드 애플리케이션 개발에 널리 사용되는 인기 있는 런타임 환경이지만, 보안 취약점이 존재할 경우 악의적인 공격자에게 쉽게 노출될 수 있습니다.
이번 포스팅에서는 Node.js 웹 애플리케이션에서 발생할 수 있는 주요 보안 취약점과 이를 방어하는 방법을 정리해 보겠습니다.
1. SQL 인젝션(SQL Injection) 방어
위험성
SQL 인젝션은 공격자가 입력 필드를 이용해 악의적인 SQL 쿼리를 삽입하는 방식입니다. 이로 인해 데이터베이스의 정보가 유출되거나 변경될 위험이 있습니다.
방어 방법
✅ ORM 및 프레임워크 사용: Sequelize, TypeORM과 같은 ORM을 사용하여 직접적인 SQL 쿼리를 피합니다.
✅ 프리페어드 스테이트먼트(Prepared Statements) 사용:
const query = 'SELECT * FROM users WHERE username = ?';
db.execute(query, [username], function(err, results) {
if (err) throw err;
console.log(results);
});
✅ 입력값 검증: validator.js와 같은 라이브러리를 활용하여 입력값을 철저히 검사합니다.
2. 크로스 사이트 스크립팅(XSS) 방어
위험성
공격자가 웹 페이지에 악성 스크립트를 삽입하여 사용자 세션을 탈취하거나 피싱 공격을 수행할 수 있습니다.
방어 방법
✅ 출력 데이터 이스케이프(Escape):
const sanitizeHtml = require('sanitize-html');
const cleanInput = sanitizeHtml(userInput);
✅ Content Security Policy(CSP) 적용:
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"]
}
}));
✅ 입력값 검증 및 필터링: 사용자의 입력값을 정리하여 불필요한 스크립트가 삽입되지 않도록 합니다.
3. 크로스 사이트 요청 위조(CSRF) 방어
위험성
공격자가 사용자의 세션을 이용해 임의의 요청을 실행하여 비밀번호 변경, 송금 등의 악의적인 동작을 수행할 수 있습니다.
방어 방법
✅ CSRF 토큰 사용: csurf 모듈을 활용하여 요청을 보호합니다.
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);
app.post('/transfer', (req, res) => {
res.send(`CSRF Token: ${req.csrfToken()}`);
});
✅ SameSite 쿠키 설정: CSRF 공격을 막기 위해 쿠키를 동일 사이트에서만 사용할 수 있도록 설정합니다.
app.use(cookieSession({
name: 'session',
keys: ['secret'],
cookie: { secure: true, httpOnly: true, sameSite: 'strict' }
}));
4. 보안 헤더 설정
위험성
적절한 보안 헤더가 없을 경우 클릭재킹(Clickjacking), 데이터 스니핑 등의 공격에 취약할 수 있습니다.
방어 방법
✅ helmet 모듈을 활용하여 보안 헤더를 자동으로 추가합니다.
const helmet = require('helmet');
app.use(helmet());
✅ 개별적으로 보안 헤더 설정
- X-Frame-Options: 클릭재킹 방지
- Strict-Transport-Security (HSTS): HTTPS 강제 사용
- X-Content-Type-Options: MIME 스니핑 방지
app.use(helmet.frameguard({ action: 'deny' }));
app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }));
app.use(helmet.noSniff());
5. NoSQL 인젝션 방어
위험성
MongoDB와 같은 NoSQL 데이터베이스에서 인젝션 공격이 발생할 수 있습니다.
방어 방법
✅ 입력값을 JSON Schema를 이용하여 검증
✅ MongoDB 쿼리에 사용자 입력값 직접 사용 금지
const user = await User.findOne({ username: sanitize(userInput) });
✅ Mongoose의 lean() 함수 활용: 데이터를 읽기 전용으로 처리하여 공격을 방지합니다.
const user = await User.findOne({ email }).lean();
6. 의존성 보안 관리
위험성
취약한 패키지가 포함된 애플리케이션은 공격자가 악용할 가능성이 높습니다.
방어 방법
✅ npm audit 사용: 취약한 패키지를 자동으로 감지하고 수정합니다.
npm audit fix
✅ 의심스러운 패키지 검토: 설치 전에 npm view package 명령어로 패키지 정보를 확인합니다.
✅ 불필요한 패키지 제거: 오래된 패키지나 사용하지 않는 모듈은 제거합니다.
7. JWT(JSON Web Token) 보안
위험성
JWT를 안전하게 관리하지 않으면 인증 정보가 노출될 위험이 있습니다.
방어 방법
✅ 안전한 서명 알고리즘 사용 (RS256, ES256)
✅ JWT 만료 시간 설정
const jwt = require('jsonwebtoken');
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
✅ JWT를 HttpOnly 및 Secure 쿠키로 저장
res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'strict' });
8. 파일 업로드 보안
위험성
악성 파일 업로드를 통해 서버가 해킹될 수 있습니다.
방어 방법
✅ 파일 유형 제한
✅ multer를 활용한 파일 검증
const multer = require('multer');
const upload = multer({ dest: 'uploads/', limits: { fileSize: 1 * 1024 * 1024 } });
✅ 파일 실행 방지: 업로드 디렉토리에 실행 권한 제거
결론
Node.js 기반 웹 애플리케이션을 개발할 때, 위에서 언급한 보안 취약점을 철저히 방어하는 것이 중요합니다.
보안은 한 번 설정하고 끝나는 것이 아니라, 지속적인 관리와 업데이트가 필요합니다.
🚀 최고의 보안 전략은 예방입니다. 안전한 코드 작성 습관을 기르고, 최신 보안 패치를 적용하여 보안 취약점을 줄이는 것이 중요합니다.
'시큐어코딩 > JavaScript' 카테고리의 다른 글
🚀【시큐어코딩】자바스크립트 크로스사이트스크립트(XSS) 보안약점 대응방안 (0) | 2025.03.03 |
---|---|
🚀【시큐어코딩】자바스크립트 시큐어코딩 가이드, 보안취약점 예방방법 (0) | 2025.03.03 |
🚀【시큐어코딩】Django, Flask, Express.js, Node.js에서 SQL 삽입(SQL Injection) 방지 방법 (0) | 2025.03.02 |
🚀【시큐어코딩】웹 보안 필수! JavaScript 개발자를 위한 시큐어코딩 완벽 가이드 (0) | 2025.03.02 |