콘텐츠로 이동

A05:2025 인젝션 icon

배경.

인젝션은 3위에서 5위로 두 단계 하락했으며, A04:2025-암호 체계 실패 및 A06:2025-안전하지 않은 설계 대비 상대적 위치는 변동이 없다. 인젝션은 가장 많이 테스트 된 카테고리 중 하나로, 조사된 모든 애플리케이션에서 테스트 되었다. 인젝션은 다른 카테고리보다 가장 많은 수의 CVE를 보유했으며, 총 37개의 CWE가 포함되었다. 이 카테고리에는 총 3만 개가 넘는 CVE가 보고된 크로스 사이트 스크립팅(높은 빈도/낮은 영향)과 1만 4천 개가 넘는 CVE가 보고된 SQL 인젝션(낮은 빈도/높은 영향)이 포함되었다. CWE-79 웹 페이지 생성 중 입력값의 부적절한 중립화(크로스 사이트 스크립팅)에 대한 보고된 CVE 수가 매우 많아 이 카테고리의 평균 가중 영향도가 낮아졌다.

점수표.

해당되는 CWE 개수 최대 취약점 발생률 평균 취약점 발생률 최대 테스트 커버리지 평균 테스트 커버리지 평균 가중 악용도 평균 가중 영향도 총 발생 건수 총 CVE 건수
37 13.77% 3.08% 100.00% 42.93% 7.15 4.32 1,404,249 62,445

설명.

인젝션 취약점은 신뢰할 수 없는 사용자 입력이 인터프리터(예: 브라우저, 데이터베이스, 커맨드 라인)로 전송되어 인터프리터가 해당 입력의 일부를 명령으로 실행하도록 허용하는 애플리케이션 결함이다.

다음과 같은 경우 인젝션 공격에 취약할 수 있다.

  • 사용자가 제공한 데이터가 애플리케이션에 의해 검증, 필터링 또는 새니타이징되지 않는 경우.
  • 동적 쿼리 또는 파라미터 바인딩이 없는 호출을 해당 컨텍스트에 맞는 이스케이프 없이 인터프리터에 직접 전달하는 경우.
  • 객체 관계형 매핑(object-relational mapping, ORM) 검색 파라미터에 새니타이징되지 않은 입력값을 사용하여, 의도하지 않은 추가 민감 데이터 레코드를 조회하는 경우.
  • 잠재적으로 악의적일 수 있는 데이터가 그대로 사용되거나, 기존 SQL 또는 커맨드 뒤에 문자열로 이어 붙여지는 경우. 그 결과 동적 쿼리, 커맨드, 또는 저장 프로시저에서 원래의 구문과 공격자가 주입한 악성 데이터가 함께 포함되게 된다.

대표적인 인젝션 유형으로는 SQL, NoSQL, OS 커맨드, 객체 관계형 매핑(ORM), LDAP, 그리고 EL 표현식(Expression Language) 및 OGNL 표현식(Object Graph Navigation Library) 인젝션이 있다. 인터프리터 종류가 달라도 핵심 원리는 동일하다. 효과적인 탐지를 위해서는 소스 코드 리뷰와 함께, 모든 파라미터, 헤더, URL, 쿠키, JSON, SOAP, 및 XML 데이터 입력에 대한 자동화(퍼징 포함) 테스트하는 게 좋다. CI/CD 파이프라인에 정적(SAST), 동적(DAST), 및 인터랙티브(IAST) 애플리케이션 보안 테스트 도구를 통합하여, 것도 운영 환경 배포 전에 인젝션 결함을 식별하는 데 도움이 될 수 있다.

한편 LLM 환경에서도 유사한 계열의 인젝션 취약점이 흔해지고 있다. 이는 OWASP LLM Top 10에서 별도로 다룬다. 특히 LLM01:2025 프롬프트 인젝션 항목에서 관련 내용을 확인할 수 있다.

대응 방안.

인젝션 공격을 예방하는 최선의 방법은 데이터를 명령 및 쿼리로부터 분리하여 유지하는 것이다.

  • 가장 권장되는 방식은 안전한 API를 사용하는 것이다. 이는 인터프리터를 전혀 사용하지 않거나, 입력값을 파라미터화하도록 하는 인터페이스를 제공하거나, 객체 관계형 매핑(ORM) 도구를 사용하는 방식을 통해 이를 달성할 수 있다. 참고: 저장 프로시저는 파라미터화되어 있더라도, PL/SQL 또는 T‑SQL 내부에서 문자열 결합으로 쿼리와 데이터를 연결하거나, EXECUTE IMMEDIATE / exec()처럼 동적 실행 기능으로 적대적 입력을 실행하면 SQL 인젝션이 발생할 수 있다.

데이터를 커맨드로부터 분리하는 것이 불가능한 경우, 다음 방법을 사용하여 위협을 줄일 수 있다.

  • 서버 측 입력값 검증은 허용 목록 기반(positive validation)을 사용한다. 다만 텍스트 입력란이나 모바일 API처럼 특수문자 입력이 필요한 경우가 많아, 이것만으로는 완전한 방어가 되기 어렵다.
  • 불가피하게 동적 쿼리를 사용하는 지점이 남아 있다면, 사용 중인 인터프리터에서 사용하는 규칙에 맞춰 특수문자를 이스케이프 처리한다. 참고: 테이블 명과 칼럼 명 같은 SQL 구문들은 이스케이프할 수 없으므로, 사용자가 제공한 입력값을 쓰는 것은 위험하다. 이런 문제는 보고서 생성 기능에서 자주 나타난다.

경고 위 방법들은 문자열을 파싱하고 이스케이프하는 복잡한 처리를 전제로 하며, 구현 실수가 발생하기 쉽고 시스템 내부 동작이 조금만 바뀌어도 방어가 쉽게 무력화될 수 있다.

공격 시나리오 예시.

시나리오 1: 애플리케이션이 신뢰할 수 없는 데이터를 사용하여 다음과 같은 취약 방식으로 SQL을 호출한다.

String query = "SELECT * FROM accounts WHERE custID='" + request.getParameter("id") + "'";

공격자는 'id' 값에 ' OR '1'='1와 같은 페이로드를 주입하여 다음과 같이 요청을 보낼 수 있다.

http://example.com/app/accountView?id=' OR '1'='1

그 결과 쿼리의 내용이 변경되어 accounts 테이블의 전체 레코드가 조회될 수 있다. 상황에 따라 공격자는 데이터 변경 및 삭제 또는 저장된 프로시저 실행 등 더 심각한 행위를 유도할 수도 있다.

시나리오 2: 프레임워크를 사용하더라도 이를 과신하면, 여전히 인젝션에 취약할 수 있다. 취약한 하이퍼네이트 쿼리 언어(Hibernate Query Language, HQL)의 경우를 보자.

Query HQLQuery = session.createQuery("FROM accounts WHERE custID='" + request.getParameter("id") + "'");

공격자는 입력값으로 ' OR custID IS NOT NULL OR custID='를 입력한다. 이는 필터를 우회하고 모든 계정의 레코드를 반환한다. HQL은 로우(raw) SQL보다 위험한 함수가 더 적지만, 사용자 입력 값이 쿼리의 문자열로 연결될 때 여전히 권한이 없는 데이터 접근할 수 있다.

시나리오 3: 애플리케이션이 사용자 입력을 OS 커맨드로 사용한다.

String cmd = "nslookup " + request.getParameter("domain");
Runtime.getRuntime().exec(cmd);

공격자는 example.com; cat /etc/passwd를 입력하여 서버에서 임의의 명령을 실행한다.

참조.

해당되는 CWE 목록.