FlawOpen

flawopen.com/Go의 SQL 인젝션/GORM Raw SQL과 SQL 인젝션

GORM Raw()는 SQL 인젝션을 막나요? Go 매개변수 바인딩

CWE-89: SQL Injection참고 페이지
요약

placeholder를 값이 담긴 봉인된 봉투라고 생각해 보세요. 데이터베이스 드라이버는 값을 SQL 명령과 분리해 보냅니다. placeholder를 쿼리에 남기고 값을 별도 인수로 전달하면 GORM Raw()가 안전하게 바인딩할 수 있습니다. 사용자 입력을 SQL 문자열에 format하면 봉투와 명령이 섞여 쿼리가 바뀔 수 있습니다.

취약한 예
// 사용자 제어 문자열이 SQL 구문이 됨
db.Raw(fmt.Sprintf(
  "SELECT * FROM users WHERE id = %s",
  userID,
)).Scan(&user)
안전한 예
// 드라이버가 userID를 값으로 바인딩
db.Raw(
  "SELECT * FROM users WHERE id = ?",
  userID,
).Scan(&user)

안전한 GORM Raw SQL과 매개변수 바인딩

placeholder가 SQL에 남고 인수를 별도로 전달되면 GORM과 Go database/sql은 값을 바인딩합니다. 예: db.Where("name = ?", userInput), db.Raw("SELECT ... WHERE id = ?", userID). placeholder는 값이지 SQL 구문이 아닙니다. 테이블명, 컬럼명, 정렬 방향, 연산자는 값 바인딩으로 안전해지지 않습니다. 동적 식별자는 고정 allowlist에서 선택하세요. 사용자 입력을 SQL 조각에 연결하면 Order, Select, Table, Raw 같은 메서드에 주입 위험이 생길 수 있습니다.

코드베이스 확인 방법

rg -n --glob '*.go' '\.(Raw|Exec|Order|Select|Table)\s*\(' . rg -n --glob '*.go' 'fmt\.(Sprintf|Sprint)' .

GORM Raw SQL 및 매개변수 FAQ

GORM의 체인형 쿼리 메서드는 항상 안전한가요?

아닙니다. Where("name = ?", input)처럼 값을 별도로 전달하면 매개변수 바인딩을 사용합니다. 입력을 조건 문자열에 연결하거나 raw SQL 구문으로 전달하면 여전히 쿼리 주입이 가능합니다. 일부 메서드는 이스케이프되지 않는 식별자나 SQL 조각을 받으므로 인수를 확인하고 동적 구조는 allowlist로 제한하세요.

placeholder로 테이블명이나 컬럼명을 바인딩할 수 있나요?

아니요. placeholder는 데이터 값을 나타냅니다. 동적 식별자나 정렬 방향은 애플리케이션에서 정한 고정 옵션으로 선택하고 검증된 상수만 SQL 구조에 넣으세요.

공식 출처 및 참고 자료

함께 보기 SQL 인젝션(CWE-89): 동작 원리와 언어별 방어 방법 — flawopen.com 따옴표 이스케이프만으로 SQL Injection을 막을 수 있을까? ORM만 쓰면 SQL 인젝션을 막기에 충분한가 Knex.js는 SQL Injection에 안전한가요? MyBatis ${}와 #{}: SQL Injection 방지 MySQLi와 PDO: 안전한 Prepared Statement NHibernate는 SQL Injection을 막을 수 있나요?