Home csurf 모듈
Post
X

csurf 모듈


csurf 모듈

CSRF 공격을 방지하는 보안 모듈로 CSRF Secret와 CSRF Token을 만들어서 서로 매칭이 되는지 확인하는 기능을 제공합니다.

보통 CSRF Secret는 session/cookie에 저장하고 CSRF Token은 POST body에 저장합니다.

  • 설치

    1
    
    npm i csurf
    

사용 방법

사용자가 티켓을 구매하는 과정으로 설명해보겠습니다.

  1. 사용자가 티켓을 구매하기 위해 https://ticket.com/purchase로 접속을 합니다.

  2. 서버에서 라우터에 GET 요청이 오면, 페이지와 CSRF 토큰을 클라이언트에 렌더링하여 제공합니다.

    1
    2
    3
    
    app.get("/purchase", csrfProtection, (req, res) => {
      res.render("index", { csrfToken: req.csrfToken() });
    });
    
  3. 클라이언트 폼 태그에 토큰 정보를 저장합니다.

    <!-- index.ejs -->
    <html>
      <body>
        <form action="/" method="POST">
          // 서버에서 전달한 csrfToken을 input hidden 태그에 저장합니다.
          <input type="hidden" name="_csrf" value=csrfToken />
          <input type="text" name="text" value="" />
          <input type="submit" name="submit" />
        </form>
      </body>
    </html>
    
  4. POST Submit을 하게 되면, input 태그의 name인 _csrf 의 value(토큰)가 서버 라우터로 넘어가고 csrfProtection 미들웨어에서 검증합니다.

    1
    2
    3
    
    app.post("/purchase", csrfProtection, (req, res) => {
      res.send(req.body.text);
    });
    

전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const express = require("express");
const session = require("express-session");
const cookieParser = require("cookie-parser");
const csrf = require("csurf");

const app = express();
const csrfProtection = csrf({ cookie: true });
// 쿠키에 csrfSecret을 저장합니다.
// 만일 세션에 저장하고 싶다면 { cookie: false }로 설정합니다.

app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session(sessionOption));

// cookie-parser나 express-session 미들웨어보다 아래에 위치해야합니다.

// csrfProtection 미들웨어를 라우터에 장착합니다.
app.get("/purchase", csrfProtection, (req, res) => {
  // req.csrfToken()를 실행하여 csrf 토큰을 생성해서 저장 전달합니다.
  res.render("index", { csrfToken: req.csrfToken() });
});

app.post("/purchase", csrfProtection, (req, res) => {
  // 폼 submit이 POST로 오면 폼 태그 안에 있던 csrf 토큰 값을 csrfProtection 미들웨어에서 검증하고 next() 해줍니다.
  res.send(req.body.text);
});

Ajax 요청에서 csrf 토큰

폼 태그가 아닌 ajax로 요청할때는 csrf 토큰을 저장할 곳을 폼이 아닌 보통 meta 태그에 저장하는 경우가 많습니다.

1
<meta name="csrf-token" content="xe1t9is6-Q1bcuuJ8G5rdTXWCRqzkSat7FUI" />

그 후 Ajax 요청을 할 때 요청 헤더에 CSRF-Token을 넣습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let token = document
  .querySelector('meta[name="csrf-token"]')
  .getAttribute("content");

fetch("/process", {
  credentials: "same-origin",
  headers: {
    "CSRF-Token": token, // fetch 헤더에 'CSRF-Token'을 명시한다
  },
  method: "POST",
  body: {
    favoriteColor: "blue", // body 데이터
  },
});

에러 처리

만일 토큰 인증에 실패하면 에러처리 미들웨어로 점프되며 err.code === 'EBADCSRFTOKEN' 객체가 담기게 됩니다.

이를 이용해 에러 처리를 할 수 있습니다.

1
2
3
4
5
6
7
// error handler
app.use(function (err, req, res, next) {
  if (err.code !== "EBADCSRFTOKEN") return next(err); // 만일 토큰 에러가 아닌 다른 에러일경우 다른 에러처리 미들웨어로 보내고

  // CSRF token errors 라면 다음과 같이 처리한다.
  res.status(403).send("form tampered with");
});
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.