[시큐어코딩 가이드] 2-1-1. SQL 삽입

입력데이터 검증 및 표현

프로그램 입력값에 대한 검증 누락 또는 부적절한 검증, 데이터의 잘못된 형식지정, 일관되지 않은 언어셋 사용 등으로 인해 발생되는 보안 약점으로 SQL 삽입, 크로스사이트 스크립트(XSS) 등의 공격을 유발할 수 있다.

 

SQL 삽입

정의

  • DB와 연동된 웹 응용프로그램에서 입력된 데이터 대한 유효성 검증을 하지 않을 경우, 공격자가 입력폼 및 URL 입력란에 SQL 문을 삽입하여 DB로부터 정보를 열람하거나 조작할 수 있는 보안약점을 의미한다.

 

안전한 코딩기법

  • DB API 사용시 인자화된 쿼리를 통해 외부 입력값을 바인딩해서 사용하면 SQL 삽입 공격으로부터 한전하게 보호할 수 있다.
  • ORM 프레임워크(Django의 querySets, SQLAlchemy, Storm)을 사용한다.

*인자화된 쿼리 : 사용자가 전달한 입력값을 그대로 쿼리 문자열로 만들지 않고, DB API에서 제공하는 기능을 사용해 쿼리 내에 사용자 입력값을 구성하는 방법

 

코드 예제

안전하지 않은 코드 예제입니다. 입력값을 name, id에 저장하고 별도의 검증 없이 쿼리문의 인자값으로 사용하는 단순문자열 결합을 통해 쿼리를 생성하고 있습니다. 이렇게 되면 테이블 전체 레코드의 name 컬럼의 내용이 공격자가 전달한 name 값으로 변경됩니다.

# SQL삽입(안전X)
def update_board(request):
    conn = connection
    with conn.cursor() as c:
        name = request.POST.get('name',)
        id = request.POST.get('id', )
        sql_query = "update hello_user set name = '" + name + "'where id= " + id

        c.execute(sql_query)
        conn.commit()
        return render(request, "success.html")

 

다음은 안전한 코드 예제입니다. 인자화된 쿼리 생성 후  execute()의 두번째  인자값으로 바인딩해서 쿼리문을 실행합니다. 매개변수 바인등을 통해 execute()를 호출하면 공격자가 쿼리를 변조하는 ㄱ밧을 삽입하더라도 해당 값이 바인딩된 매개변수의 값으로만 사용되기 때문에 안전합니다.

# SQL삽입(안전O)
def update_board_good(request):
    conn = connection
    with conn.cursor() as c:
        name = request.POST.get('name', )
        id = request.POST.get('id', )
        sql_query = "update hello_user set name=%s where id=%s"
        c.execute(sql_query, (name, id))
        conn.commit()
        return render(request, "success.html")

 

ORM은 객체를 연결해준다는 의미입니다. Django의 ORM 프레임워크는 원시 SQL 쿼리를 수행하기 위해 Manager.raw() 기능을 제공합니다.

#ORM 사용(안전O)
def update_board_ORM(request):
    name = request.POST.get('name', )
    id = request.POST.get('id', )

    sql_query = "update blog_user set name=%s where id=%s"

    User.objects.raw(sql_query, (name, id))
    return render(request, "success.html")

 

 

 

입력데이터 검증 및 표현/SQL 삽입

[참고문헌] Python 시큐어코딩 가이드(2022) / KISA(한국인터넷진흥원)