SQL 인젝션 위험 피하기
SQL 인젝션 위험 피하기 (Avoiding SQL injection risk)
SQL 파라미터 값을 sql 패키지 함수의 인자로 제공하면 SQL 인젝션 위험을 피할 수 있어요. sql 패키지의 많은 함수는 SQL 문을 위한 파라미터와 그 문의 파라미터에 사용될 값을 위한 파라미터를 함께 제공합니다(다른 함수들은 준비된 문(prepared statement)을 위한 파라미터와 값 파라미터를 제공하기도 해요).
출처: Go 공식 문서
다음 예시의 코드는 ? 기호를 id 파라미터의 자리 표시자(placeholder)로 사용하며, 그 값은 함수 인자로 제공됩니다:
// Correct format for executing an SQL statement with parameters.
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)
데이터베이스 연산을 수행하는 sql 패키지 함수들은 여러분이 제공한 인자로부터 준비된 문을 만듭니다. 실행 시점에 sql 패키지는 SQL 문을 준비된 문으로 바꾸고, 그것을 별도로 분리된 파라미터와 함께 보냅니다.
참고: 파라미터 자리 표시자는 사용하는 DBMS와 드라이버에 따라 달라져요. 예를 들어 Postgres용 pq 드라이버는 ? 대신 $1 같은 자리 표시자 형태를 받습니다.
fmt 패키지의 함수를 써서 SQL 문을 파라미터가 포함된 문자열로 조립하고 싶은 유혹이 들 수도 있어요. 이런 식으로요:
// SECURITY RISK!
rows, err := db.Query(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))
이건 안전하지 않아요! 이렇게 하면 Go가 전체 SQL 문을 조립하면서, %s 형식 동사를 파라미터 값으로 치환한 뒤, 완성된 문 전체를 DBMS로 보냅니다. 이는 SQL 인젝션 위험을 만들죠. 코드의 호출자가 id 인자로 예상치 못한 SQL 조각을 보낼 수 있기 때문입니다. 그 조각은 SQL 문을 예측할 수 없는 방식으로 완성시켜 애플리케이션에 위험을 초래할 수 있어요.
예를 들어 특정 %s 값을 전달하면, 아래와 같은 결과가 나올 수도 있는데, 이는 데이터베이스의 모든 사용자 레코드를 반환할 수 있습니다:
SELECT * FROM user WHERE id = 1 OR 1=1;
더 알아보기 (Learn more)
database/sql패키지 — SQL 데이터베이스 작업의 표준 인터페이스- Database access 개요 — Go에서 데이터베이스 다루기 시작하기
- SQL 인젝션(Wikipedia) — SQL 인젝션 공격에 대한 이해