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

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

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


Node.js/Nestjs (Nest.js)

Nestjs 프레임워크 서버(CSRF 방지, csurf) -10

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

# CSRF(csurf 모듈)

 

크로스 사이트 리퀘스트 변조(cross site request forgery, CSRF)는 사이트간 요청위조를 의미 합니다.

피해자의 권한으로 피해자 모르게 해커가 요청을 수행 하도록 만드는 것을 의미 합니다.

특히 "결재"와 관련된 웹 어플리케이션에서는 필수이며 가장 강력한 수준의 조치가 요구 됩니다.

* 해커가 악성스크립트를 통하여 원치않는 결재, 결재정보노출 등을 할 수 있기 때문 입니다.

 

nestjs에서도 이러한 문제를 방지하기 위해 csurf 모듈을 활용하여 기능 구현을 쉽게 할 수 있도록 해줍니다.

fastify 모듈도 있지만..설명이나 사용법이 워낙 불친절하고, 제 취향이 아녔..

 

원리와 개념은 간단 합니다.

 

클라이언트가 데이터를 변경하려는 페이지에 접근한 상태라면 특정 키 값을 발행 합니다.  * 예: 게시글 등록

이후에 서버에서 발행한 키 값을 가지고 클라이언트가 데이터 변경 요청을 하게되면,

서버에서는 키 값이 일치하면 올바른 요청으로 간주하고 컨트롤러에 값을 전달하여 줍니다.

그 이후에는 사용한 키값은 만료시키고 새로운 키 값을 다시 클라이언트에게 전달하여 줍니다.

이런 개념입니다!

 

기능 구현을 위해서 먼저 모듈을 추가하여 줍니다.

 

npm install --save cookie-parser
npm install --save csurf

 

CSRF설정은 어플리케이션이 동작하는 최상위 단계에서 먼저 동작해야 하므로 메인파일에서 설정을 합니다.

* 파일이름 : main.ts

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

async function bootstrap() {

  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  app.useStaticAssets('html파일이 있는경로');
  app.use(cookie());
  app.use(csurf({cookie:{maxAge : 1000 * 60 * 5 }}));  //쿠키로 설정, 5분
  app.use((req: any, res: any, next: any) => {  //사용 설정
    const token = req.csrfToken();
    res.cookie('XSRF-TOKEN', token);  //키 값 입니다
    res.locals.csrfToken = token;
    next();
  }); 

  await app.listen(3000);
}
bootstrap();

 

이제 응답을 받을 컨트롤러를 작성하여 줍니다.

* 파일이름 : app.controller.ts

import { Request, Response } from 'express';
import { Controller, All, Res, Req, HttpStatus} from '@nestjs/common';

@Controller()
export class AppController {

  @All('getCsrf')
  겟방식엔관대하다(@Req() req: Request, @Res() res: Response) {
    res.status(HttpStatus.OK).send({ result : 'ok' });
  }  

}

 

getCsrf 요청에 대해서 ok 값을 리턴하는 메소드를 추가하였습니다.

이제 테스트를 위한 페이지가 필요 합니다.

이를 위해서는 메인 파일(main.ts)에서 useStaticAssets 함수에 html 페이지가 있는 경로를 입력하여 간단한 index.html 파일을 접근 할 수 있게 해주었습니다.

index.html 파일 내용은 아래와 같습니다.

* 파일이름 : index.html

<html>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>index</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>

<body>
    <button type="button" onclick="sendData()">sendData</button>
</body>

</html>

<script>
function getCookie(cname) {
    let name = cname + "=";
    let decodedCookie = decodeURIComponent(document.cookie);
    let ca = decodedCookie.split(';');
    for (let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

let csrfToken
fetch('getCsrf') //토큰을 발급 받습니다.
.then((response) => {
    csrfToken = getCookie('XSRF-TOKEN'); 
    console.log(csrfToken)   
});

function sendData() {
    console.log('보내기위한 값',csrfToken)
    fetch('getCsrf', {  //토큰을 가지고 인증합니다
        method: 'POST',  //POST, PUT 등 데이터를 변화하는 요청에 의해 동작합니다
        headers: {
            'XSRF-TOKEN':  csrfToken,  //main.ts 파일에서 설정한 키 값 입니다!!!!
            //'XSRF-TOKEN':  'hahah',
        },
        body: {data:1234}
    }).then((res)=>console.log(res));        
}
</script>

 

index.html 파일이 준비가 완료되고 나면 아래 적어둔 fetch함수에 의해서 getCsrf 요청이 발생 합니다.

getCsrf 요청에서는 쿠키로 CSRF 토큰값을 받을 수 있습니다.

쿠키로 값을 받았습니다.

 

인증받은 값이 존재하기 때 문에 이제 데이터를 변경하는 행위에 대해서 CSRF 위변조 기능이 동작할 것 입니다.

이를 위해서 index.html에 있는 버튼을 누릅니다.

해당 버튼은 해더에 키로 "XSRF-TOKEN", 값은 서버에서 발급받은 값을 가지고 POST 방식으로 요청하게 되어 있습니다.

post 로 보냈습니다!

200 코드가 받아진 것으로 보아하니 문제없이 완료가 된 것을 알 수 있습니다.

테스트를 위해서 이상한 값을 한번 보내봅니다.

index.html파일에서 sendData함수의 주석처리된 "hahah" 값을 보내면 아래처럼 오류가 발생 합니다.

유효하지 않는 값 입니다.

 

간단한 설정 하나만으로 위 내용처럼 쉽게 CSRF 변조 공격을 방지할 수 있습니다!

해당 내용에 사용된 소스코드는 제 깃허브에서 받아서 확인 할 수 있습니다.

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

 

GitHub - TaeSeungRyu/NestProject: Nestjs 프로젝트

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

github.com

 

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

 * fastify는 너무 불친절하네요..좀 더 분석해 보겠습니다..ㅜ

 

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

댓글