본문 바로가기

[Project] 프로젝트 삽질기30 (feat helmet 구성)

어가며

NestJS로 개발하면서, 외부로부터 오는 해커들의 공격에 어떻게 대응할 수 있을지 고민하곤 했습니다. 그때 helmet이라는 라이브러리를 알 수 있었습니다. NestJS에서 helmet을 보다 잘 활용하려면 어떻게 해야 할지 고민하기 위해 작성된 글입니다. 이 글은 [NODE / 보안] 📚 helmet 모듈 사용법 - 웹 보안은 내가 👮 글을 참고했습니다. 해당 링크 글을 보시면 더 잘 이해되실 겁니다.

 

 

 

 
 

NestJS Helmet

helmet 모듈은 웹서버를 외부의 공격으로부터 보호해주는 대표적인 노드 보안 모듈입니다. helmet 모듈은 다양한 HTTP 헤더를 자동 설정해줘서 서버 애플리케이션의 보안을 강화해줍니다. helmet을 활용하려면 모듈을 설치해주고, 선언만 하면 됩니다.

 

 

npm install helmet
app.use(helmet( { contentSecurityPolicy: false } )); // contentSecurityPolicy는 꽤 복잡한 설정이기 때문에 일단 꺼둔다. (후

 

 

helmet은 Express의 미들웨어 모듈이며, Helmet 또한 여러 미들웨어 모듈을 합쳐 놓은 미들웨어 패키지 모듈입니다. Express 기반 애플리케이션에서 HTTP Response Header를 설정하는 여러 개의 작은 미들웨어 함수 유형 모음인 것입니다. 각 미들웨어 함수들의 기능은 아래와 같습니다. 

 

 

 

// 기본 설정 기능 사용
app.use(helmet());
 
// or
 
// 특정 세부 기능 하나하나 설정할때 사용
app.use(helmet.contentSecurityPolicy());
app.use(helmet.crossOriginEmbedderPolicy());
app.use(helmet.crossOriginOpenerPolicy());
app.use(helmet.crossOriginResourcePolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.expectCt());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.originAgentCluster());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());

 

 

그럼, helmet에서 설정할 수 있는 기능을 더 자세히 알아보겠습니다. 먼저 helmet을 기본으로 설정하면 아래와 같은 기능만 설정이 구성됩니다.

 

 

  • dnsPrefetchControl
  • frameguard
  • hidePoweredBy
  • hsts
  • ieNoOpen
  • noSniff
  • xssFilter

 

 

먼저 기본으로 설정되는 요소들을 살펴보겠습니다.

 

 

 

 

 

 

Helmet 기본 설정

helmet의 기본 설정 값을 알아보겠습니다.

 

 

1. dnsPrefetchControl

도메인이 미리 로딩되는 Prefetch에 대해 컨트롤하기 위해 X-DNS-Prefetch-Control 헤더를 설정합니다. 대부분의 브라우저는 성능을 향상하기 위해서 페이지의 링크에 대한 DNS 레코드(record)를 미리 추출(prefetch)합니다. 이러한 방식으로써 사용자가 링크를 클릭할 때 대상에 대한 IP가 이미 알려져 있습니다. 

 

이로 인해 유발될 수 있는 상황은 DNS 서비스가 과도하게 사용되는 것 (큰 웹사이트를 소유하고 있는 경우, 수백만 명이 방문하는 경우 등), 개인 정보 보호 문제(도청자 한 명이 당신이 특정 페이지에 있다고 추론할 수 있음), 페이지 통계 변경(일부 링크는 방문되지 않은 경우에도 방문된 것으로 나타날 수 있음)이 있습니다.

 

즉, 보안 요구가 높은 경우에는 성능 저하를 감수하고서 DNS 프리 페치를 비활성화할 수 있습니다. 

 

 

2. frameguard

X-Frame-Options 헤더를 설정하여 클릭재킹의 공격을 방지합니다. 클릭재킹이란 사용자가 자신이 클릭하고 있다고 인지하는 것과 다른 것을 클릭하도록 하여 속이는 해킹 기법입니다. 속이기 위해 보이지 않는 레이어에 보이지 않는 버튼을 만드는 방법이 있습니다. 

 

서버가 제공한 페이지에 서버의 허락 없이 악의 적인 페이지를 로드한 frame 혹은 iframe 태그를 넣을 수 있기 때문에 악의적인 콘텍스트에서 페이지에 클릭재킹을 실행할 수 있기 때문입니다. 이러한 context에서, 해커는 서버가 최초에 제공한 페이지 위에다가 숨겨진 레이어를 넣을 수 있습니다. 그래서 숨겨진 버튼을 사용하여 나쁜 스크립트를 실행할 수도 있는 것입니다.

 

