Yunseok's Dev Blog

배운 것을 적는 블로그입니다.

Express security best practices

Overview

  • 개발용 환경과 배포용 환경은 조금 다르게 설정될 수 있습니다. 개발용 환경에서는 로그가 출력되도 상관없지만 배포용에서는 그러면 안됩니다. 그리고 개발용 환경에서는 안전성, 확장성이나 성능은 고려하지 않아도 되지만 배포용에서는 고려해야 합니다.

Don’t use deprecated or vulnerable versions of Express

  • Express 2.x 와 3.x는 더이상 관리되지 않습니다. 이러한 버전들에서 보안과 성능은 보장되지 않습니다. 이러한 버전을 사용하지말고 버전을 업그레이드 해야합니다.
  • 자세한 보안 업데이트 내용들은 Security updates page을 확인하세요.

Use TLS

Use Helmet

  • Helmet은 이미 잘알려진 HTTP 헤더들의 취약점을 보완해줍니다.
  • 사실 Helmet은 단순히 보안과 관련된 HTTP응답 헤더들을 설정해주는 더 작은 미들웨어들의 모음입니다.
npm install --save helmet
// ...

var helmet = require('helmet')
app.use(helmet())

// ...

At a minimum, disable X-Powered-By header

  • Helmet을 사용하고싶지 않다면 적어도 X-powered-By헤더는 사용하지 못하게 해야합니다. 공격자는 이 헤더를 통해 서버가 Express로 가 동작하고 있다는걸 알 수 있고 공격을 할 수 있습니다.
app.disable('x-powered-by')

Use cookies securly

  • 쿠키가 앱을 악용하지 못하도록하려면 기본 세션 쿠키 이름을 사용하지 말고 쿠키 보안 옵션을 적절히 설정해야 합니다.
  • 두 개의 자주 사용되는 모듈이 있습니다.
    • express-sessionexpress.session을 대체합니다.
    • cookie-session은 내장된 express.cookieSession을 대체합니다.

express-session

  • express-session은 데이터를 서버에 저장하고 session ID만 쿠키에 저장합니다.
  • 기본적으로는 in-memory storage에 저장하고 production환경에서 사용하기에는 적합하진 않습니다.
  • production환경에서는 확장가능한 저장소가 필요합니다.
  • 가능한 목록을 확인하려면 compatible session stores을 확인하세요.
  • cookie-session은 전체 session데이터를 쿠키에 저장합니다.
  • 상대적으로 작은 데이터나 쉽게 인코딩할 수 있는 원시타입을 저장하는데 많이 사용됩니다.
  • 4093byte를 초과하지 않아야합니다.
  • 쿠키는 client에서 볼 수 있는 데이터입니다. 따라서 민감한 정보를 세션에 저장해야 할 경우에는 express-session으로 저장하는 것이 좋습니다.
  • 기본 세션 쿠키 이름을 사용하는 것은 공격에 취약할 수 있습니다.
  • x-powered-by헤더 문제처럼 해커가 이를 이용하여 공격 대상으로 지정할 수 있습니다.
  • 이 문제를 피하기 위해 일반적인 쿠키 이름을 사용해야 합니다.
var session = require('express-session')
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 's3Cur3',
  name: 'sessionId'
}))

보안을 위해 다음 옵션들을 설정하는게 좋습니다.

  • secure - 브라우저가 쿠키를 오직 HTTPS를 통해서만 보낼 수 있도록 합니다.
  • httpOnly - client JavaScript가 아닌 오직 HTTP(S)로만 쿠키를 전송할 수 있도록 합니다. cross-site-scripting공격을 방지할 수 있습니다.
  • domain - 쿠키의 도메인을 지정합니다. 요청한 도메인과 서버에서 지정한 도메인과 일치하는지 확인합니다. 같은 도메인에서만 요청할 수 있도록 강제합니다.
  • path - 쿠키의 path를 지정합니다. 요청한 path와 비교할 때 사용됩니다. 둘이 일치해야만 쿠키를 전송할 수 있습니다.
  • expires - 쿠키의 만료시간을 설정합니다.
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
  }
}))

Prevent brute-force attacks against authorization

  • 로그인 endpoint는 민감한 데이터를 더 안전하게 보관하기 위해 안전해야 합니다.
  • 같은 유저가 같은 IP주소로 실패하는 요청을 연속적으로 요청하는 것을 방지해야 합니다.
  • 하루에 같은 IP 주소로 실패하는 요청을 할 경우 블락해야합니다.
  • rate-limiter-flexible 패키지는 이런 기술들을 쉽게 구현할 수 있도록 도와줍니다. 예제는 여기를 참고하세요.

Ensure your dependencies are secure

  • npm은 패키지들을 편리하게 관리할 수 있도록 도와주지만 아무리 다른 보안을 강하게 한다고 하더라도 한개라도 보안에 약한점이 존재한다면 위험에 노출됩니다.
  • npm 버전6 이상부터는 자동으로 설치할 때 체크하고 수동으로 npm audit을 통해 점검할 수 있습니다.
npm audit

Avoid other known vulnerabilities

Additional considerations

Sources