flawopen.com/SQL Injection/ORM만으로 충분한가?
ORM을 쓰면 SQL 인젝션을 막기에 충분한가?
ORM은 주문서에 따라 음식을 만드는 주방과 같습니다. 일반 쿼리 빌더는 값을 분리합니다. 원시 SQL 지름길은 별도의 통로이며 규칙도 다릅니다. 보간된 값을 신뢰하기 전에 API를 확인하세요.
var sql = $"SELECT * FROM dbo.Blogs WHERE Url = '{url}'";
var rows = await context.Blogs
.FromSqlRaw(sql)
.ToListAsync();
// User text is inserted into SQL before execution.
var rows = await context.Blogs
.FromSql($"SELECT * FROM dbo.Blogs WHERE Url = {url}")
.ToListAsync();
// EF Core 7+: interpolation becomes a SQL parameter.
모든 ORM에 공통된 패턴
ORM 쿼리 빌더와 원시 SQL API는 동일한 보안 경계가 아닙니다. 공식 문서는 Django raw(sql, params), 실행 매개변수를 분리하는 SQLAlchemy text(), Rails 플레이스홀더 조건, EF Core 7+ FromSql / FromSqlInterpolated, GORM Raw(sql, args...), Prisma 태그 템플릿 $queryRaw의 안전한 값 바인딩을 설명합니다. API 동작은 서로 다릅니다. 별도 매개변수를 제공하면 FromSqlRaw를 안전하게 사용할 수 있지만 SQL 문자열에 사용자 텍스트를 넣으면 위험합니다. $queryRawUnsafe도 값을 따로 바인딩할 수 있지만 신뢰할 수 없는 문자열을 연결하면 위험합니다. 메서드 이름만으로 값 처리 방식을 알 수 없습니다. 각 API의 문서화된 방식으로 모든 값을 바인딩하세요. SQL 식별자와 쿼리 구조는 매개변수화할 수 없으므로 애플리케이션의 고정 허용 목록에서 선택하세요. 자세한 내용은 아래 가이드를 참고하세요.
"충분"이 실제로 요구하는 것
- ✓일반 값과 필터에는 ORM 쿼리 빌더를 사용하세요.
- ✓원시 SQL에서는 API가 문서화한 방식으로 모든 값을 바인딩하세요.
- ✓동적 식별자는 애플리케이션의 고정 허용 목록에서 선택하세요. SQL 구조는 매개변수화할 수 없습니다.
- ✓원시 쿼리에서 문자열 연결, 보간, 위험한 메서드 변형을 검토하세요.
FAQ
아닙니다. 일부 원시 SQL API는 별도 전달된 값을 바인딩하고, 안전한 API는 보간된 값을 매개변수로 받습니다. EF Core 7+ FromSql은 FormattableString을 받지만 FromSqlRaw에는 매개변수를 별도로 전달해야 합니다. Prisma $queryRawUnsafe도 값을 바인딩할 수 있지만 직접 만든 SQL 텍스트를 정화하지는 않습니다. 메서드 계약을 확인하고 신뢰할 수 없는 값이 SQL 구조에 들어가지 않게 하세요.