SQL 인젝션 (SQL Injection)
SQL Injection
설명
SQL 인젝션은 사용자 입력이 파라미터 바인딩 없이 쿼리 문자열에 직접 결합될 때 발생합니다. DB는 해당 입력을 단순한 값이 아닌 SQL 구문으로 해석할 수 있어, WHERE 절 조작, UNION 주입, 서브쿼리/함수 호출 등이 가능해집니다. 공격자는 따옴표, OR, UNION, 주석(--) 등의 토큰을 섞어 인증을 우회하거나 데이터 조회·변조·삭제를 수행할 수 있습니다. Sequelize에서 raw query(sequelize.query)를 사용할 때 문자열 연결로 사용자 입력을 넣으면 쉽게 악용됩니다.
잠재적 영향
인증 우회: 로그인/권한 체크 쿼리를 조작해 비인가 사용자가 정상 사용자로 가장할 수 있습니다.
데이터 유출: UNION, 서브쿼리 등을 주입해 민감한 정보를 대량으로 조회할 수 있습니다.
데이터 변조/삭제: INSERT/UPDATE/DELETE, DROP 등을 유발하여 데이터 손실이나 위변조가 발생할 수 있습니다.
서비스 거부(DoS): 복잡한 쿼리나 대기 함수(SLEEP 등)를 주입해 DB/애플리케이션 자원을 고갈시킬 수 있습니다.
해결 방법
파라미터 바인딩 사용: sequelize.query에 replacements 또는 bind 옵션을 사용해 값을 바인딩합니다. 예) '... WHERE id = ?' + { replacements: [id] } 또는 '... WHERE id = $id' + { bind: { id } }.
문자열 연결 금지: 사용자 입력을 쿼리 문자열에 직접 이어붙이지 않습니다.
식별자(테이블/컬럼명) 처리: placeholder는 식별자에 사용할 수 없습니다. 필요한 경우 미리 정의된 allowlist에서 매핑하여 선택합니다.
ORM API 우선 사용: 가능한 경우 Model.findAll/findOne 등 Sequelize의 attribute/where 옵션으로 쿼리를 작성해 내부 바인딩을 활용합니다.
불가피한 경우 escape 사용: sqlstring 또는 커넥터의 escape API로 값(리터럴)만 안전하게 이스케이프합니다(식별자에는 사용 금지).
입력 검증: 허용 목록, 길이/형식 체크(숫자 파싱 등)로 예상치 못한 토큰 입력을 차단합니다.
취약한 코드 및 안전한 코드 예시
취약한 코드
안전한 코드
설명:
취약한 코드: 사용자 입력을 쿼리 문자열에 직접 삽입했습니다. 공격자는 term에 ' OR 1=1 -- 같은 토큰을, col에는 price FROM users -- 등의 페이로드를 넣어 WHERE 절 우회, 다른 테이블 조회, 구조적 변경을 유발할 수 있습니다. Sequelize가 raw SQL을 그대로 DB로 전달하므로 입력이 SQL로 해석됩니다.
안전한 코드: - 값(리터럴)은 replacements로 바인딩하여 DB가 데이터를 SQL 구문이 아닌 값으로만 처리하게 했습니다.
컬럼명처럼 바인딩이 불가능한 식별자는 미리 정의한 allowlist에서만 선택하도록 제한했습니다.
ORM API 사용 시에도 내부적으로 Prepared Statement/파라미터 바인딩이 적용되어 같은 위험을 예방합니다.
참조
Last updated