NoSQL Injection

Y30L·2023년 1월 29일
0

Web hacking

목록 보기
1/1

해당 포스팅은 https://dreamhack.io/lecture/courses/189 를 참고하여 작성하였으며 공부 목적으로 작성하였습니다.

비관계형 DB 등장 배경


관계형 DB에서는 스키마를 정의하고 해당 규격에 맞는 데이터를 2차원 테이블 형태로 저장하기 때문에 복잡성이 증가하고 데이터가 많아질 경우 용량의 한계에 다다른다는 한계점이 존재한다.

⇒ 이를 해결하기 위해 등장한 것이 비관계형 데이터 베이스(Non-Relational DBMS) 이다. NoSQL 또한 이용자의 입력값을 통해 동적으로 쿼리를 생성해 데이터를 저장하기 때문에 이와 같은 문제점이 발생한다.

비관계형 DB


관계형 DBMS와는 다르게 최적화된 저장 공간이 크며 키-값을 사용해 값을 저장한다는 차이점이 존재한다.

하지만, 비관계형 DBMS 마다 사용 문법과 구조가 다르기 때문에 이를 익혀야한다는 단점도 존재한다.

MongoDB


MongoDB의 경우 실제 프로젝트에도 자주 이용했던 비관계형 DBMS이다. Mongo DB의 특징은 다음과 같다.

💡 **MongoDB의 특징**
  1. JSON 형태인 데이터를 저장하고 스키마를 따로 정의하지 않기 때문에 콜렉션(테이블과 비슷한 의미)에 대한 정의가 필요하지 않다
  2. JSON 형식으로 쿼리 작성이 가능하다
  3. _id 필드가 Primary Key 역할을 한다.

Mongo DB에서는 연산자 사용이 가능한데 비교연산자부터 살펴보면 다음과 같다.

NameDescription
$eq지정된 값과 같은 값을 찾음 (equal)
$in배열 안의 값들과 일치하는 값을 찾음 (in)
$ne지정된 값과 같지 않은 값을 찾음 (not equal)
$nin배열 안의 값들과 일치하지 않는 값을 찾음 (not in)

논리적 연산자는 다음과 같다.

NameDescription
$and논리적 AND, 각각의 쿼리를 모두 만족하는 문서가 반환됩니다.
$not쿼리 식의 효과를 반전시킵니다. 쿼리 식과 일치하지 않는 문서를 반환합니다.
$nor논리적 NOR, 각각의 쿼리를 모두 만족하지 않는 문서가 반환됩니다.
$or논리적 OR, 각각의 쿼리 중 하나 이상 만족하는 문서가 반환됩니다.

요소(Element)를 찾거나 선택하는 연산자는 다음과 같다.

NameDescription
$exists지정된 필드가 있는 문서를 찾습니다.
$type지정된 필드가 지정된 유형인 문서를 선택합니다.

Evaluation 과 관련된 연산자는 다음과 같다.

NameDescription
$expr쿼리 언어 내에서 집계 식을 사용할 수 있습니다.
$regex지정된 정규식과 일치하는 문서를 선택합니다.
$text지정된 텍스트를 검색합니다.

Redis


Redis는 다른 타 기반의 DB와 다르게 메모리 기반의 DBMS이다. Redis는 Key-Value 쌍을 가진 데이터를 저장하며 명령어는 공식 문서에 정리되어 있다.

Commands

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
profile
악성코드 분석, 파이썬을 사용한 개발을 공부하는 대학생입니다❗

0개의 댓글