본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Node.js/Nestjs (Nest.js)

Nestjs 프레임워크 서버(XSS 방지, Cross Site Scripting) -11

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2022. 8. 29.

# XSS(cross site scripting)

크로스 사이트 스크립트 변조(cross site scripting, xss)는 공격자가 상대방의 브라우저에 스크립트가 실행되도록 해 사용자의 세션을 가로채거나, 웹사이트를 변조하거나, 악의적 콘텐츠를 삽입하거나, 피싱 공격을 진행하는 것을 말합니다.

아래와 같은 파라미터를 통해서 게시글을 등록하였다고 가정하여 봅니다.

http://127.0.0.1:3000/등록?id=asdf&name=<script> while(alert(1234)) </script>&desc=aaa

 

id,  name, desc 값으로 해당 내용이 등록이 되고 등록된 내용을 사용자가 본다고 가정하면, while( alert(1234) ) 라는 명령어에 의해서 다른 사용자가 게시글을 보게 될 경우에 경고창이 1234 라는 숫자와 함께 끝나지 않고 계속해서 뜰 것 입니다.

이러한 방법으로 해커는 악성코드나 악성스크립트를 심어서 다른 사용자의 정보를 탈취하는 데 사용합니다.

그러므로 xss 방지하는 기능이 항상 준비 되어야 하겠습니다.

 

 

#1. 파이프(pipe)를 이용한 대비

 

파이프 기능은 컨트롤러에 요청(Request)이 도달하기 전 동작하는 클래스 입니다.

인터셉터, 가드와 마찬가지 역할을 하지만 우선순위는 낮습니다.

* 우선순위는 아래 공식 홈피에서 확인가능 합니다!

https://docs.nestjs.com/faq/request-lifecycle

 

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

 

일반적으로 파이프는 데이터의 매핑을 위해서 주로 사용됩니다.

무슨 뜻 인지 알기 위해서는 먼저 아래 클래스를 살펴 보겠습니다.

class 데이터구조{
  id : any;
  name : any;
  desc : any;
}

 

데이터구조 라는 클래스가 있다고 가정하여 봅니다.

브라우저에서 사용자가 요청을 할 때 이러한 데이터구조 라는 형태로 요청을 매핑하기를 원한다고 가정하여 봅니다.

데이터를 xss필터를 통해서 이러한 데이터 형태로 매핑하는 개념으로 접근 해 보겠습니다.

아래 4가지 모듈을 설치하여 줍니다.

#1 매핑 모듈
npm install class-transformer
npm install reflect-metadata

#2 html엘리먼트 검증 모듈
npm install sanitize-html
npm install -D @types/sanitize-html

 

필요한 모듈(라이브러리) 설치가 완료되었으면 아래 클래스를 만들어 줍니다.

해당 클래스는 사용자의 요청(Request)을 받아서 불필요한 HTML 엘리먼트를 제거해 주는 클래스 입니다.

* 파일이름 : 파이프클래스.ts

import {ArgumentMetadata, Injectable, PipeTransform} from '@nestjs/common';
import {plainToClass, plainToInstance} from 'class-transformer';
import * as sanitizeHtml from 'sanitize-html';

@Injectable()
export class 파이프클래스 implements PipeTransform {

    constructor(private readonly className: any) {    }

    transform(value: any, metadata: ArgumentMetadata) { 
        for(let key of Object.keys(value)){
            value[key] = sanitizeHtml(value[key]);  //html 엘리먼트 형식 데이터를 변환 합니다.
        }
        //plainToClass는 deprecated 되었습니다.
        return plainToInstance(this.className, value, {excludeExtraneousValues : true}); //Request를 변경
    }
}

 

어렵지 않는 내용의 클래스 입니다.

요청(Request)된 값을 분석하여 html 테그를 검증한 뒤에 plainToInstance 함수를 통해 반환하게 하였습니다.

plainToInstance 함수는 부여받은 클래스 형태로 객체를 만들어주는 역할을 합니다.

 

가운데 반복문은 키와 값을 통해서 사용자의 요청(Request) 값을 순회하게 하는 코드 입니다.

