사용자의 입력을 절대 신뢰하지 마라!

개발자가 모든 일반 유저가 본인의 의도대로 입력값을 주어줄 것이라는 믿음을 갖고 있다면 다양한 문제가 발생할 수 있습니다. 사용자가 실수로 의도치 않은 값을 입력할 경우, 예기치 않은 오류가 발생할 수도 있고 사용자가 고의로 악의적인 값을 입력한다면 여러분의 코드에 보안 문제가 발생할 수 있습니다. 따라서 이러한 문제를 근본적으로 막아줄 수 있는 방법이 필요합니다.

사용자의 입력값을 검사하자

일반 유저의 값을 신뢰하지 않는다면 무엇을 해야할까요? 사용자가 입력하는 값을 철저히 검사하는 방법이 있습니다. npm에서 사용자의 입력을 검증하는 수많은 패키지를 확인할 수 있습니다. 이 글에서는 여러 패키지 중에서 express-validator를 살펴보겠습니다.

express-validator

설치 방법

1
npm install express-validator

사용 방법

간단히 새로운 유저를 데이터베이스에 추가하는 코드를 보겠습니다.

1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.use(express.json());
app.post('/user', (req, res) => {
User.create({
username: req.body.username,
password: req.body.password
}).then(user => res.json(user));
});

이런 코드는 사용자의 입력값을 아무런 검증 없이 바로 데이터 베이스에 추가 하게 됩니다. 앞서말한 문제들을 해결하기 위해 express-validator를 이용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 검증을 위한 코드를 제외하고 생략
const { body, validationResult } = require('express-validator');

app.post('/user', [
// username은 이메일 주소
body('username').isEmail(),
// 비밀번호는 최소 5자 이상
body('password').isLength({ min: 5 })
], (req, res) => {
// 이 요청에서 유효성 오류를 찾아 편리한 기능이 있는 객체로 wrapping합니다.
const errors = validationResult(req);
if (!errors.isEmpty()) {
// 검증에 문제가 있는 경우, 다음 작업을 진행하지 않고 유저에게 에러 정보를 반환합니다.
return res.status(400).json({ errors: errors.array() });
}

// 바로 위 조건에서 검증에 문제가 있는경우 return 되었기 때문에, if문 이후로는 데이터가 검증된 상태임을 의미합니다.
User.create({
username: req.body.username,
password: req.body.password
}).then(user => res.json(user));
});

위의 코드에 대한 설명은 주석을 참고하시기 바랍니다. 위의 예시에서는 isEmail()과 같은 표준 validator를 이용하였습니다. 이 외의 표준 validator는 아래와 같습니다.

  • isInt: 주어진 문자열이 정수인지 확인합니다.
  • isEmail: 주어진 문자열이 이메일 형식인지 검증합니다.
  • contains: 주어진 문자열을 포함하는지 검증합니다.
  • equals: 문자열이 일치하는지 검증합니다.
  • isCreditCard: 주어진 문자열이 신용카드 번호인지 검증합니다.
  • isDate: 주어진 문자열이 유효한 날자 형식인지 검증합니다. 예) [2002-07-15, new Date()].
  • isEmpty: 주어진 문자열의 길이가 0인지 검증합니다.

이외의 매우 다양한 표준 validator가 존재함으로 더 많은 validator여기를 확인해주세요.

의도되지 않은 입력값에 의해 검증에 실패하는 경우, 서버는 아래와 같이 응답합니다.

1
2
3
4
5
6
7
{
"errors": [{
"location": "body",
"msg": "Invalid value",
"param": "username"
}]
}

표준 validator이외에 커스텀 validator를 이용하는 방법을 알고싶다면 다음 게시글을 확인해주세요.