따라서 iframe에 넣는 허용된 사이트만을 제한하는 개념입니다. 

 

 

3. hidePoweredBy

응답 헤더에 있는 X-Powerd-By에 서버 소프트웨어가 표기되는데 이를 숨겨줍니다. 이 정보는 악의적으로 활용될 가능성이 높기에 헬멧을 통해서 제거해 주는 것이 좋습니다. 

 

 

4. hsts

서버에 대한 안전한(SSL/TLS를 통한 HTTP) 연결을 적용하는 Strict-Transport-Security 헤더를 설정합니다. 브라우저에게 HTTPS만을 통해서 사이트에 액세스 할 수 있도록 요청합니다. (기본 false)

 

사용자가 특정 사이트에 접속할 때 해당 사이트가 HTTPS를 지원하는지, 하지 않는지를 미리 모르는 경우가 대부분입니다. 그렇기에 브라우저는 디폴트로 HTTP로 먼저 접속을 시도합니다. 이때 HTTPS로 지원되는 사이트였다면 301 Redirect나 302 Redirect를 응답하여 HTTPS로 다시 접속하도록 합니다.

 

하지만 이때 해커가 중간자 공격을 하여, 중간에 프락시 서버를 두고, 나와 해커 사이에서는 HTTP 통신을 하고, 해커와 웹사이트 사이에선 HTTPS 통신을 한다면, 개인 정보가 HTTP 프로토콜을 통해 해커에게로 전해지는 참사가 일어납니다.

 

이러한 공격을 SSL Stripping이라고 하며 이런 공격을 방지하기 위해 HSTS를 설정합니다. 

 

 

5. ieNoOpen

X-Download-Options 헤더를 설정하여 ie8 이상에서만 사용할 수 있도록 설정합니다.

 

 

6. noSniff

X-Content-Type-Options를 설정하여 선언된 콘텐츠 유형으로부터 벗어난 응답에 대한 브라우저의 MIME 스니핑을 방지합니다. MIME란 Multipurpose Internet Mail Extensions의 약자로 클라이언트에게 전송된 문서의 다양성을 알려주기 위한 포맷입니다. 브라우저는 리소스를 내려받을 때 MIME 타입을 보고 동작하기에 정확한 설정이 중요합니다.

 

MIME 스니핑이란 브라우저가 특정 파일을 읽을 때 파일의 실제 내용과 Content-Type에 설정된 내용이 다르면 파일로부터 형식을 추측하여 실행하는 것인데, 편리함을 위한 기능이지만 공격자에게 악용될 가능성이 있습니다. 

 

 

7. xssFilter

X-XSS-Protection 헤더를 0으로 설정하여 XSS 공격 스크립트를 비활성화하여 예방합니다. 

 

 

 

 

 

 

Helmet 추가 설정

helmet에서 추가적으로 설정할 수 있는 것들도 알아보겠습니다.

 

 

 

1. contentSecurityPolicy(CSP)

콘텐츠 보안 정책 설정 및 구성을 통해 의도하지 않은 내용이 페이지에 삽입되는 것을 방지합니다. Content-Security-Policy 헤더를 설정해서 XSS나 교차 사이트 인젝션 등을 방지합니다. 다른 사이트의 script를 불러오는 것도 막기 때문에 헬멧 적용 전 별도로 설정이 필요합니다.

 

만약 CSP를 사용하면 웹사이트의 http 응답에 CSP 헤더가 추가됩니다. CSP 헤더가 존재할 경우, 브라우저는 CSP 헤더에 언급되지 않은 리소스들을 다음과 같이 로드하지 않게 됩니다. Helmet의 CSP 기본 설정은 'self' 즉, 자신의 웹사이트에 존재하는 리소스들만을 허용하기 때문입니다. 

 

 

그래서 내 웹서비스에 CDN 등의 외부 사이트의 소스를 이용할 경우, 다른 웹사이트에서 이미지 로드하는 경우, 심지어 인라인 스크립트로 자바스크립트 코드를 작성한 경우 다음과 같이 모두 에러를 발생시키게 됩니다. 

 

 

