비관계형 데이터 베이스 그리고 NoSQL Injection
해당 포스팅은 https://dreamhack.io/lecture/courses/189 를 참고하여 작성하였으며 공부 목적으로 작성하였습니다.
비관계형 DB 등장 배경
관계형 DB에서는 스키마를 정의하고 해당 규격에 맞는 데이터를 2차원 테이블 형태로 저장하기 때문에 복잡성이 증가하고 데이터가 많아질 경우 용량의 한계에 다다른다는 한계점이 존재한다.
⇒ 이를 해결하기 위해 등장한 것이 비관계형 데이터 베이스(Non-Relational DBMS)
이다. NoSQL 또한 이용자의 입력값을 통해 동적으로 쿼리를 생성해 데이터를 저장하기 때문에 이와 같은 문제점이 발생한다.
비관계형 DB
관계형 DBMS와는 다르게 최적화된 저장 공간이 크며 키-값을 사용해 값을 저장한다는 차이점이 존재한다.
하지만, 비관계형 DBMS 마다 사용 문법과 구조가 다르기 때문에 이를 익혀야한다는 단점도 존재한다.
MongoDB
MongoDB의 경우 실제 프로젝트에도 자주 이용했던 비관계형 DBMS이다. Mongo DB의 특징은 다음과 같다.
- JSON 형태인 데이터를 저장하고 스키마를 따로 정의하지 않기 때문에 콜렉션(테이블과 비슷한 의미)에 대한 정의가 필요하지 않다
- JSON 형식으로 쿼리 작성이 가능하다
- _id 필드가 Primary Key 역할을 한다.
Mongo DB에서는 연산자 사용이 가능한데 비교연산자부터 살펴보면 다음과 같다.
Name | Description |
$eq | 지정된 값과 같은 값을 찾음 (equal) |
$in | 배열 안의 값들과 일치하는 값을 찾음 (in) |
$ne | 지정된 값과 같지 않은 값을 찾음 (not equal) |
$nin | 배열 안의 값들과 일치하지 않는 값을 찾음 (not in) |
논리적 연산자는 다음과 같다.
Name | Description |
$and | 논리적 AND, 각각의 쿼리를 모두 만족하는 문서 반환 |
$not | 쿼리 식의 효과를 반전시킵니다. 쿼리 식과 일치하지 않는 문서 반환 |
$nor | 논리적 NOR, 각각의 쿼리를 모두 만족하지 않는 문서 반환 |
$or | 논리적 OR, 각각의 쿼리 중 하나 이상 만족하는 문서 반환 |
요소(Element)를 찾거나 선택하는 연산자는 다음과 같다.
Name | Description |
$exists | 지정된 필드가 있는 문서 검색 |
$type | 지정된 필드가 지정된 유형인 문서 선택 |
Evaluation 과 관련된 연산자는 다음과 같다.
Name | Description |
$expr | 쿼리 언어 내에서 집계 식을 사용 |
$regex | 지정된 정규식과 일치하는 문서 선택 |
$text | 지정된 텍스트를 검색 |
Redis
Redis는 다른 타 기반의 DB와 다르게 메모리 기반의 DBMS이다. Redis는 Key-Value 쌍을 가진 데이터를 저장하며 명령어는 공식 문서에 정리되어 있다.

CouchDB
CouchDB 또한 MongoDB와 같이 JSON 형태의 데이터를 저장하지만 웹 기반의 DBMS이므로 REST API 형식으로 요청을 처리한다. Couch DB에 사용되는 명령어는 우리가 흔히 아는 GET, POST이다.
NoSQL Injection
NoSQL Injection은 SQL Injection과 공격 방법 및 목적이 매우 유사하다. SQL Injection은 블랙 리스트 기반의 파일 확장자 필터링, 특수문자 필터링 적용하지 않음 등과 같이 다양한 원인에서 발생할 수 있지만 NoSQL Injection은 주로 이용자의 입력값에 대한 타입 검증이 불충분할 때 발생한다.
다음과 같이 이용자가 입력한 uid와 upw에 해당하는 데이터를 찾고 출력하는 코드가 있다고 할 때 입력값에 대한 타입 검증 로직이 없는 것을 알 수 있다.
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const db = mongoose.connection;
mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true });
app.get('/query', function(req,res) {
db.collection('user').find({
'uid': req.query.uid,
'upw': req.query.upw
}).toArray(function(err, result) {
if (err) throw err;
res.send(result);
});
});
const server = app.listen(3000, function(){
console.log('app.listen');
});
해당 코드에 다음과 같은 요청을 보내게 되면 타입 검증을 하지 않기 때문에 a가 아닌([$ne]
) 데이터를 모두 획득할 수 있다.
http://localhost:3000/query?uid[$ne]=a&upw[$ne]=a
Blind NoSQL Injection
Blind NoSQL Injection은 Blind SQL Injection과 동일하게 참/거짓 결과를 통해 DB 정보를 알아내는 것이다.
Mongo DB에서 Blind NoSQL Injection을 위해 사용하는 것은 $regex
, $where
이다.
$regex
$regex은
정규식과 일치하는 데이터를 조회하는 것으로 예시와 같이 사용하면 test에서 a로 시작하는 데이터를 죄회할 수 있다.
db.user.find({test: {$regex: "^a"}})
$where
$where는 인자로 전달한 Js 표현식을 만족하는 데이터를 조회하는 연산자이다. 하지만 다른 연산자와는 다르게 필드에서 사용할 수 없다.
db.user.find({$where:"return 1==1"}) # 사용 가능
db.user.find({test:{$where:"return 1==1"}}) # 필드 사용 불가
$where를 사용하는 대표적인 Blind NoSQL Injection은 substring
, Sleep 함수를 통한 Time based Injeciton
, Error based Injection
이 존재한다.
간단하게 substring을 사용한 Blind NoSQL Injection 예시는 다음과 같다.
db.user.find({$where: "this.test.substring(0,1)=='a'"})
오류, 잘못된 점 또는 궁금한 점이 있으시다면 댓글 남겨주세요❗
Uploaded by N2T