flawopen.com/SQL Injection in PHP/Doctrine ORM
Doctrine ORM은 SQL 인젝션에 안전한가요?
Reference page
짧은 답변
매개변수를 바인딩한 DQL은 안전합니다. 문자열 연결로 DQL이나 네이티브 SQL을 만들면 일반 raw query와 같은 위험이 생깁니다.
취약 — 입력이 DQL 구문이 됨
$em->createQuery( "SELECT u FROM User u " . "WHERE u.id = $userId" );
안전 — 신뢰된 표현식만 사용
$qb->select('u')
->from('User', 'u')
->where('u.id = :id')
->setParameter('id', $userId);
바인딩된 값은 쿼리 구조를 보호하지 않습니다
setParameter()는 DQL 값을 바인딩하고 Doctrine은 이를 쿼리 문자열과 분리된 SQL 매개변수로 변환합니다. createQuery()에 입력을 연결하면 보호를 우회합니다. 네이티브 SQL도 마찬가지입니다. Connection::executeQuery()는 placeholder와 매개변수 배열을 사용할 때 안전하며, 입력을 SQL 문자열에 삽입하면 안전하지 않습니다.
매개변수는 식별자를 대체하지 않습니다
Placeholder는 값만 나타내며 DQL 식별자, SQL 열/테이블명, 연산자 또는 정렬 방향을 대신하지 않습니다. ORM 보안 문서는 QueryBuilder 표현식 메서드에 사용자 입력을 전달하면 DQL 인젝션이 가능하다고 경고합니다. 표현식은 신뢰된 코드에 두고 정렬 선택은 엄격한 허용 목록으로 매핑하세요.
취약 — 입력이 DQL 구문이 됨
$qb->orderBy(
'u.' . $request->query->get('sort'),
$request->query->get('direction')
);
안전 — 신뢰된 표현식만 사용
$fields = [
'name' => 'u.name',
'created' => 'u.createdAt',
];
$directions = ['asc' => 'ASC', 'desc' => 'DESC'];
$key = (string) $request->query->get('sort', 'name');
$sort = $fields[$key] ?? $fields['name'];
$requestedDirection = strtolower(
(string) $request->query->get('direction', 'asc')
);
$direction = $directions[$requestedDirection] ?? 'ASC';
$qb->orderBy($sort, $direction);
- ✓외부 값은 모두 setParameter() 또는 DBAL placeholder로 바인딩하고 DQL/SQL에 연결하지 마세요.
- ✓QueryBuilder 표현식 메서드와 DQL 조각을 코드로 취급하세요. 필드, 정렬 방향, 연산자, 함수는 허용 목록으로 제한하세요.
- ✓네이티브 SQL에는 placeholder가 있는 executeQuery($sql, $params) 또는 executeStatement($sql, $params)를 사용하세요.
- ✓데이터 흐름과 쿼리 구조를 함께 검토하세요. 바인딩된 값도 권한 없는 레코드를 노출할 수 있으므로 권한 검사는 별도로 하세요.
코드베이스 점검 방법
rg -n -g '*.php' 'createQuery\(|orderBy\(|addOrderBy\(|executeQuery\(' .각 결과에서 문자열 연결/보간, 표현식 API에 전달되는 사용자 입력, 고정 허용 목록 밖의 식별자를 확인하세요. 검색 결과는 검토 후보이지 악용 가능성의 증거는 아닙니다.