CSP는 신뢰할 수 있는 컨텐츠 소스의 화이트리스트를 정의하는 방식으로 작동합니다. 웹 페이지에 필요한 각 리소스 유형(스크립트, 스타일시트, 글꼴, 프레임, 미디어 등)에 대해 구성할 수 있습니다. 사용 가능한 지침(directive)이 여러 개 있으므로 웹 사이트 소유자는 세부적인 제어를 할 수 있습니다. 따라서 이를 해결하기 위해서는 웹 페이지가 사용할 신뢰할 수 있는 리소스들의 도메인을 CSP 헤더에 추가해야 합니다. contentSecurityPolicy 옵션 설정법은 다음과 같습니다.

 

 

const cspOptions = {
  directives: {
    // 헬멧 기본 옵션 가져오기
    ...helmet.contentSecurityPolicy.getDefaultDirectives(), // 기본 헬멧 설정 객체를 리턴하는 함수를 받아 전개 연산자로 삽입
    
    /* 
    none : 어떳 것도 허용하지 않음
	self : 현재 출처에서는 허용하지만 하위 도메인에서는 허용되지 않음
	unsafe-inline : 인라인 자바스크립트, 인라인 스타일을 허용
	unsafe-eval	: eval과 같은 텍스트 자바스크립트 메커니즘을 허용 
    */
    // 구글 API 도메인과 인라인 스크립트, eval 스크립트를 허용
    "script-src": ["'self'", "*.googleapis.com", "'unsafe-inline'", "'unsafe-eval'"],
 
    // 다음과 카카오에서 이미지 소스를 허용
    'img-src': ["'self'", 'data:', '*.daumcdn.net', '*.kakaocdn.net'],
    
    // 소스에 https와 http 허용
    "base-uri" : ["/", "http:"],
  }
}
 
// Helmet의 모든 기능 사용. (contentSecurityPolicy에는 custom option 적용)
app.use(helmet({
  contentSecurityPolicy: cspOptions,
}));

 

 

 

 

 

2. crossOriginEmbedderPolicy

Cross-Origin-Embedder-Policy 헤더를 require-corp로 설정합니다.

 

 

3. crossOriginOpenerPolicy

Cross-Origin-Opener-Policy 헤더를 설정

 

 

4. crossOriginResourcePolicy

Cross-Origin-Resource-Policy 헤더를 설정

 

 

5. exprectCt

Expect-CT 헤더를 설정하여 SSL 인증서 오발급을 예방

 

 

6. originAgentCluster

Origin-Agent-Cluster 헤더를 설정하여 오리진 간 문서를 별도 에이전트 클러스터로 분리

 

 

 

7. permittedCrossDomainPolicies

X-Permitted-Cross-Domain-Policies 헤더를 설정하여 크로스 도메인 콘텐츠 정책을 설정합니다. X-Permitted-Cross-Domain-Policies 헤더는 일부 클라이언트에 도메인 간 콘텐츠 로드에 대한 도메인 정책을 처리합니다. 

 

 

 

8. referrerPolicy

참조 referrer 헤더를 숨깁니다. 

 

 

 

 

 

 


 

 

 

 

 

 

마치며

앞으로도 팀의 발전을 돕는 개발자가 되기 위해 노력하려 합니다. 팀에 필요한 부분이 무엇일지 고민하면서, 팀에 도움이 된다면, 열심히 공부해서 실무에 적용할 수 있는 개발자가 되기 위해 노력하고 싶습니다. 팀의 성장에 기여할 수 있는 개발자가 되겠습니다. 

 

 

 

 


 

 

 

 

 

참고 및 출처

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

GitHub - helmetjs/helmet: Help secure Express apps with various HTTP headers

Help secure Express apps with various HTTP headers - GitHub - helmetjs/helmet: Help secure Express apps with various HTTP headers

github.com

 

[NODE / 보안] 📚 helmet 모듈 사용법 - 웹 보안은 내가 👮

helmet 모듈 helmet 모듈은 서버에서 다양한 HTTP 헤더를 자동 설정을 통해 서버 어플리케이션의 보안을 강화해주는 대표적인 노드 보안 모듈이다. 헬멧을 써서 내 머리를 보호하듯이, 내 웹서버를

inpa.tistory.com

 

CSP(Content Security Policy).. 날 힘들게 만드는구나

비루한(...) 프론트엔드 실력으로 HTML과 스크립트만으로 프로젝트를 진행하던 와중, Script 태그 안에다가 모든것을 박아넣어 동작을 구현하려 했다.이런 식으로... 하지만 스크립트가 동작하지 않

velog.io