Injetion : 보안 취약점을 이용하여, 임의의 SQL 문을 주입하여 데이터베이스가 비정상적인 동작을 하도록 조작하는 행위
** 📜 Content**
🗡️ Injection 공격 예시
/* 어떠한 POST방식 API에
아래와 같은 인자값을 넘기는 것이 정상적인 방법일 때*/
{ testKey : "testValue" }
/* 악의적인 사용자는 다음과 같이 인자값을 설정하여 쿼리를 조작할 수 있다. */
{ testKey : "testValue' and '해킹'='해킹" }
위 인자값이 SQL Query에 적용될 때 정상적인 인자값을 넘겨받은 쿼리
select * from testTable where key = 'testValue'
Injection 공격을 시도된 쿼리
select * from testTable where key = 'testValue' and '해킹'='해킹'
🛡️ python에 대한 injection 방어 코드
실무에서 사용된 DB 호출 코드 방식 (Injection 대응 전)
import psycopg2
from psycopg2.extras import RealDictCursor
# DB 연결 준비
conn = psycopg2.connect("dbname={} user={} host={} password={}".format(db_name, db_user, db_host, db_pass))
# DB 호출함수 선언
def fetch(conn, query):
result = []
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute(query) # SQL 실행
# DB조회 결과값 담기
raw = cursor.fetchall()
for line in raw:
result.append(line)
return result
# SQL 쿼리 선언
query = "select pjt_no, item_type from testTable "
query += "where pjt_no = '" + pjt_no + "' "
query += "and item_type = '" + type_cd2 + "'"
# DB 호출함수 실행
result = fetch(conn=conn, query=query)
Injection 대응 (Type 1)
import psycopg2
from psycopg2.extras import RealDictCursor
# DB 연결 준비
conn = psycopg2.connect("dbname={} user={} host={} password={}".format(db_name, db_user, db_host, db_pass))
# DB 호출함수 선언
def fetch(conn, query, sql_arg):
result = []
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute(query, sql_arg) # SQL 실행
# DB조회 결과값 담기
raw = cursor.fetchall()
for line in raw:
result.append(line)
return result
# SQL 쿼리 선언
query = "select pjt_no, item_type from testTable "
query += "where pjt_no = %(pjt_no)s"
query += "and item_type = %(item_type)s"
sql_arg = {
'pjt_no' : pjt_no,
'item_type' : item_type,
}
# DB 호출함수 실행
result = fetch(conn=conn, query=query, sql_arg=sql_arg)
Injection 대응 (Type 2)
(딕셔너리 타입(X) → tuple 타입으로 넘기는 것도 가능)
import psycopg2
from psycopg2.extras import RealDictCursor
# DB 연결 준비
conn = psycopg2.connect("dbname={} user={} host={} password={}".format(db_name, db_user, db_host, db_pass))
# DB 호출함수 선언
def fetch(conn, query, sql_arg):
result = []
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute(query, sql_arg) # SQL 실행
# DB조회 결과값 담기
raw = cursor.fetchall()
for line in raw:
result.append(line)
return result
# SQL 쿼리 선언
query = "select pjt_no, item_type from testTable "
query += "where pjt_no = %s"
query += "and item_type = %s"
sql_arg = (pjt_no, item_type)
# DB 호출함수 실행
result = fetch(conn=conn, query=query, sql_arg=sql_arg)슈슈
🎯 이슈
1. 정수형에 대한 Injection 대응
파이썬에서 %s는 문자형을 받을때 사용하고 정수는 %d로 받는다.
그럼 DB에서 컬럼이 int 타입일 때의 Injection 대응코드는?? 혹시 %(변수명)d 혹은 %d
확인 결과 : 똑같이 문자형으로 넘겨주면 된다.
DB에서 컬럼이 int 타입이라도 인자값은 문자형으로 만들어서 넘긴다. 즉, Argument 부터 정수여도 문자열로 형변환해서 넘긴다.
그것을 쿼리에서
%()s로 받아주면 된다.
test = 1 # 인자값
query = 'select * from testTable where key = %(str_test)s'
query_paramDict = { 'str_test' : str(test) }
cursor.execute(query, query_paramDict)
2. 테이블에 null값이 되도록 해야할 경우
update나 insert시 전달받은 인자값이 공백(‘‘)일 때, 테이블에는 부득이하게 null을 넣고자할 경우
# 'test'라는 변수에 전달받은 인자값이 공백일 경우
query = 'select * from testTable where key = %(str_test)s'
query_paramDict = { 'str_test' : str(test) }
if test is '': query = query.replace('%(str_test)s', 'null')
cursor.execute(query, query_paramDict)
※ 쿼리문 기준으로 보면 select * from testTable where key = null ( ~ ~ where key = 'null' ❌** )**