Node.js 보안 취약점

코이그·2023년 7월 25일

Injection Attacks

  • 공격자가 취약 애플리케이션에 악성 코드를 작성하고 애플리케이션이 이를 실행했을 때 발생함.

종류

  • SQL injection
  • NoSQL injection
  • Command injection

문제

// SQL injection attack의 예시
const express = require("express");
const app = express();
const mysql = require("mysql");

const connection = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "password",
  database: "test",
});

app.get("/user", (req, res) => {
  const id = req.query.id;
  const query = `SELECT * FROM users WHERE id = ${id}`;
  connection.query(query, (error, results) => {
    if (error) {
      throw error;
    }
    res.send(results);
  });
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • req로 받은 id가 SQL 쿼리에 바로 사용이 되는데 이 때 id값으로 1 혹은 1=1 등을 주게 된다면 users 테이블의 모든 레코드를 가져오게 된다.

해결방법

app.get("/user", (req, res) => {
  const id = req.query.id;
  const query = "SELECT * FROM users WHERE id = ?";
  connection.query(query, [id], (error, results) => {
    if (error) {
      throw error;
    }
    res.send(results);
  });
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • SQL injection 공격을 방지하기 위해서는 req로 받은 id를 쿼리에 바로 사용하는 것이 아니라 prepared statement를 사용하여 id를 쿼리에 바인딩하는 방법이 있다.

Cross-Site Scripting(XSS)

  • 다른 사용자들이 보는 웹페이지에 악성 스크립트를 삽입하는 것.

  • 이러한 공격으로 인해 민감한 데이터(login credentials 등)가 도난당할 수 있음.

문제

// XSS attack의 예시
const express = require("express");
const app = express();

app.get("/", (req, res) => {
  const name = req.query.name;
  res.send(`<h1>Hello, ${name}</h1>`);
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • name이 HTML response에 직접적으로 포함이 되어있는데, name에 <script>alert('XSS')</script>라는 값을 넘겼을 때 이는 악성 스크립트가 된다.

해결방법

  • 이러한 취약점을 방지하기 위해서는 escape-html 등의 라이브러리를 사용하면 된다.
const express = require("express");
const app = express();
const escapeHtml = require("escape-html");

app.get("/", (req, res) => {
  const name = escapeHtml(req.query.name);
  res.send(`<h1>Hello, ${name}</h1>`);
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});

Denial-of-Service(DoS)

  • 서버에 과부하를 주어서 마비시키는 것.

    • 서버에 많은 요청을 보내거나
    • 서버에 데이터를 플러딩하는 등 다양한 방법을 통해 공격할 수 있음
  • 이를 방지하기 위해서는 속도 제한을 구현하고, 적절한 오류 처리를 사용하고, 강력한 인프라를 구축해야 한다.

문제

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  // Do a resource-intensive operation
  while (true) {}
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • 들어온 요청들에 대해 적절한 처리가 되어있지 않음
  • 대량의 요청이 들어온다면 무한 루프 때문에 마비가 될 것

해결방법

  • 들어온 요청에 대한 적절한 검증 및 처리
  • 요청이 처리할 수 있는 자원의 제한
const express = require("express");
const app = express();
const rateLimit = require("express-rate-limit");

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
  message: "Too many requests, please try again later",
});

app.use(limiter);

app.get("/", (req, res) => {
  res.send("Hello, World!");
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • express-rate-limit 패키지를 활용하여 최대 요청 수를 제한하는 미들웨어를 사용할 수 있다.

Improper Authentication and Authorization

  • 부적절한 인증 및 권한 부여로 인해 민감한 데이터에 대한 무단 액세스가 발생하여 도난이나 손상이 발생할 수 있음

  • 이를 방지하기 위해 적절한 인증 및 권한 부여 방법을 구현해야 함

    • 보안 암호 사용
    • 이중 인증 사용

문제

const express = require("express");
const app = express();

app.get("/secret", (req, res) => {
  res.send("This is a secret page!");
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • /secret 엔드포인트는 보안이 적용되어있지 않아서 이 경로를 아는 누구나 접근할 수 있음

해결방법

  • 적절한 인증 메커니즘 구현
const express = require("express");
const app = express();

const checkAuth = (req, res, next) => {
  if (!req.session.user) {
    return res.status(401).send("Unauthorized");
  }
  next();
};

app.get("/secret", checkAuth, (req, res) => {
  res.send("This is a secret page!");
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
  • checkAuth 미들웨어를 사용함으로써 /secret 엔드포인트에 접근하기 전 사용자의 인증정보를 확인한다.

Insecure Direct Object References

  • 안전하지 않은 직접 개체 참조에서 공격자는 의도된 보안 제어를 우회하여 개체에 직접 접근 및 조작을 할 수 있음

문제

const express = require("express");
const app = express();

const users = [
  { id: 1, name: "John Doe" },
  { id: 2, name: "Jane Doe" },
];

app.get("/user/:id", function (req, res) {
  let user = users.find((user) => user.id == req.params.id);

  if (!user) {
    res.status(404).send("User not found");
    return;
  }

  res.send(user);
});

app.listen(3000);
  • users 배열에서 params로 받은 id값에 해당하는 user 정보를 추출하는데 이 때, id로 주는 값에 따라 남의 정보 또한 추출할 수 있음.

해결방법

  • 위의 공격과 마찬가지로 현재 사용자가 접근 권한이 있는 사용자인지 체크하는 미들웨어를 사용하여 남의 정보에 접근하는 것을 방지할 수 있음.

https://blog.javascripttoday.com/blog/node-js-server-vulnerabilities/

profile
COYG🔴⚪

0개의 댓글