FlawOpen

flawopen.com/C# SQL Injection/EF Core FromSqlRaw

Entity Framework Core의 FromSqlRaw는 안전한가요?

참고 가이드
짧은 답변

값이 SQL 텍스트와 분리되어 있으면 안전합니다. FromSqlRaw는 raw SQL을 받으므로 신뢰할 수 없는 입력을 문자열에 연결하거나 보간하면 쿼리가 바뀔 수 있습니다. 별도의 매개변수 인수를 사용하거나 보간 결과인 FormattableString을 FromSqlInterpolated(또는 EF Core 7+의 FromSql)에 전달하세요. 매개변수는 봉인된 값 슬롯과 같습니다. 데이터베이스에는 값만 전달되고 새 SQL 명령은 전달되지 않습니다.

위험 — 보간 결과가 일반 문자열이 됨
// 보간은 먼저 문자열로 평가됩니다.
var sql = $"SELECT * FROM Users WHERE Id = {id}";

// FromSqlRaw에는 SQL 텍스트가 전달되며 FormattableString이 아닙니다.
var query = context.Users.FromSqlRaw(sql);
안전 — 값이 매개변수로 유지됨
// EF Core 3–6: 위치 placeholder와 별도 값.
var byRawParameter = context.Users.FromSqlRaw(
    "SELECT * FROM Users WHERE Id = {0}", id);

// EF Core 3+: 보간 문자열을 매개변수 API에 전달하세요.
var byInterpolation = context.Users.FromSqlInterpolated(
    $"SELECT * FROM Users WHERE Id = {id}");

// EF Core 7+: FromSql도 FormattableString을 받습니다.
var byFromSql = context.Users.FromSql(
    $"SELECT * FROM Users WHERE Id = {id}");

헷갈리기 쉬운 핵심

FromSqlInterpolated는 FormattableString을 받습니다. EF Core는 형식과 인수를 읽어 보간된 각 값을 별도의 데이터베이스 매개변수로 전송하며, 컴파일 시점에 가로채는 방식이 아닙니다. EF Core 7에는 같은 안전한 보간 동작을 제공하는 FromSql이 추가되었습니다. 이전 버전에서는 FromSqlInterpolated를 사용하세요. FromSqlRaw도 {0} 같은 placeholder에 해당하는 값을 별도 인수로 전달하면 안전합니다. 보간 문자열을 먼저 string 변수에 저장하면 매개변수 메타데이터가 이미 사라집니다.

매개변수는 값을 바인딩하며 열 이름은 바인딩하지 않습니다

데이터베이스 매개변수는 값을 나타내며 테이블명, 열 이름, SQL 연산자 또는 정렬 방향을 나타내지 않습니다. 쿼리 구조를 바꿔야 한다면 애플리케이션 코드의 고정 allow-list에서 식별자를 선택하세요. 식별자를 매개변수화하려 하거나 문자 필터링으로 allow-list를 대체하지 마세요. 권한과 쿼리 비용도 확인해야 합니다. 유효한 열도 민감한 데이터를 노출하거나 비용이 큰 스캔을 일으킬 수 있습니다.

코드베이스 확인 방법

rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .
각 호출에서 오버로드와 인수 형식을 확인하세요. 신뢰할 수 없는 데이터가 연결되는지, FromSqlRaw에 보간값이 전달되는지, FormattableString이 string으로 변환되는지 추적하세요. 동적 식별자는 별도로 검토합니다.

자주 묻는 질문

FromSqlRaw에 매개변수를 전달하면 안전한가요?

SQL에 placeholder가 있고 값을 별도 인수로 전달하면 안전합니다. 예: FromSqlRaw("... {0}", value). 값을 먼저 SQL 문자열에 연결하거나 보간하지 마세요.

C# 보간 문자열을 써도 FromSqlInterpolated가 안전한 이유는 무엇인가요?

FormattableString을 받아 형식과 값을 분리해 보존하기 때문입니다. EF Core는 각 값을 데이터베이스 매개변수로 바꿉니다. 먼저 string에 저장하거나 FromSqlRaw에 전달하면 보호가 사라집니다.

매개변수로 테이블명이나 열 이름을 대체할 수 있나요?

아니요. 매개변수는 값만 나타냅니다. 동적 식별자는 사용자 선택을 고정된 식별자 allow-list에 매핑하고 권한과 쿼리 비용을 검토하세요.

참고 자료

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