해커들의 백도어 SQL Injection

[제목] 해커들의 백도어 SQL Injection

해커들이 가장 애용하는 해킹 기법들 중의 하나로 SQL Injection 기술을 들 수 있다. SQL Injection이란 SQL 쿼리를 해커가 조작함으로써 일반적으로 노출되지 말아야 하는 정보를 해커가 얻어 내는 것이다. 그런데, 이러한 SQL Injection 문제는 개발자가 흔히 하는 데이타베이스 코딩을 통해 쉽게 노출되기 때문에, 개발자가 특히 주의해야 하는 부분이다.

이 아티클에서는 해커가 어떻게 데이타를 얻어올 수 있는지 예제를 통해 설명함으로써, 개발자의 실수가 어떻게 정보 누출로 연결되는지를 알아본다. SQL Injection은 데이타베이스를 사용하는 모든 플랫폼에서 일어날 수 있지만, 가장 일반적으로 웹 어플리케이션 예를 들어 보자. 아래는 간단한 웹페이지로 사용자가 검색할 저자명을 입력하면 해당 저자의 도서들을 보여주는 검색화면이다.

<html>
<head>    
</head>
<body>
    <form action="search.aspx" method="post">
        저자 검색 : <input type="text" name="q" size="100" />
        <input type="submit" value="Search" />
    </form>

</body>
</html>

입력창에 저자명을 쓰고 버튼을 누르면, search.aspx라는 웹페이지를 호출하는데, 이 웹폼은 다음과 같은 간단한 form을 갖고 있다고 가정하자.

// SEARCH.ASPX

<form id="form1" runat="server">
<div>
    <h2>결과 : <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></h2>
    <asp:ListBox ID="ListBox1" runat="server" Rows="10"></asp:ListBox>    
</div>
</form>

search.aspx의 code behind 코드는 다음과 같다. 즉, 입력 파라미터를 받아 book 이라는 테이블에서 저자명을 검색하여 해당 저자 도서들을 읽어오고 이를 ListBox1 아이템에 추가하는 것이다.

public partial class search : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string query = Request["q"];
        this.LoadData(query);
    }

    private void LoadData(string query)
    {
        string connStr = "Data Source=.;Integrated Security=SSPI;Database=Test";            
        using (var conn = new SqlConnection(connStr))
        {
            conn.Open();
            string sql = "SELECT title FROM book WHERE author='" + query + "'"; // 문제
            SqlCommand cmd = new SqlCommand(sql, conn);
            SqlDataReader reader = cmd.ExecuteReader();

            Label1.Text = query;

            while (reader.Read())
            {
                ListBox1.Items.Add(reader[0].ToString());
            }
        }
    }
}

언뜻 보기에는 아무 문제가 없어 보이는 코드이고, 실제 저자명을 쳐서 검색하면 제대로 결과가 나온다. 그렇다면, 어떻게 이를 해킹에 이용할 수 있을까? 문제는 쿼리를 직접 문자열 조작을 통해 SQL을 만들어 냈다는 점에 있다. 만약 저자명을 Albert라고 치면, SELECT 문장은 SELECT title FROM book WHERE author="Albert" 가 된다. 하지만, 해커가 검색어를 Albert" UNION SELECT name from sys.objects -- 와 같이 지정하면, 이는 Albert 도서명뿐만 아니라 현재 데이타베이스에 있는 모든 테이블명을 보여주게 된다. 마지막의 --를 SQL에서 코멘트로 인식하기 때문에 위의 라인 15의 마지막 단일 인용부호를 무시하게 한다. 이렇게 DB내의 테이블명을 알아 냈으면, 관심이 가는 테이블 예를 들어 Membership이나 Login등의 테이블을 조작할 수 있다. 즉 마찬가지로 Albert" UNION SELECT * from Membership --와 같이 입력을 주면 멤버쉽에 있는 고객명단 및 인적 사항들을 검색할 수 있다.

입력예

좀 더 나아가면, 멤버쉽 데이타는 갱신하거나 추가할 수 있는데, 예를 들어 Albert" ; UPDATE Membership SET Name="Kim" WHERE Id=1234 -- 와 같이 고객 이름을 변경하거나,
Albert" ; INSERT Membership(Name,Expire) VALUES("Lee","12/31/2015"); -- 와 같이 새 멤버쉽을 추가할 수도 있다.
또한 Albert" ; DELETE FROM Membership --와 같이 모든 테이블 데이타를 간단히 삭제할 수도 있다

이렇듯 개발자의 프로그래밍 실수는 해커들에 의해 쉽게 악용될 수 있기 때문에 특히 주의를 기울여야 하는 부분이다. 그렇다면 위의 경우 어떻게 이를 방지할 수 있는가? 이는 아래와 같이 SqlParameter를 이용한 쿼리를 사용함으로써 해결할 수 있다. 아래와 같이 Parameterized Query를 사용하면 해커가 UNION SELECT 등과 같은 추가 쿼리를 보내더라도, 해당 문자열 전체를 하나의 author 파라미터로 보기 때문에 SQL Injection을 방지할 수 있다.

private void LoadDataSafe(string query)
{
    string connStr = "Data Source=.;Integrated Security=SSPI;Database=Test";
    using (var conn = new SqlConnection(connStr))
    {
        conn.Open();
        string sql = "SELECT title FROM book WHERE author=@author";
        SqlCommand cmd = new SqlCommand(sql, conn);
        var p1 = new SqlParameter("@author", query);
        cmd.Parameters.Add(p1);
        SqlDataReader reader = cmd.ExecuteReader();

        Label1.Text = query;

        while (reader.Read())
        {
            ListBox1.Items.Add(reader[0].ToString());
        }
    }
}



본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.