루프 조작(Loop Bound Injection)

사용자 제어 length 값으로 인한 과도한 반복

설명

루프의 종료 조건에 사용자 제어 객체의 length 값을 그대로 사용하는 경우, 공격자가 length를 비정상적으로 크게(예: 1e12, 1e100) 설정해 매우 긴 반복이나 사실상 무한 루프를 유발할 수 있습니다. 이로 인해 CPU/메모리 자원이 고갈되어 서비스 거부(DoS)가 발생할 수 있습니다. 예를 들어, 배열을 기대하는 코드에 { length: 1000000000000 } 같은 객체를 전달하면 for/while 루프가 끝나지 않거나 매우 오래 실행됩니다.

잠재적 영향

  • Denial of Service (서비스 거부): 과도한 반복으로 요청 처리가 지연되거나 서버가 응답 불가 상태가 될 수 있습니다.
  • CPU 자원 고갈: 루프가 장시간 실행되어 CPU를 점유, 다른 요청 처리에 영향을 줍니다.
  • 메모리 고갈: 루프 내에서 push/concat 등 메모리 증가 작업이 수행되면 프로세스 메모리가 급격히 커져 크래시가 발생할 수 있습니다.
  • 성능 저하/타임아웃: 이벤트 루프가 막혀 전체 서비스 응답 시간이 급격히 증가하고, 상위 레이어(로드밸런서/게이트웨이)에서 타임아웃이 발생할 수 있습니다.

해결 방법

  • 길이를 읽기 전에 Array.isArray로 실제 배열인지 확인하세요.
  • 복사하거나 순회하기 전에 애플리케이션의 고정 MAX_ITEMS 상한을 초과하는 배열을 거부하세요.
  • 유사 배열 객체를 허용한다면 길이가 유한한 비음수 정수인지 검증하고, 할당이나 변환 전에 MAX_ITEMS 상한을 적용하세요.
  • Array.from(obj).slice(0, MAX_ITEMS)는 자르기 전에 전체 컬렉션을 처리하므로 입력 크기 제한에 사용하지 마세요.
  • 요청 크기 제한을 적용하고 작업의 목적에 맞게 각 요소를 검증하세요.

예시

변경 전

javascript
const express = require("express");
const app = express();
app.use(express.json());

// 취약: 신뢰되지 않은 length를 루프 경계에 직접 사용
app.post("/sum", (req, res) => {
  const list = req.body.items; // 사용자가 마음대로 보낼 수 있음
  let total = 0;

  // BAD: 공격자가 { items: { length: 1e12 } }를 보내면 과도한 반복 발생
  for (let i = 0; i < list.length; i++) {
    total += Number(list[i] || 0);
  }

  res.json({ total });
});

app.listen(3000);

변경 후

javascript
const express = require("express");
const app = express();
app.use(express.json());

const MAX_ITEMS = 10000; // 업무 요구에 맞게 합리적 상한 설정

// 안전: 타입과 길이를 검증한 뒤 제한된 배열만 순회
app.post("/sum", (req, res) => {
  const list = req.body.items;

  // 1) 실제 배열만 허용
  if (!Array.isArray(list)) {
    return res.status(400).json({ error: "items must be an array" });
  }

  // 2) 순회나 복사 전에 길이 상한 적용
  if (list.length > MAX_ITEMS) {
    return res.status(400).json({ error: "too many items" });
  }

  let total = 0;
  for (const v of list) {
    total += Number(v || 0);
  }

  res.json({ total });
});

app.listen(3000);

변경 전에는 요청 본문의 items.length를 반복 상한으로 신뢰합니다. 변경 후에는 실제 배열인지 확인하고 MAX_ITEMS를 초과하면 복사나 순회 전에 거부합니다. 예시의 숫자 변환과 별도로 각 요소의 허용 타입·값 범위를 검증하고 요청 크기를 제한하세요.

참조