Ajv allErrors:true로 인한 리소스 고갈(DoS)
Uncontrolled Resource Consumption (Resource Exhaustion)
Last updated
Uncontrolled Resource Consumption (Resource Exhaustion)
Last updated
import express from "express";
import Ajv from "ajv";
const app = express();
app.use(express.json({ limit: "1mb" }));
// BAD: 운영 환경에서 allErrors:true 사용 (모든 에러를 수집)
const ajv = new Ajv({ allErrors: true });
const userSchema = {
type: "object",
additionalProperties: false,
properties: {
name: { type: "string" },
tags: { type: "array", items: { type: "string" } },
},
required: ["name", "tags"],
};
const validateUser = ajv.compile(userSchema);
app.post("/users", (req, res) => {
const valid = validateUser(req.body);
if (!valid) {
// 많은 에러를 그대로 생성/반환 -> CPU/메모리 소모
return res.status(400).json({ errors: validateUser.errors });
}
res.send("ok");
});
app.listen(3000);import express from "express";
import Ajv from "ajv";
const app = express();
// 요청 크기 제한과 타임아웃 설정
app.use(express.json({ limit: "512kb" }));
app.use((req, res, next) => {
req.setTimeout(5000);
next();
});
// GOOD: 기본값(fail-fast). 필요 시 개발 환경에서만 allErrors를 켜기
const enableAllErrors = process.env.DEBUG_VALIDATE === "1";
const ajv = new Ajv({ allErrors: enableAllErrors });
const userSchema = {
type: "object",
additionalProperties: false,
properties: {
name: { type: "string" },
tags: {
type: "array",
maxItems: 50,
items: { type: "string", maxLength: 64 },
},
},
required: ["name", "tags"],
};
const validateUser = ajv.compile(userSchema);
app.post("/users", (req, res) => {
const valid = validateUser(req.body);
if (!valid) {
// 요약된 메시지로 반환 (대량의 에러 객체 노출 방지)
return res.status(400).json({ message: "Invalid payload" });
}
res.send("ok");
});
app.listen(3000);