SQL 인젝션 방어
작성자: 니콜 홀든 2024년 4월 29일
2024년 3월, CISA는 SQL 인젝션(SQLi)과 관련된 다음과 같은 권고문을 발표했습니다: “디자인부터 안전: 소프트웨어에서 SQL 인젝션 취약점 제거“. SQL 인젝션은 데이터베이스 관리자와 개발자가 방어해야 할 가장 널리 퍼져 있고, 파괴적인 취약점 유형 중 하나입니다. CISA에 따르면, “SQL 인젝션 취약점은 사용자 입력을 SQL 명령에 직접 삽입함으로써 공격자가 임의의 쿼리를 실행할 수 있도록 하는 것이다. 이는 소프트웨어 개발자가 보안 모범 사례를 따르지 않아 데이터베이스 쿼리와 사용자 제공 데이터를 혼합하기 때문에 발생한다”고 합니다.
SQL 인젝션이 악용되면, 공격자는 민감한 데이터에 무단 접근하거나 데이터베이스 내용을 수정하며, 심지어는 서버에서 임의의 명령을 실행할 수도 있습니다. 성공적인 SQL 인젝션 공격의 결과는 고객 정보의 유출, 금전적 손실, 평판 훼손, 규제적 제재와 같은 치명적인 결과를 초래할 수 있습니다.
효과적인 방어 전략
다행히도, 이러한 공격 유형을 방어하기 위한 확립된 모범 사례를 적용할 수 있습니다. CISA 권고사항뿐만 아니라 **OWASP(Open Web Application Security Project)**도 SQL 인젝션 방지를 위한 지속적으로 업데이트된 지침을 제공합니다. 입력 검증, 신뢰할 수 없는 데이터 이스케이핑, 준비된 명령문 사용, 저장 프로시저 및 코드 리뷰 등은 SQLi와 그로 인한 손상을 방지하는 데 도움이 될 수 있습니다. 또한 최소 권한 원칙을 적용하여 데이터베이스 요청을 보내는 서비스 계정이나 애플리케이션의 권한을 제한하면 SQLi 공격이 발생했을 때 영향을 줄일 수 있습니다.
EDB(Postgres Advanced Server, EPAS)를 사용하는 고객은 SQL Protect와 Privilege Analysis 같은 도구를 활용하여 위의 모범 사례와 결합해 SQL 인젝션 방어를 강화할 수 있습니다.
입력 검증
입력 검증은 SQLi 방지를 위한 주요 방법이 되어선 안 되지만, 데이터 흐름 초기 단계에서 위험을 줄이는 데 기여할 수 있습니다. OWASP는 입력 검증 전략을 **구문적 검증(syntactic validation)**과 **의미적 검증(semantic validation)**의 두 가지로 나눕니다. 구문적 검증은 구조화된 필드의 올바른 구문을 강제하며, 의미적 검증은 특정 비즈니스 맥락에서 값의 정확성을 보장합니다.
PostgreSQL 사례
언어별 입력 검증 외에도 데이터베이스를 설계할 때, 특히 **체크 제약 조건(check constraints)**을 사용하는 것은 SQLi에 대한 또 다른 방어선이 될 수 있습니다. PostgreSQL 문서에서 체크 제약 조건에 대해 다음과 같이 설명합니다.
“체크 제약 조건은 가장 일반적인 제약 조건 유형입니다. 이는 특정 열의 값이 부울(참/거짓) 표현식을 충족해야 한다고 지정할 수 있도록 합니다. 예를 들어, 제품 가격이 양수여야 한다고 요구하려면 다음을 사용할 수 있습니다.”
sql코드 복사/* 양수 제품 가격을 요구하는 체크 제약 조건 추가 */
CREATE TABLE products (
productnum integer,
name text,
price numeric CHECK (price > 0)
);
이 작업은 기존 데이터베이스에서도 ALTER TABLE을 사용하여 수행할 수 있습니다. ADD table_constraint, ALTER CONSTRAINT, DROP CONSTRAINT와 같은 하위 명령도 이를 지원하기 위해 존재합니다.
신뢰할 수 없는 데이터 탈피
OWASP의 “데이터 인코딩 및 이스케이핑 체크리스트“는 이스케이핑이 SQLi 같은 공격을 방지하기 위해 데이터를 안전하게 포맷하는 방법이라고 설명합니다. 이스케이핑은 신뢰할 수 있는 라이브러리의 함수를 사용하는 것이 좋습니다.
PostgreSQL 사례
예를 들어, PostgreSQL C 라이브러리(libpq)에는 명령 실행 함수와 함께 사용할 수 있는 내장 이스케이핑 함수가 포함되어 있습니다. PQescapeLiteral은 SQL 명령에 포함될 문자열을 이스케이핑하는 데 사용됩니다. 공식 libpq 문서에 따르면, “이 함수는 SQL 명령에서 데이터 값을 리터럴 상수로 삽입할 때 유용합니다. 따옴표나 백슬래시와 같은 특정 문자는 SQL 파서에 의해 특별히 해석되지 않도록 이스케이핑되어야 합니다.”라고 되어 있습니다.
이 라이브러리에는 이 외에도 유용한 이스케이핑 함수들이 포함되어 있습니다. 예를 들어, PQescapeByteaConn은 이진 데이터를 이스케이핑하는 데 사용되며, PQescapeIdentifier는 테이블 이름과 같은 SQL 식별자로 사용되는 문자열을 이스케이핑하는 데 사용됩니다. 입력 검증과 마찬가지로, 이러한 내장 함수를 사용하는 것이 “직접 구현”하는 것보다 더 나은 모범 사례입니다.
준비된 명령문, 매개변수화된 쿼리 및 저장 프로시저
CISA 권고문에서는 SQLi를 방어하기 위한 주요 접근 방식으로 매개변수화된 쿼리를 권장합니다. 권고문에 따르면, **”소프트웨어 제품을 설계하고 개발하는 동안, 개발자는 준비된 명령문을 사용하는 매개변수화된 쿼리를 활용하여 SQL 코드와 사용자 제공 데이터를 분리해야 한다. 이러한 분리는 시스템이 사용자 입력을 실행 가능한 코드가 아닌 데이터로 처리하도록 보장하며, 악성 사용자 입력이 SQL 명령문으로 해석되는 위험을 제거한다.”**고 명시되어 있습니다.
매개변수화된 쿼리는 코드에서 SQL 문과 함께 값을 전달하며, 제공된 값이 삽입될 자리표시자가 포함된 쿼리입니다. **준비된 명령문(prepared statement)**은 매개변수를 수용할 준비가 된 사전 컴파일된 쿼리를 참조합니다. **저장 프로시저(stored procedure)**는 애플리케이션에서 자주 사용되는 쿼리 시퀀스를 저장할 수 있는 명령 집합입니다. 준비된 명령문과 저장 프로시저는 SQLi 방어에 함께 사용될 수 있습니다.
저장 프로시저는 적절히 사용될 경우 추가적인 이점을 제공합니다. 이는 한 번 컴파일된 후 실행 가능한 형태로 저장되므로 성능이 향상됩니다. 실행 가능한 코드는 캐시되며, 메모리 요구량을 줄입니다. 저장 프로시저는 재사용 가능한 코드, 더 쉬운 유지 보수, 개선된 보안을 지원합니다. 프로시저는 수행되는 프로세스와 활동을 제어하여 데이터베이스 객체를 보호하고 보안을 간소화합니다. 또한, 공격자가 프로시저 내부의 SQL 문에 명령을 삽입하기가 더 어렵습니다.
PostgreSQL 사례
/* Inserting data using a procedure */
procedure_demo=# CREATE OR REPLACE PROCEDURE genre_insert_data(GenreId integer, Name character varying)
procedure_demo-# LANGUAGE SQL
procedure_demo-# AS $$
procedure_demo$# INSERT INTO public.Genre VALUES (GenreId, Name);
procedure_demo$# $$;
CREATE PROCEDURE
procedure_demo=# CALL genre_insert_data(26,'Pop');
CALL procedure_demo=# select * from public.Genre where GenreId = 26;
코드 리뷰
코드 리뷰를 수행하는 데에는 다양한 방법론이 있습니다. 단독 리뷰, 페어 프로그래밍, 소규모 팀 리뷰, 크로스 펑셔널 리뷰 등 어떤 방식을 선택하든, 코드 리뷰를 통해 보안 코드 요구 사항을 준수하도록 하면 앞서 언급된 모범 사례들이 일관되게 사용되고 구현될 수 있습니다. 코드 리뷰의 궁극적인 목표는 코드 품질을 향상시키고, 팀 내 협업을 촉진하며, 보안 실천 사항의 책임감을 모든 팀원에게 분산시키는 것입니다.
최소 권한 원칙(Principle of Least Privilege)
최소 권한 원칙은 사용자가 수행해야 할 작업에 필요한 최소 수준의 액세스 또는 권한만을 부여하도록 권장하는 보안 원칙입니다. SQLi의 영향을 제한하기 위해 최소 권한 원칙을 적용하려면 데이터베이스 사용자와 애플리케이션이 SQL 쿼리를 실행하고 데이터를 액세스하는 데 필요한 권한만 가지도록 해야 합니다. 예를 들어, 사용자가 특정 테이블의 데이터를 읽기만 해야 한다면 전체 데이터베이스에 대한 읽기-쓰기 권한 대신 해당 테이블에 대한 읽기 전용 권한을 부여하는 방식으로 SQL 삽입 공격 시 발생할 수 있는 영향을 줄일 수 있습니다.
최소 권한 원칙은 앞서 언급된 저장 프로시저와도 함께 작동합니다. 데이터베이스 작업을 캡슐화하고 액세스 제어를 강화하기 위해 특정 작업에 대해 저장 프로시저를 정의하고, 해당 프로시저에 대해서만 실행 권한을 부여함으로써 기본 테이블에 대한 직접 액세스를 제한할 수 있습니다. 이를 통해 SQL 삽입 취약점을 예방할 수 있습니다.
EDB의 SQL Protect와 Privilege Analysis
EDB Postgres Advanced Server(EPAS)를 사용하는 고객을 위해, EDB는 앞서 설명된 모범 사례와 결합할 수 있는 도구와 기능을 제공합니다.
첫 번째로, SQL Protect는 들어오는 쿼리를 검사하고, 서명을 사용해 잠재적인 공격을 식별하며, 보호된 역할을 기반으로 쿼리를 모니터링하고, 공격 시도에 대한 포괄적인 통계와 기록을 제공합니다. SQL Protect는 악성 쿼리가 탐지되었을 때 경고를 보내도록 구성할 수 있어, 공격 시도에 대한 신속한 대응이 가능하도록 합니다.
두 번째로, Privilege Analysis는 EPAS v16에 새로 도입된 기능으로, 불필요하게 부여된 권한을 식별하는 데 도움을 줍니다. EDB는 “사용되지 않은 권한 식별 방법(How to Use Privilege Analysis to Identify [Un]Used Privileges)”이라는 튜토리얼을 제공하고 있습니다. 이 기능은 “사용 중이거나 사용되지 않은 권한을 분석”하여 관리자가 사용자가 업무를 수행하는 데 필요한 권한과 액세스만 보유하도록 확인할 수 있도록 도와줍니다.
결론
SQLi는 여전히 가장 널리 퍼져 있으며 치명적인 취약점 중 하나이지만 이를 방지하기 위한 효과적인 방법이 존재합니다. 널리 알려져 있고 잘 지원되는 보안 코드 관행과 코드 리뷰를 결합하면 소프트웨어를 안전하게 유지할 수 있습니다. 대부분의 경우, 이러한 방어 조치를 구현하면 성능 향상, 코드 유지 보수 용이성 증가, 개발 팀 내 보안 코딩 지식 전파와 같은 추가적인 이점을 제공합니다.
CISA 권고문은 또한 투명성과 소프트웨어 보안을 위한 책임을 포함하여 보안 목표를 달성할 수 있는 조직 구조를 구축하는 것의 중요성을 강조합니다. 이러한 투명성의 요구를 지원하기 위해, 향후 게시물에서 거버넌스가 보안 관행을 지원하는 데 있어 직면하는 도전과 중요성에 대한 추가 정보를 공유하고, 우리의 관행에 대한 더 많은 세부 정보를 제공하며, 새롭게 등장하는 보안 권고와 규정을 지속적으로 최신 상태로 유지할 것입니다.
본문: Protecting Against SQL Injection
EDB 영업 기술 문의: 02-501-5113
이메일: salesinquiry@enterprisedb.com
홈페이지 문의하기