외부 입력이 포맷 문자열로 사용됨

외부 입력이 포맷 문자열로 사용됨

설명

사용자 입력이 util.format, console.log, sprintf 같은 포맷 문자열 위치에 들어가면 공격자가 %s, %d 같은 포맷 지정자를 삽입해 뒤따르는 인자를 의도와 다르게 해석하게 만들 수 있습니다. 보안 로그나 감사 로그에서 중요한 값을 숨기거나 왜곡하는 데 악용될 수 있습니다.

잠재적 영향

  • 로그 메시지의 구조가 공격자 입력에 의해 바뀌어 감사 추적이 왜곡될 수 있습니다.
  • 뒤따르는 인자가 공격자 지정 포맷에 의해 노출되거나 다른 의미로 기록될 수 있습니다.
  • 보안 모니터링 규칙이 기대한 로그 형태를 찾지 못해 탐지가 어려워질 수 있습니다.

해결 방법

  • 포맷 문자열은 상수로 고정하고 사용자 입력은 별도 인자로 전달하세요.
  • 사용자 입력을 포맷 문자열에 결합해야 하는 경우 포맷 지정자 문자를 해당 포맷 API의 규칙에 따라 처리하세요. 가능하면 결합 자체를 피하세요.
  • 구조화 로그를 사용해 메시지 템플릿과 사용자 값을 분리하세요.

예시

app, 인증된 req.user와 %s 형식을 지원하는 logger가 준비되어 있다는 전제의 발췌입니다. 고정된 포맷은 비밀 값 마스킹이나 줄바꿈 등 로그 내용의 처리를 대신하지 않습니다.

변경 전

javascript
const util = require("util");

app.get("/search", (req, res) => {
  logger.info(util.format(req.query.message, req.user.email));
  res.send("ok");
});

변경 후

javascript
app.get("/search", (req, res) => {
  logger.info("search message=%s user=%s", req.query.message, req.user.email);
  res.send("ok");
});

설명:

  • 변경 전: 사용자 입력이 포맷 문자열 자체가 되어 뒤따르는 인자를 공격자가 제어한 방식으로 해석할 수 있습니다.
  • 변경 후: 포맷 문자열을 상수로 고정하고 사용자 입력은 값 인자로 전달합니다.

참조