그러면서 sanitize-html 모듈을 통해서 <script> 형식의 코드를 제거하여 줍니다.

 

조금 어려울수 있겠지만 아래 컨트롤러 코드를 보면 바로 이해할 수 있습니다.

* 파일이름 : app.controller.ts

import { Request, Response } from 'express';
import { Controller, All,   Get, Res, Req, UsePipes, HttpStatus, Body, Query } from '@nestjs/common';
import { 파이프클래스 } from './파이프클래스';
import {Expose} from "class-transformer";

class 데이터구조{
  @Expose() id : any;
  @Expose() name : any;
  @Expose() desc : any;
}

@Controller()
export class AppController {

  @All('normal')
  normal(@Req() req: Request, @Res() res: Response){
    res.status(HttpStatus.OK).send({ yourData: 1234 });
  }  

  @Get('pipe')
  @UsePipes(new 파이프클래스(데이터구조))
  파이프사용(@Query() data : 데이터구조,@Req() req: Request, @Res() res: Response){
    console.log('data : ',data);
    console.log('query : ',req.query);
    res.status(HttpStatus.OK).send({ yourData: data });
  }  
}

 

데이터구조 라는 클래스를 파이프클래스에 new 연산자를 통해 넣어주었습니다.

그러면 파이프클래스 plainToInstance 함수를 통해서 요청받은 데이터를 데이터구조 라는 클래스로 바꾸어 줍니다.

이때 sanitize-html 모듈에 의해서 불필요한 html 엘리먼트가 제거되는 기능이 동작하게 됩니다.

요기!

 

실제 동작을 시켜봅니다.

desc 값으로 script 코드를 넣어서 전송을 해 봅니다.

desc가 비어있습니다!

 

입력하였던 desc값이 사라지는 것을 볼 수 있습니다.

<script> 로 시작하는 테그는 악성 스크립트로 사용될 수 있기 때문 입니다.

서버에서의 데이터가 어떻게 되었는지 확인하여 봅니다.

desc 값이 제거되어 있습니다.

 

역시 서버에서도 desc값이 제거된 것을 볼 수 있습니다.

이렇게 파이프를 사용하면 원하는 곳에 쉽게 xss 대비를 할 수 있습니다.

 


 

#2. helmet

 

만사 귀찮다! 알아서 다해라!

이를 위해서는 헬멧(helmet) 모듈을 사용하면 편리 합니다.

npm install helmet

 

모듈을 설치하고 나서 이제 적용해 봅니다.

* 파일이름 : main.ts

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import * as helmet from "helmet";

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.use(helmet());
  await app.listen(8080);
}
bootstrap();

 

적용이 완료되었습니다.

헬멧은 아주 간단하면서도 사용이 쉬운(?) 보안프레임워크 입니다.

좀더 세부적으로 사용하기 위해서는 아래처럼 사용하기도 합니다.

* 파일이름 : main.ts

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
import * as helmet from "helmet";

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  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());
  await app.listen(8080);
}
bootstrap();

 

세부적인 함수 내용은 아래 사이트를 들어가서 확인하여주세요!

https://www.npmjs.com/package/helmet

 

helmet

help secure Express/Connect apps with various HTTP headers. Latest version: 6.0.0, last published: 3 days ago. Start using helmet in your project by running `npm i helmet`. There are 3389 other projects in the npm registry using helmet.

www.npmjs.com

 

위 내용에 사용된 소스코드는 제 깃헙에서 받아볼 수 있습니다.

https://github.com/TaeSeungRyu/NestProject/tree/main/step10

 

GitHub - TaeSeungRyu/NestProject: Nestjs 프로젝트

Nestjs 프로젝트. Contribute to TaeSeungRyu/NestProject development by creating an account on GitHub.

github.com

 

공부하면 할 수록 재미있고 즐거운 nestjs!!!
다음 포스팅에서는 웹소켓 설정에 대해서 살펴보겠습니다.
궁금한점 또는 틀린부분은 언제든 연락 주세요!

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글