...
hpp (HTTP Parameter Pollution) 모듈
hpp (HTTP Parameter Pollution) 는 Express의 중복 이름 파라메터 공격을 방어해주는 모듈이다.
Express가 동일한 이름을 가진 파라메터들이 있을 경우 Array로 만들어주는데, 이때 의도치 않은 동작을 하도록 외부에서 공격할 수 있는 보안 문제가 될 수 있기 때문이다. (입력 데이터 검증을 회피하거나 앱 크래시를 유발)
만일 다음과 같은 url을 요청한다고 해보자.
https://localhost:9876/api/api1?keyName=data1&keyName=data2
url 쿼리스트링에 같은 keyName 키가 두개를 썼다.
이렇게 중복한 키가 있을경우, 위에서 언급했듯이 익스프레스는 배열로 만들어버린다.
// req.query
{
keyName: ["data1", "data2"]
}
따라서 누군가 서버 코드의 구조를 파악하고 악의적으로 GET 요청 쿼리스트링에 위험동작을 유발시킬수 있는 parameter를 넣게되면 이 데이터가 그대로 서버로 넘어가 문제를 일으킬수 있기 때문에, hpp 모듈은 여러개의 query parameter로 전달된 값들이 모두 무시되고 단 한개의 값만 담겨지게 만든다.
// req.query
{
keyName: "data2"
}
hpp 사용법
사용하는 에디터의 터미널에서 아래와 같이 hpp 모듈을 설치해주고 간단하게 선언만 해주면 된다. (배포용 모듈)
> npm install hpp # hpp 설치
// 바디 파서 보다 아래에 위치해야 한다.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// ...
// hpp는 개발환경에서는 필요없는 모듈이니 배포환경에서만 활성
if (process.env.NODE_ENV === 'production') {
app.use(morgan('combined'));
app.use(helmet( { contentSecurityPolicy: false } )); // helmet 모듈
app.use(hpp()); // hpp 모듈
} else {
app.use(morgan('dev'));
}
이렇게 hpp() 를 설정해주게 되면, GET 요청과 POST 요청으로 오는 파라미터 값들이 다음과 같이 객체에 담겨지게 된다.
GET 요청 (req.query)
기본적으로 hpp는 req.query 의 최상위 파라메터가 Array인지 체크한다.
만약 파라메터가 Array라면 req.queryPolluted 로 이동시켜버리고 req.query 에는 가장 마지막 쿼리스트링 값을 할당한다.
GET /search?firstname=John&firstname=Alice&lastname=Doe
→
req: {
query: {
firstname: 'Alice', // 기본적으로 마지막으로 온 키 파라미터 값을 허용한다
lastname: 'Doe',
},
queryPolluted: {
firstname: [ 'John', 'Alice' ] // queryPolluted라는 객체를 새로 만들어서 요청된 데이터를 백업한다.
}
}
만일 이러한 기본 필터 기능을 해제하고 싶을 때에는 다음과 같이 옵션을 추가해준다
app.use(hpp({ checkQuery: false }))
POST 요청 (req.body)
GET 요청 뿐만 아니라 body에 값을 넣어 요청하는 POST요청 역시 필터링 할 수 있다.
만약 body 파라메터가 Array라면 req.bodyPolluted 로 이동시켜버리고 req.body 에 같은 키의 가장 마지막 파라미터 값을 할당한다.
POST firstname=John&firstname=Alice&lastname=Doe
→
req: {
body: {
firstname: 'Alice',
lastname: 'Doe',
},
bodyPolluted: {
firstname: [ 'John', 'Alice' ]
}
}
만일 이러한 기본 필터 기능을 해제하고 싶을 때에는 다음과 같이 옵션을 추가해준다
app.use(hpp({ checkBody: false }))
화이트리스트(White list) 등록
만일 특정 파라메터가 Array로서 전달되는 것을 허용하고자 한다면 아래와 같이 hpp middleware를 추가해주면 된다.
app.use(hpp());
// filter 라는 키 파라미터는 이제 Array로 받을 수 있다.
app.use('/search', hpp({ whitelist: [ 'filter' ] }));
GET /search?package=Helmet&package=HPP&filter=nodejs&filter=spring
→
req: {
query: {
package: 'HPP', // // 화이트리스트 등록이 안되서 package 키는 단일로 온다
filter: [ 'nodejs', 'spring' ], // 화이트리스트 등록이 되서 filter 키는 배열로 온다
},
queryPolluted: {
package: [ 'Helmet', 'HPP' ]
}
}
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.