FlawOpen

flawopen.com/Rust의 SQL 인젝션/sqlx

sqlx의 컴파일 시 쿼리 검사가 SQL 인젝션을 막나요?

참고 페이지
핵심 답변

query!는 컴파일 시 리터럴 쿼리를 실제 데이터베이스 스키마와 대조합니다. 동적으로 조립한 쿼리를 사후에 안전하게 만들지는 않습니다. 바인딩 매개변수로 값을 SQL과 분리하세요.

위험 — 입력을 SQL에 삽입
let user_id: String = read_user_input();
let q = format!(
  "SELECT id FROM users WHERE id = '{}'",
  user_id
);
// Representative only: data and SQL syntax are mixed.
// Never pass user-influenced SQL as AssertSqlSafe.
안전 — query!로 값을 바인딩
let user_id: &str = read_user_input();
// PostgreSQL; query! checks the configured schema.
sqlx::query!(
  "SELECT id FROM users WHERE id = $1",
  user_id
).fetch_one(&pool).await?

컴파일 시 검사가 실제로 검증하는 항목

SQLx 0.9의 query! 매크로는 문자열 리터럴을 요구하며 빌드 시 설정된 데이터베이스 스키마를 기준으로 구문, 바인드 매개변수 형식, 결과 형식을 검사합니다. DATABASE_URL을 설정하거나 버전 관리되는 오프라인 메타데이터를 사용하세요. 이 코드 예제는 PostgreSQL 전용이며 플레이스홀더는 $1입니다. SQLx는 PostgreSQL과 SQLite에 $1, MySQL과 MariaDB에 위치 기반 ? 플레이스홀더를 권장합니다. SQL 구조가 고정되어 있거나 애플리케이션이 제어하는 경우 런타임 query()에서도 .bind()로 값을 바인딩할 수 있습니다. SQLx 0.9에서 동적 쿼리 문자열은 AssertSqlSafe를 통해 전달해야 합니다. 이 타입은 안전하다고 단언할 뿐 문자열을 정화하지 않습니다. 신뢰할 수 없는 보간 값이 든 문자열을 안전하다고 표시하는 데 절대 사용하지 마세요.

코드베이스 검사 방법

rg -n 'format!\s*\(|sqlx::query(_as)?\s*\(' --glob '*.rs'

자주 묻는 질문

항상 query() 대신 query! 매크로를 써야 하나요?

리터럴 쿼리와 빌드 시 스키마 요구사항이 프로젝트에 맞으면 query!를 우선하세요. 컴파일 시 SQL과 타입 검사도 수행합니다. 동적 query()도 모든 값을 .bind()로 바인딩하면 안전합니다. 어느 방식이든 신뢰할 수 없는 데이터를 SQL 문법에 삽입하지 마세요.

참고 자료

함께 보기 SQL 인젝션(CWE-89): 동작 원리와 언어별 방어 방법 — flawopen.com 저장 프로시저는 SQL 인젝션을 막나요? TypeORM QueryBuilder는 SQL Injection에 안전한가요? Django ORM SQL Injection: 안전한 패턴 Doctrine ORM SQL 인젝션: 안전한 쿼리 EF Core FromSqlRaw: 안전한 SQL 매개변수화 FastAPI SQL 인젝션: Pydantic이 막는 것과 막지 못하는 것