import { validationResult } from 'express-validator';
export const validate = (req, res, next) => {
const errors = validationResult(req); // req ★
if (errors.isEmpty()) {
return next();
}
res.status(400).json({ message: errors.array()[0].msg });
};
*isEmpty
: 하나라도 들어 있다면, 즉 비어 있지 않다면 에러!*
import jwt from 'jsonwebtoken';
import * as userRepository from '../data/auth.js';
import { config } from '../config.js'
const AUTH_ERROR = { message: 'Authorization' };
export const isAuth = async (req, res, next) => {
let token;
// 1. check the cookie (브라우저용)
// Cookie (for Browser Client)
token = req.cookies['token'];
// 2. check the header (브라우저 외 클라이언트용)
// Header (for Non-Browser Client)
if(!token) {
const authHeader = req.get('Authorization');
if ((authHeader && authHeader.startsWith('Bearer'))) {
token = authHeader.split(' ')[1];
// Bearer $2sdl@#$@$SDFSAF... : 토큰 데이터를 가져오기 위해 [1] 배열 index
}
}
if(!token) {
return res.status(401).json(AUTH_ERROR);
}
jwt.verify(
token,
config.jwt.secretKey,
async (error, decoded) => { // decoded : secret 키로 token 을 인증하면 받는 값
if (error) {
return res.status(401).json(AUTH_ERROR);
}
const user = await userRepository.findById(decoded.id);
if (!user) {
return res.status(401).json(AUTH_ERROR);
}
req.userId = user.id; // req.customData (request에 사용자 지정 데이터를 추가할 수 있다.)
req.token = token;
next();
}
)
}
어떤 해당 서비스를 이용하고자 할 때 유저 정보가 존재하는지 확인하는 미들웨어로, 로그인이 된 사용자만 서비스를 이용하도록 하기 위해서 사용자 정보(쿠키에 저장된 토큰의 데이터 정보) 를 확인하는 과정을 거쳐서 확인된 사용자만 서비스를 요청에 대한 응답을 받을 수 있다. → 쿠키 & JWT 활용
import bcrypt from 'bcrypt';
import { config } from '../config.js';
export const csrfCheck = async (req, res, next) => {
if (
req.method === 'GET' ||
req.method === 'HEAD' ||
req.method === 'OPTIONS'
) {
return next();
}
const csrfHeader = req.get('dwitter-csrf-token');
if (!csrfHeader) {
console.warn('Missing required "dwitter-csrf-token" header.', req.headers.origin);
return res.status(403).json({ message: 'Faild CSRF check' });
}
try {
const valid = await validateCsrfToken(csrfHeader);
if (!valid) {
console.warn(
'Value provided in "dwitter-csrf-token" header does not validate',
req.headers.origin,
csrfHeader
);
return res.status(403).json({ message: 'Failed CSRF check' });
}
next();
} catch (error) {
console.log(error);
return res.status(500).json({ message: 'Something went wrong' });
}
};
async function validateCsrfToken(csrfHeader) {
return bcrypt.compare(config.csrf.plainToken, csrfHeader);
};
정상적인 경로를 통한 액션만 동작하게끔 하기 위하여 브라우저와 서버간의 서로 대칭키를 가지고 있게하여 해당 키가 없다면 POST, PUT, DELETE 같은 액션을 할 수 없도록 한다.
→ CSRF Attack 에 대한 보안방법
import rateLimit from 'express-rate-limit';
import { config } from '../config.js';
// 호출 시 middleware callback 함수 반환
export default rateLimit({
windowMs: config.rateLimit.windowMs, // ms 단위만 가능하다.
max: config.rateLimit.maxRequest, // 위 시간동안 처리가능한 요청 개수 (요청이란 GET, POST, PUT, DELETE 등의 요청을 말한다.)
keyGenerator: (req, res) => 'dwitter-limit', // 식별을 위한 key 이름 지정
});