개발/Go

[Gin] GORM에서 안전한 쿼리 작성법 - SQL 인젝션 방지와 Named Parameter 전략

devhooney 2025. 6. 9. 08:09
728x90

SQL 인젝션은 여전히 실무에서 가장 빈번하게 발생하는 보안 위협 중 하나다.
Go 백엔드에서 ORM으로 GORM을 쓴다고 해도, 안전하지 않은 쿼리 작성은 여전히 인젝션 공격의 여지를 남긴다.

이번 글에서는 다음 내용을 상세히 다룬다.


 

 

✅ 목차
- SQL 인젝션이란?
- GORM에서 발생할 수 있는 인젝션 사례
- 안전한 쿼리 작성법
- 바인딩 방식
- Named Parameter 전략
- Struct 기반 조건
- Raw SQL 시 주의할 점
- 실전 방어 전략 요약

 

 

728x90

 

 

 

 


 

1. SQL 인젝션이란?


SQL 인젝션(SQL Injection)이란, 쿼리문에 사용자 입력값이 그대로 삽입되면서 악의적인 SQL이 실행되는 공격이다.

예를 들어 아래처럼 문자열을 직접 연결하면:

db.Exec("DELETE FROM users WHERE name = '" + userInput + "'")

 

사용자가 userInput에 anything'; DROP TABLE users; -- 같은 값을 넣으면, 전체 테이블이 삭제될 수도 있다.
즉, 쿼리문이 조작되는 것이다.

 

 

 


 

2. GORM에서도 발생할 수 있다


ORM을 쓴다고 해서 무조건 안전한 건 아니다. 아래처럼 쿼리를 조합하거나 값을 직접 넣으면 위험하다.

❌ 인젝션 가능한 코드 예시

db.Where("name = " + userInput).Find(&user)

 

또는

db.Exec("UPDATE users SET status = 'active' WHERE name = '" + userInput + "'")

 

- userInput이 SQL 조작 문자열이면 그대로 실행됨.
- ORM이 내부적으로 쿼리를 생성하긴 하지만, 문자열을 직접 조작하면 소용없다.

 

 

 

 


 

3. 안전한 쿼리 작성법


✅ 방법 1: 바인딩된 변수 사용

db.Where("name = ?", userInput).Find(&user)

 

- ? 자리에 안전하게 바인딩됨.
- SQL 인젝션 방지의 기본.

 

 

✅ 방법 2: Named Parameters 사용
GORM은 map[string]interface{} 또는 gorm.Named를 통해 Named Parameter를 지원한다.

 

- map 방식

db.Where("name = @name AND age = @age", map[string]interface{}{
    "name": userInput,
    "age":  20,
}).Find(&user)

 

- gorm.Named 방식

db.Raw("SELECT * FROM users WHERE name = @name", gorm.Named("name", userInput)).Scan(&user)

 

Named Parameter는 여러 값이 있을 때 가독성도 좋고 안전성도 높다.

 

 

 

✅ 방법 3: Struct 기반 조건
GORM에서는 struct 자체를 조건으로 쓸 수 있다.

type Filter struct {
    Name string
    Age  int
}

db.Where(&Filter{Name: userInput, Age: 20}).Find(&user)

 

- 내부적으로 안전하게 필드 값을 추출해서 WHERE 절을 생성
- 단, zero value (0, "", false)는 조건에서 제외됨 (주의!)

 

 

 

 


 

4. Raw SQL 쓸 때 주의점


Raw SQL은 강력하지만, 인젝션 위험이 크기 때문에 반드시 바인딩을 써야 한다.

❌ 이렇게 쓰면 위험하다

db.Raw("SELECT * FROM users WHERE name = '" + userInput + "'").Scan(&user)

 

✅ 바인딩을 쓰자

db.Raw("SELECT * FROM users WHERE name = ?", userInput).Scan(&user)

 

또는

db.Raw("SELECT * FROM users WHERE name = @name", gorm.Named("name", userInput)).Scan(&user)

 

 

 


 

 

5. 실전 방어 전략 요약

 

방법 인젝션 방지 여부 가독성 추천도
? 바인딩 ✅ 안전함 보통 ★★★★☆
Named Parameters ✅ 매우 안전함 높음 ★★★★★
Struct 기반 조건 ✅ 안전함 좋음 ★★★★☆
Raw SQL 문자열 연결 ❌ 매우 위험 낮음 ☆☆☆☆☆

 

- ✅ 요약: 절대 문자열을 직접 쿼리에 붙이지 말고, 항상 ?, @name, struct를 이용해 바인딩하자.

 

 

 

 


 

 

🔚 마무리

 


GORM을 쓴다고 무조건 안전한 건 아니다.
쿼리를 어떻게 작성하느냐에 따라, SQL 인젝션을 막을 수도 있고 초래할 수도 있다.

신뢰할 수 없는 입력값은 절대 직접 쿼리에 삽입하지 말자.
작은 실수가 운영 DB를 위험에 빠뜨릴 수 있다.

728x90