본문 바로가기

프로그래밍/미분류

[ASP] SQL 인젝션 방지법: 패러미터화된 쿼리를 사용한다

반응형

SQL 인젝션 방지법: 패러미터화된 쿼리를 사용한다

SQL Injection Mitigation: Using Parameterized Queries

출처 : https://blogs.technet.microsoft.com/neilcar/2008/05/21/sql-injection-mitigation-using-parameterized-queries/

neilcarMay 21, 200814

마이클 하워드가 어제 SDL에 SQL인젝션에 어떻게 대응하는지에 대한 훌륭한 아티클 을 썼다. 그는 대응을 위한 3가지 코딩 필수요건/방어책을 살폈다.

  • 패러미터화된 SQL 쿼리를 사용하라.
  • 저장프로시져를 사용하라.
  • 실행-only 권한을 사용하라.

Michael Howard wrote an excellent article yesterday on how the SDL addresses SQL injection. He walks through three coding requirements/defenses:

  • Use SQL Parameterized Queries
  • Use Stored Procedures
  • Use SQL Execute-only Permissions

마이클이 지적한 대로, 첫번째 질문에 대한 진정한 해답은 오직 1번 방책 뿐이다. 나머지 두가지는 추가적인 방어를 제공할 뿐이다.

As Michael points out, only the first, parameterized queries, remedies the problem. The other two provide additional defense.

좋은 소식은 ASP 페이지를 쿼리문자열을 만들어 쿼리를 하는 방법에서 패러미터화된 쿼리로 바꾸는 것이 매우 간단하다는 것이다. 나쁜 소식은 MSDN에는 ASP에서 패러미터화된 쿼리를 하는 샘플이 많지 않다는 점이다. 그래서 간단한 예를 보이려 한다.

The good news is that changing your ASP pages to use parameterized queries instead of just dynamically building the query is dead simple. The bad news is that MSDN doesn’t have a lot of samples of how to do parameterized queries in ASP so I thought providing one would be helpful.

예를 들어, 내가 생각하기에 많은 SQL 인젝션을 받은 웹사이트들은 대충 다음과 같이 생겼을 것이다.

As an example, I’m sure that a lot of the websites that have been compromised recently via SQL injection have something like this:

Set objConnection = Server.CreateObject("ADODB.Connection") 
objConnection.Open "Provider=SQLOLEDB;Data Source=SQLSERVER;" _ 
    & "Initial Catalog=website;User Id=user;Password=password;" _ 
    & "Connect Timeout=15;Network Library=dbmssocn;" 
strSQL = "SELECT name, info FROM [companies] WHERE name =" & strSearch & "';" 
Set objSearchResults = objConnection.Execute(strSQL)

이런 코드는 보안에 극히 취약하다. 왜냐하면, (웹폼에서 받은 문자열이 그대로 쿼리로 전달되는) 사용자 입력을 가져다가 SQL 문에 붙여버리기 때문이다.

This code is going to be extremely vulnerable to SQL injection since it’s just taking the user input (which was passed in via a query string from a web form) and pasting it into the SQL statement.

매개변수화(parameterizaion)는 "실행" 코드 ( "SELECT name, info..." ) 와 사용되는 "데이터" ( strSearch ) 를 분리한다는 점에서 좋다. 위 코드를 약간만 변경하면 패러미터화한 쿼리로 바꿀 수 있다. 이렇게 작은 수정만으로 취약점을 방어할 수 있다.

The good thing about parameterization is that it separates the ‘executable’ code (“SELECT name, info…”) from the ‘data’ (strSearch) we’re using. With a few changes, we can make this code use parameters for the query and, with this small change, defend against being exploited in this way.

Set objConnection = Server.CreateObject("ADODB.Connection") 
objConnection.Open "Provider=SQLOLEDB;Data Source=SQLSERVER;" _ 
    & "Initial Catalog=website;User Id=user;Password=password;" _ 
    & "Connect Timeout=15;Network Library=dbmssocn;" 
strSql = "SELECT name, info FROM [companies] WHERE name = ?;" 
set objCommand = Server.CreateObject("ADODB.Command") 
objCommand.ActiveConnection = objConnection
objCommand.CommandText = strSql 
objCommand.Parameters(0).value = strSearch 
Set objSearchResults = objCommand.Execute()

변경된 위 코드를 설명하면 다음과 같다.

  • SQL 쿼리 문장의 질의문자열 부분을 ? (물음표)로 바꾼다. (매개변수에 대한 위치지정자)
  • 커맨드 실행을 위한 Command 객체를 생성한다.
  • 액티브 커넥션과 커맨드 문자열을 Command 객체(ActiveConnection, CommandText)에 assign 한다.
  • 첫번째 매개변수 값( parameters(0).value )을 검색문자열로 설정한다.
  • 커맨드를 실행(Execute)한다.

All that we needed to do was:

  • Replace the query string in our SQL squery statement with a ? (which is the placeholder for a parameter).
  • Create a new Command object for our command.
  • Assign our active connection and command text to the Command object.
  • Set the first parameter in the parameters collection to our dynamic string.
  • Execute the command.

둘 이상의 패러미터를 쿼리에서 사용한다면, 쿼리 텍스트(strSql)의 해당 부분에 물음표를 추가하고, 매개변수를 추가하면 된다. 예를 들면 아래와 같다.

If we needed to use multiple parameters in our query, we’d add additional question marks to strSQL and additional parameters to the Parameters collection. For example:

...
strSql = "SELECT name, info FROM [companies] WHERE name = ?" _ 
          & "AND info = ?;" 
... 
objCommand.Parameters(0).value = strName 
objCommand.Parameters(1).value = strInfo 
...

여기 중요한 주의점이 있는데, 위 예제 코드는 퍼포먼스 문제가 있을 수 있다. 왜냐하면, 패러미터들의 타입(형)을 지정하지 않았기 때문이다. 즉, 타입(형)을 지정하지 않으면, ADO는 실제 실행 전에 패러미터의 형을 알아내기 위해서 SQL 서버를 다녀와야 한다는 것이다. 이 문제는 패러미터를 적절한 타입(형)을 지정하여 만들면 해결할 수 있다. 이렇게 하면서, 입력값에 대한 밸리데이션도 할 수 있다는 것은 보너스이다. 흥미가 있다면, 다음번에 형이 지정된 패러미터 쿼리에 대한 글도 쓰겠다. ( 여기 있음. )

There is a BIG caveat on this — the method I show above has a performance hit because I haven’t specified the types of the parameters. This means that ADO has to make a roundtrip to the SQL server to figure out the type before actually using it. You can fix this by creating parameters objects with the appropriate type which would have the added bonus of doing simple input validation as well. If there’s interest, I’ll write a followup in the next few weeks with some samples of typed, parameterized queries. (EDIT: Written, it’s here.)

다른 부가적 정보는 MSDN 을 참조하면 된다. NomadPete 은 좀 더 심화된 논의를 한다.

Additional info is available on MSDN here. NomadPete has a fuller walkthrough here that covers parameterized queries and stored procedures.

언제나 그렇듯이, 이 방법은 SQL 인젝션 방어를 위한 한가지일 뿐이다. 하지만, 또한 이 변경이 가장 중요한 방법일 것이다.

As always, this is only part of the job in securing against SQL injection; however, it is probably the single most useful change you could make.

(Big thanks to Bala Neerumalla for tech reviewing this for me.)

(Edit: Fixed two minor issues with the code examples. Thanks, Steve!)

728x90