SQL-Injection을 비즈니스 계층에서 해결하기

오랫만입니다. 얼마전에 해킹 존에 올린 프로그램은 잘 사용하고 계시겠지요? 소리바다에서 제공하는 “파도”라는 플레이어를 비롯한 PLA 포맷을 지원하는 대부분의 오디오 플레이어에서 유용하게 쓰실 수 있을겁니다. ^^


오늘은 대부분의 데이터 베이스 기반 웹 사이트들이 겪는 보안 상의 취약점인 SQL-Injection을 해결하는 범용적인 솔루션을 연구해 보도록 하지요.


SQL-Injection이란, 매개 변수를 강력하게 검사하지 않고 문법의 일부로 더하는 프로그램에서 흔하게 벌어지는 공격 기법입니다. 유별난 공격법이 아니지만 막대한 양의 정보가 한꺼번에 유출되거나, Microsoft SQL Server와 같이 관리자에게 너무나도 풍족한 리소스를 주는 데이터 베이스 서버에서는 다른 파티션을 포맷하는것까지도 가능하게 합니다. (SQL Server에서 기본으로 제공하는 스토어드 프로시저 중에는 셸 명령을 실행할 수 있도록 하는 프로시저도 제공됩니다. 얼마든지 응용하기 나름인 셈이지요.) 무시무시한 이야기이지요.


SQL-Injection이 어떻게 벌어지는지 그 과정을 살펴보도록 하지요.


string commandText = “SELECT * FROM USERS WHERE USER_ID=’” + value + ““;


위와 같은 코드를 IDbCommand.CommandText에 지정하고 ExecuteReader() 등으로 실행하면 원하는 결과가 나오리라 흔히 생각합니다. 하지만 큰 착각입니다. value에 무엇이 들어가느냐에 따라서 다음과 같이 크게 변조되니까요.


value = “admin“; // 이것이 흔히 기대하는 형태입니다.


// 완성된 질의문: “SELECT * FROM USERS WHERE USER_ID=’admin


value = “admin’ AND USER_NAME=’관리자“; // 이와 같이 변형될 수 있습니다.


// 완성된 질의문: “SELECT * FROM USERS WHERE USER_ID=’admin’ AND USER_NAME=’관리자


이런 엄청난 사태가 발생하지 않도록 하려면 몇 가지 해결책이 있습니다.


1. 동일한 기능을 수행하는 스토어드 프로시저 또는 함수를 데이터 베이스 서버에 직접 구축하고, 웹 어플리케이션은 단지 이것을 호출하는 것으로 구조를 변경합니다. 위와 같이 문자열이 전달되었을 경우 “admin’ AND USER_NAME=’관리자” 라는 문자 자체가 하나의 값으로 처리되고, 질의문, 스토어드 프로시저, 함수의 기능에는 영향을 주지 않으므로 제일 안전하고 빠른 방법입니다.


2. 강력한 형식 검사를 수행합니다. 이것이 오늘 살펴볼 방법이며, 위와 같이 스토어드 프로시저나 함수를 지원하지 않는 데이터 베이스에서도 유용하게 쓰일 수 있는 방법입니다. 프로그래밍 방식으로서, 비즈니스 계층 단위로서 문제를 해결할 수 있는 것이 특징입니다.


코드 살펴보기


아래의 함수는 문제가 될 수 있는 문자열을 제거하는 기능을 수행합니다. pattern이라는 매개 변수에는 제거하고 싶은 문자를 지정하며, 이스케이프 시퀀스가 필요한 문자인 백슬래시, 작은 따옴표, 큰 따옴표와 공백 문자 등을 지정합니다.



    public static string EnsureString(string pattern, string target)
    {
       bool flag = false;
       StringBuilder sb = new StringBuilder(target.Length);

       for(int i=0; i<target.Length; i++)
       {
           for(int j=0; j<pattern.Length; j++)
           {
               // 조건에 해당되는 문자임을 통지하고 검사 과정을 파기합니다.
               if(target[i].Equals(pattern[j]))
               {
                   flag = true;
                   break;
               }
           }


           // 조건에 해당하는 문자임을 확인하고 검사 문자를 생략합니다.
           if(flag)
           {
               flag = false;
               continue;
           }


           // 검사 조건에 해당하지 않는 문자이므로 추가합니다.
           sb.Append(target[i]);
       }

       return sb.ToString();
    }


위의 함수를 사용하여 SQL-Injection을 막아보도록 하겠습니다.


string commandText = “SELECT * FROM USERS WHERE USER_ID=’” + EnsureString(“‘””, value) + ““;


// 공격 예시: “admin’ AND USER_NAME=’관리자“;


// 방어 결과: “admin AND USER_NAME=관리자“;


// 질의문 결과: “SELECT * FROM USERS WHERE USER_ID=admin AND USER_NAME=관리자


스토어드 프로시저나 함수를 이용했을 때와 같은 결과를 얻게 되었습니다. 이 함수를 사용함으로서 얻을 수 있는 또 하나의 장점은 이것이 SQL 질의문이 아니라 연결 문자열 등에도 활용될 수 있다는 점이며, 파일이나 디렉터리 경로를 통한 공격도 비슷한 형태로 막아낼 수 있다는 것이 장점입니다. 그리고 패턴은 얼마든지 추가가 가능하므로 공격 정보만 수집되면 몇 글자 추가하는 것으로 이와 유사한 형태의 공격을 방어할 수 있을 것입니다.


설날이 어느덧 돌아왔군요. 모두들 맛있는 떡국들 많이 드시고 올 해도 건강하십시오. ^^

댓글 남기기