03 - Injection
https://owasp.org/Top10/A03_2021-Injection/
개요
OWASP Top 10 2021 중 3위로 올라간 Injection (주입)이다.
- 애플리케이션의 94%가 어떤 형태로든 주입 공격을 테스트했으며. 최대 발생률은 19%, 평균 발생률은 3%, 발생 횟수는 27만 4천 회이다.
- 거의 모든 유형의 인젝션 (SQL Injection, NoSQL Injection, OS Command Injection 등) 공격이 포함된다.
- 2021년부터는 OWASP가 XSS를 Injection의 한 종류로 포함시켰다.
- 신뢰할 수 없는 입력값이 코드, 쿼리, 명령어 등으로 해석되거나 실행될 수 있는 경우 발생한다.
구체적 발생 예시
- 사용자 입력이 검증, 필터링, 정제되지 않았을 때 발생하기 쉽다.
- 동적 커리(코드 실행 중 쿼리 문장을 만들어 내는 방식)나 파라미터가 없는 호출(쿼리 안에 입력값이 직접 들어가서 실행되는 것)을 그대로 실행하는 경우 발생한다.
- ORM(Object Relational Mapping, 객체 지향 언어에서 DB를 쉽게 다루기 위해 중간에 자동으로 SQL을 만들어주는 기술이다.) 기반 검색에서 악의적인 데이터가 그대로 사용될 때 발생한다.
공격 시나리오 예시
String query = "SELECT * FROM accounts WHERE custID='" + request.getParameter("id") + "'";
- 이 입력에서
id
파라미터에 ' OR '1'='1
등을 넣으면 모든 레코드가 노출 가능하다.
Query HQLQuery = session.createQuery("FROM accounts WHERE custID='" + request.getParameter("id") + "'");
- Hibernate(java에서 가장 많이 쓰이는 ORM 라이브러리) 기반 ORM에서 다음과 같이 취약한 HQL을 사용하는 경우도 공격자가
id
값을 조작하면 데이터베이스를 조회 또는 조작할 수 있다.
방지 방법
- 안전한 API 사용 또는 파라미터화된 쿼리 활용
- 입력값을 쿼리에 직접 집어넣지 말고, 미리 자리를 지정해두고(
?
등 활용) 그 자리에 넣는 방식으로 처리한다.
- ORM 도구도 도움이 되지만, 스토어드 프로시저 형태로도 데이터 병합이 발생할 수 있어 주의가 필요하다.
- 서버 측 화이트리스트(허용된 값만 통과시키는 방식) 기반 입력 검증
- 하지만 모든 상황에 허용값을 정의하기 어렵고, 예상 못한 우회 방법이 있을 수 있다. 화이트리스트 만으로는 충분하지 않을 수 있으니 주의한다.
- 동적 쿼리 사용 시 특수문자 이스케이핑(문법을 깨트리는 특수기호를 문자로 취급하게 만드는 작업)
- 단, 테이블명이나 컬럼명 등 구조적 요소는 이스케이프가 불가능하기에, 이런 구조를 사용자가 직접 조작하는 것은 여전히 위험하다.
실습 환경
- DOKER
- DVWA (SQL Injection)
문제 설명
웹 애플리케이션의 ID 입력란에 SQL Injection 페이로드를 삽입하여, 데이터베이스 users
테이블에 저장된 모든 레코드를 화면에 출력한다.
공격 과정 요약
low
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
- 소스 코드를 확인해보면 파라미터 없는 호출을 이용하여, 사용자의 입력을 그대로 대입하고 있다.
' OR 1=1; #
을 입력하여 SQL Injection 공격을 시도한다.
- 이는
$id
에 그대로 삽입되는 것으로, SELECT first_name, last_name FROM users WHERE user_id = '' OR 1=1; #';
가 된다.
- 성공적으로 테이블에 저장된 레코드를 확인할 수 있다.

medium
- 직접 USER ID를 입력할 수 있던 LOW와 다르게, MEDIUM은 아예 입력창이 없다.
- 따라서 Burp Suite 를 이용하여 직접 GET 파라미터를 조작해야 한다.
- Burp Suite 애서 요청 바디를 확인해 보면
id=1&Submit=Submit
데이터가 존재한다.
- 값을 바꿔
id=1 OR 1=1 #&Submit=Submit
을 입력해 주었더니 성공적으로 테이블에 저장된 레코드를 확인할 수 있다.

high
here to change your ID
하이퍼링크를 타고 들어가면 새로운 창이 열리고, 그 안에서 id를 서치해볼 수 있는 형식이다.
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
- 하지만 이 소스코드에서 확인할 수 있듯, 여전히 사용자의 입력을 별 다른 검토 없이 바로 id에 집어넣는 형식임을 알 수 있다.
LIMIT 1
을 이용해 한 줄만 출력이 되게끔 하였지만, 이 역시도 주석(--
, #
)을 입력하면 효력이 없어진다.
' OR 1=1; #
을 입력하여 SQL Injection 공격을 시도한다.
- 성공적으로 테이블에 저장된 레코드를 확인할 수 있다.

발생한 보안 문제
- 사용자 입력값의 검증 및 필터링 부재
- 사용자가 입력한 값이 쿼리에 그대로 삽입되었지만, 별 다른 검사나 필터링이 없었다.
- 이로 인해 공격코드가 그대로 SQL 구문에 반영되어, 쿼리 로직을 조작할 수 있었다.
- 화이트리스트 방식이나 이스케이프 처리도 없었기에 취약했다.
- 클라이언트 측의 한계
- Medium과 High 레벨에서는 입력창을 없애거나 세션 변수로 값을 전달하는 등 공격 경로를 간접적으로 숨기는 시도를 하였다.
- 하지만 공격자는 Burp Suite 같은 프록시 툴을 활용해 HTTP 요청을 직접 조작할 수 있어 입력창 없이 파라미터를 변조할 수 있다.
- 또한 새션 변수로 값을 전달하여도 세션 값에 대한 검증 및 이스케이프가 없었기에, 공격은 그대로 가능하였다.
배운 점
- 사용자 입력 값을 검증 하지 않는다면, 일반 사용자도 간단한 입력만을 통해 중요한 정보를 얻을 수 있음을 알게 되었다.
mysqli_real_escape_string
등의 이스케이프 함수 만으로도 완벽하지 않음을 알게 되었다.
- DB에 저장된 정보들은 현대 데이터 시대에 가장 중요한 정보들임으로, 더욱 더 안전하게 보호될 수 있도록 실무에서 확실한 보안을 해야겠다고 다짐하였다.