Express?? 너무나 당연히 썼던 서버 프레임워크 - (2)

2020. 11. 5. 03:11Project-Review

프로젝트 발표날이었다. 각 팀은 자신들의 결과물을 발표하였는데, 한 팀이 해킹으로 인한 백업 파일과 로그의 중요성을 얘기하였다. 이 생각은 자연스레 내가 쓰고 있는 express는 안전한가?로 이어졌다. 물론 지금의 실력으로 작정하고 들어오는 것들을 막을 수 있는 능력은 없지만, 적어도 권장하는 방법이나 추천하는 것은 있지 않을까? 하고 공식문서에 들어가 봤더니 Advanced Topics - Security best practices라는 주제가 있어 정리해보고자 한다. 앞으로 express가 있다면 이런 방법으로 써보기 위해 미리 알아보려고 한다.

 

1. Don't Use deprecated or vulnerable version of Express - 권장되지 않는 방법을 사용하지 말고, 취약한 버전을 사용하지 말라

보안과 퍼포먼스는 계속 변하기 때문에, 2.x나 3.x는 version 4로 옮기라는 말이다. 위와 함께 migration 방법이 있는데 이는 필요할 때 알아볼 것이다. 또한 다음과 같은 변화도 있었다.

 

종속성의 제거로 이제 미들웨어를 직접 추가해야한다 

Express 4는 더 이상 Connect에 의존하지 않으며 express.static기능을 제외한 모든 내장 미들웨어를 코어에서 제거한다 . 즉, Express는 이제 독립적 인 라우팅 및 미들웨어 웹 프레임 워크이며 Express 버전 관리 및 릴리스는 미들웨어 업데이트의 영향을받지 않는다.

 - 모듈 설치 : npm install --save <module-name>
 - 앱에서 모듈이 필요 : require('module-name')
 - 설명서에 따라 모듈을 사용 : app.use( ... )

 

(2) app.use의 매개변수 허용 : 이제 미들웨어에서도 다음과 같이 매개변수를 받을 수 있다.

app.use('/book/:id', function (req, res, next) {
  console.log('ID:', req.params.id)
  next()
})


(3) app.route()방법 : 체인 방식으로 이제 중복성 방지와 모듈식 생성이 가능하다

app.route('/book')
  .get(function (req, res) {
    res.send('Get a random book')
  })
  .post(function (req, res) {
    res.send('Add a book')
  })
  .put(function (req, res) {
    res.send('Update the book')
  })

개인적으로 이건 프로젝트 때 알았으면 굉장히 좋았을 것 같다. 보기에도 직관적이고, 분명히 CRUD 관련 API를 만들때도 간결하게 쓸 수 있었을텐데 다음에 꼭 써봐야겠다.


(4) 미들웨어 사용의 변경이 있습니다. - 이건 직접 표를 보기로 하자. 그 외에도 기타 변경사항을도 확인하면 좋을 것 같다.

 

2. TLS를 사용하자!!! - (그런데 TLS가 뭐지???)

TLS란 Transport Layer Security의 약자로 전송 계층에서의 보안을 뜻한다. SSL의 다음 단계라고도 얘기하며,  클라이언트에서 서버로 갈 때 encrypt를 해서 보낸다는 것 같다. 클라이언트에서 서버로 보내는 것은 탈취를 당하기 쉽고 Ajax나 POST는 언뜻보면 암호화를 하는 것 같지만, packet snippet이나 man-in-the-middle attacks에는 취약하다고 나와있다. 간략하게 어떤 뜻인지만 알고가자.

 

packet snippet : wikipedia에서 한줄로 가장 되어있는 걸 찾아보니 이건 것 같다.

Packet capture  is the process of intercepting and logging traffic.

 

네트워크 전송시에는 데이터 스트림 플로우가 있는데, 이때 각각의 패킷을 가져가 정보를 raw data로 변경하거나 RFC와 같은 것으로 분석을 한다는 말 같다.

 

man-in-the-middle attacks

the attacker secretly relays and possibly alters the communications between two parties who believe that they are directly communicating with each other.

그리고 NginX에서의 TLS사용 방법도 나와 있지만, 나는 프로젝트때 사용해보질 않았다. 만약 사용해본다면 이때 보안 측면에서 사용해보는게 좋을 것 같다. 또한 Lets' Encrypt란 free certificate라는 편한 툴도 추천해주고 있으니 필요한 사람은 사용해보도록 하자.

 

3. Helmet을 사용하자

Helmet은 작지만 유명한 middleware로 HTTP response header에 보안과 관련된 설정을 해준다. cors도 있지만, Express에서는 Helmet이 좋은가보다. 공식문서를 보면 다양한 설정들이 나와있는데...솔직히 그냥 자신이 프로젝트한다면 이걸 다 지킬까???라는 생각이 든다. 그래서 나 같은 사람이 있는 것인지 다음과 같이 최소한! 이거는 하라고 말해준다.

