flawopen.com/참고 자료/MongoDB NoSQL Injection: 입력이 쿼리를 바꾸는 경우
MongoDB NoSQL Injection: 입력이 쿼리를 바꾸는 경우
MongoDB는 SQL을 실행하지 않으므로 SQL 문자열 연결이 문제는 아닙니다. 애플리케이션이 스칼라를 기대하는 위치에서 신뢰할 수 없는 중첩 객체를 받아 MongoDB 필터에 전달하면 NoSQL Injection이 발생할 수 있습니다. 이는 애플리케이션 입력 검증 결함이며 MongoDB 서버 CVE가 아닙니다.
쉽게 말해: 값이 규칙으로 바뀝니다
이름을 묻는 양식에서 담당자가 입력란에 보관 지침 전체를 받아들이는 상황을 떠올려 보세요. 데이터베이스는 이름 하나를 비교하는 대신 그 지침을 따릅니다. 기대하는 값의 유형을 검증하고 검색 규칙을 직접 만드세요.
신뢰 경계: 요청 JSON에서 MongoDB 필터까지
- 쿼리 필터
- MongoDB가 필드 비교로 해석하는 BSON 문서이며, 명시된 경우 쿼리 연산자도 포함합니다.
- 연산자 주입
- 애플리케이션이 단일 리터럴 값을 기대한 위치에 사용자가 제어하는 구조를 전달하는 문제입니다.
- 스칼라 검증
- 입력이 데이터베이스 작업에 영향을 주기 전에 유형, 길이, 허용 형식을 확인합니다.
- 프로젝션
- 우발적 데이터 노출을 제한하기 위해 서버가 선택하는 반환 필드 목록입니다.
오류가 발생하는 과정
엔드포인트는 스칼라를 기대합니다
API 계약은 username을 문자열로 정의하지만 JSON 파서는 배열, 객체, 불리언, null도 표현할 수 있습니다.
애플리케이션이 유형 검증을 생략합니다
핸들러가 파싱된 값을 서버가 만든 필터에 직접 넣으므로 객체는 리터럴 문자열이 아니라 쿼리 구조로 남습니다.
MongoDB가 전달된 구조를 평가합니다
중첩 문서에 쿼리 연산자가 있으면 조건이 의도한 정확한 값 비교와 다른 의미가 될 수 있습니다. 영향받는 레코드는 쿼리와 애플리케이션 권한 검사에 따라 달라집니다.
응답이 노출 범위를 결정합니다
반환 필드와 레코드 수는 프로젝션, 엔드포인트 동작, DB 계정 권한에 따라 달라집니다. 이것만으로 인증 우회나 전체 DB 접근이 입증되지는 않습니다.
MongoDB 공식 드라이버 지침은 엔드포인트가 검증되지 않은 JSON을 BSON으로 바꿔 필터로 사용하는 경계를 설명합니다. 이 가이드는 애플리케이션 계층의 결함을 다룹니다. 모든 MongoDB 배포가 취약하다고 주장하거나 서버 CVE를 지정하거나 인증 우회 방법을 제공하지 않습니다.
Node.js의 취약한 패턴과 강화된 패턴
app.post("/users/lookup", async (req, res, next) => {
try {
// The route expects a string, but req.body.username is untrusted JSON.
const user = await users.findOne({ username: req.body.username });
res.json(user ? { username: user.username } : null);
} catch (error) {
next(error);
}
});
const loginSchema = z.object({
username: z.string().trim().min(1).max(128),
password: z.string().min(1).max(1024)
}).strict();
app.post("/login", async (req, res, next) => {
try {
const parsed = loginSchema.safeParse(req.body);
if (!parsed.success) return res.sendStatus(400);
const { username, password } = parsed.data;
// The application owns the filter key; input supplies only a string value.
const user = await users.findOne(
{ username },
{ projection: { _id: 1, username: 1, passwordHash: 1 } }
);
const passwordMatches = user &&
await argon2.verify(user.passwordHash, password);
if (!passwordMatches) return res.sendStatus(401);
return res.sendStatus(204); // Session creation is application-specific.
} catch (error) {
next(error);
}
});
강화된 흐름은 데이터베이스에 접근하기 전에 문자열이 아닌 필드를 거부하고, 클라이언트가 보낸 필터 객체를 받지 않으며, 애플리케이션 코드에서 고정된 필터 키를 구성합니다. 선택한 필드만 반환하고 사용자를 조회한 뒤 암호 해시를 검증합니다. 예시에서 z는 Zod, users는 MongoDB 컬렉션, argon2는 설정된 암호 해시 라이브러리입니다. 세션 생성은 의도적으로 예시 범위에서 제외했습니다.
수정 검증 체크리스트
- ✓일반 문자열을 보내고 핸들러가 애플리케이션이 정한 정확한 필드만 조회하는지 확인하세요.
- ✓객체, 배열, 숫자, 불리언, null, 누락 필드, 너무 긴 문자열, 알 수 없는 속성을 보내고 MongoDB 호출 전에 schema가 거부하는지 확인하세요.
- ✓DB 호출이 서버가 만든 필터를 받으며 클라이언트 필터나 원시 JSON 조각을 받지 않는지 확인하세요.
- ✓프로젝션이 암호 해시와 불필요한 필드를 제외하고 DB 역할에 필요한 권한만 있는지 확인하세요.
- ✓Argon2id 또는 bcrypt로 암호를 해시하고 애플리케이션에서 검증하세요. 제출된 암호를 MongoDB 필터로 사용하지 마세요.
1차 출처
- MongoDB — 클라이언트 라이브러리 모범 사례: JSON을 BSON으로 변환하기 전에 신뢰할 수 없는 입력 검증
- OWASP — NoSQL 보안 치트 시트
- MongoDB — 쿼리 필터 만들기(C# 드라이버): 검증은 애플리케이션 책임