app.disable('x-powered-by')

 

4. Use Cookie Securely

쿠키와 세션은 개발에 있어서 로그인에서도 사용하고, 참 많은 방법으로 사용하는 것 같다. 그렇지만, 이 또한 보안이 필요한 것이다. 물론 민감하지 않거나 알아도 쓸모 1도 없는(?) 그런거라면 상관이 없겠지만, 알면 매우 유용할 것 같다.

우선 두가지의 Version 3.X부터 변경된 사항들이 두개 있다. 참고로 나는 express-session과 cookie-session을 깔아 쓰는 건 줄 알고만 있었는데 밑에 내용을 보고 약간 촌스럽게 진행한게 아닌가 하고 멍했다.

 

  • express-session that replaces express.session middleware built-in to Express 3.x.
  • cookie-session that replaces express.cookieSession middleware built-in to Express 3.x.

그리고 이 두가지의 차이점도 설명해 주고 있다. express-session은 서버에 데이터를 저장하는 것으로, 쿠키에서는 Session ID만 저장을 한다. 필요시 session-store를 만들 필요가 있으며 이는 mysql-session-store나 다양한게 많으므로 알아서 사용하자. cookie-session은 세션 키보다는 세션 자체를 serialize해놓는다. 그래서 원시적인 데이터나 작은 용량의 것들을 사용하는 것으로 권장하고 있다.

 

(1) Don’t use the default session cookie name

X-Powerd-By와 같이 보안 문제가 생길 수 있으므로, generic cookie name 사용을 권장한다.

var session = require('express-session')
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 's3Cur3',
  name: 'sessionId'
}))

 

(2) 옵션 설정을 잘하자

개인적으로도 express-session이나 cookie-session은 사용이 아마 정말 많을 것이라고 생각한다. 그래서 Express에서 얘기해주는 5가지는 꼭 설정해주기로 하자. 꼭 프로젝트에서 써봤으면 좋겠다.

  • secure - Ensures the browser only sends the cookie over HTTPS.
  • httpOnly - Ensures the cookie is sent only over HTTP(S), not client JavaScript, helping to protect against cross-site scripting attacks.
  • domain - indicates the domain of the cookie; use it to compare against the domain of the server in which the URL is being requested. If they match, then check the path attribute next.
  • path - indicates the path of the cookie; use it to compare against the request path. If this and domain match, then send the cookie in the request.
  • expires - use to set expiration date for persistent cookies.

아래는 cookie-session의 예시로 나와 있는 것이다.

var session = require('cookie-session')
var express = require('express')
var app = express()

var expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
  name: 'session',
  keys: ['key1', 'key2'],
  cookie: {
    secure: true,
    httpOnly: true,
    domain: 'example.com',
    path: 'foo/bar',
    expires: expiryDate
  }
}))

 

5. 무차별 공격 대응(Brute-force Attack)

무차별 공격 대응이란? : wikipedia에서 다음과 같이 설명해주고 있다. 정말 무식한?방법이지만 존버는 이긴다고 하지 않았는가라는 생각이 머리속에 돌았다.

 

 

그리고 이에 대한 방안으로 두가지를 제시한다.

  1. The first is number of consecutive failed attempts by the same user name and IP address.
  2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day.

즉, 사용자 이름과 IP 주소에 대해 연속 실패 횟수나 누적 실패 횟수가 있으면 이는 무차별 공격 대응으로 봐야한다는 것이다. 그리고 package로 rate-limiter-flexible를 추천해주니 쓸일이 있으면 써보도록 하자.

 

6.Ensure your dependencies are secure

npm install -g snyk
$ cd your-app

//어플리케이션의 취약성을 확인하기 위한 테스트
snyk test

//발견 된 취약점을 수정하기 위해 업데이트 또는 패치를 적용하는 프로세스를 안내해준다.
snyk wizard

 

7. 그 외

물론 이것만 한다고 해서 '난 절대방어야!'라고 할 수 는 없겠다. 그래서 그 외로 걱정할 것들이 있다면 제안을 해주고 있다.

 

  • Use csurf middleware to protect against cross-site request forgery (CSRF).
  • Always filter and sanitize user input to protect against cross-site scripting (XSS) and command injection attacks.
  • Defend against SQL injection attacks by using parameterized queries or prepared statements.
  • Use the open-source sqlmap tool to detect SQL injection vulnerabilities in your app.
  • Use the nmap and sslyze tools to test the configuration of your SSL ciphers, keys, and renegotiation as well as the validity of your certificate.
  • Use safe-regex to ensure your regular expressions are not susceptible to regular expression denial of service attacks.