다음을 통해 공유


CA2100: 보안상 취약한 부분이 있는지 SQL 쿼리를 검토하십시오.

TypeName

ReviewSqlQueriesForSecurityVulnerabilities

CheckId

CA2100

범주

Microsoft.Security

변경 수준

주요 변경 아님

원인

메서드가 메서드에 대한 문자열 인수로부터 만들어진 문자열을 사용하여 IDbCommand.CommandText 속성을 설정합니다.

규칙 설명

이 규칙에서는 문자열 인수에 사용자 입력이 포함된 것으로 가정합니다.사용자 입력으로부터 만들어진 SQL 명령 문자열은 SQL 삽입 공격에 취약합니다.SQL 삽입 공격을 수행하는 악의적 사용자는 내부 데이터베이스에 무단으로 액세스하거나 내부 데이터베이스를 손상시키기 위해 쿼리의 디자인을 변경하는 입력을 제공합니다.이러한 일반적인 기술에는 SQL 리터럴 문자열 구분 기호인 작은따옴표나 아포스트로피, SQL 주석을 나타내는 두 개의 대시, 새 명령이 시작됨을 나타내는 세미콜론을 삽입하는 것이 포함됩니다.사용자 입력이 쿼리의 일부여야 하는 경우 효과가 큰 순서로 나열한 다음 방법 중 하나를 사용하여 공격의 위험을 줄여야 합니다.

  • 저장 프로시저를 사용합니다.

  • 매개 변수가 있는 명령 문자열을 사용합니다.

  • 명령 문자열을 만들기 전에 사용자 입력의 형식과 내용 모두에 대해 유효성 검사를 수행합니다.

다음 .NET Framework 형식은 CommandText 속성을 구현하거나 문자열 인수를 사용하여 속성을 설정하는 생성자를 제공합니다.

ToString 메서드 형식을 명시적이거나 암시적으로 사용하여 쿼리 문자열을 생성할 때 이 규칙이 위반됩니다.예를 들면 다음과 같습니다.

int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";

악의적인 사용자가 ToString() 메서드를 무시할 수 있으므로 규칙이 위반됩니다.

또한 규칙은 ToString이 암시적으로 사용될 때 위반됩니다.

int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);

위반 문제를 해결하는 방법

이 규칙 위반 문제를 해결하려면 매개 변수가 있는 쿼리를 사용합니다.

경고를 표시하지 않는 경우

명령 텍스트에 사용자 입력이 포함되지 않는 경우 이 규칙에서 경고를 표시하지 않아도 안전합니다.

예제

다음 예제에서는 규칙을 위반하는 UnsafeQuery 메서드와 매개 변수가 있는 명령 문자열을 사용하여 규칙을 충족하는 SaferQuery 메서드를 보여 줍니다.

Imports System
Imports System.Data
Imports System.Data.SqlClient

Namespace SecurityLibrary

   Public Class SqlQueries

      Function UnsafeQuery(connection As String, _ 
         name As String, password As String) As Object

         Dim someConnection As New SqlConnection(connection)
         Dim someCommand As New SqlCommand()
         someCommand.Connection = someConnection

         someCommand.CommandText = "SELECT AccountNumber FROM Users " & _ 
            "WHERE Username='" & name & "' AND Password='" & password & "'"

         someConnection.Open()
         Dim accountNumber As Object = someCommand.ExecuteScalar()
         someConnection.Close()
         Return accountNumber

      End Function

      Function SaferQuery(connection As String, _ 
         name As String, password As String) As Object

         Dim someConnection As New SqlConnection(connection)
         Dim someCommand As New SqlCommand()
         someCommand.Connection = someConnection

         someCommand.Parameters.Add( _ 
            "@username", SqlDbType.NChar).Value = name
         someCommand.Parameters.Add( _ 
            "@password", SqlDbType.NChar).Value = password
         someCommand.CommandText = "SELECT AccountNumber FROM Users " & _  
            "WHERE Username=@username AND Password=@password"

         someConnection.Open()
         Dim accountNumber As Object = someCommand.ExecuteScalar()
         someConnection.Close()
         Return accountNumber

      End Function

   End Class 

   Class MalaciousCode

      Shared Sub Main(args As String())

         Dim queries As New SqlQueries()
         queries.UnsafeQuery(args(0), "' OR 1=1 --", "anything")
         ' Resultant query (which is always true): 
         ' SELECT AccountNumber FROM Users WHERE Username='' OR 1=1

         queries.SaferQuery(args(0), "' OR 1 = 1 --", "anything")
         ' Resultant query (notice the additional single quote character):
         ' SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --' 
         '                                   AND Password='anything'
      End Sub

   End Class

End Namespace
using System;
using System.Data;
using System.Data.SqlClient;

namespace SecurityLibrary
{
   public class SqlQueries
   {
      public object UnsafeQuery(
         string connection, string name, string password)
      {
         SqlConnection someConnection = new SqlConnection(connection);
         SqlCommand someCommand = new SqlCommand();
         someCommand.Connection = someConnection;

         someCommand.CommandText = "SELECT AccountNumber FROM Users " +
            "WHERE Username='" + name + 
            "' AND Password='" + password + "'";

         someConnection.Open();
         object accountNumber = someCommand.ExecuteScalar();
         someConnection.Close();
         return accountNumber;
      }

      public object SaferQuery(
         string connection, string name, string password)
      {
         SqlConnection someConnection = new SqlConnection(connection);
         SqlCommand someCommand = new SqlCommand();
         someCommand.Connection = someConnection;

         someCommand.Parameters.Add(
            "@username", SqlDbType.NChar).Value = name;
         someCommand.Parameters.Add(
            "@password", SqlDbType.NChar).Value = password;
         someCommand.CommandText = "SELECT AccountNumber FROM Users " + 
            "WHERE Username=@username AND Password=@password";

         someConnection.Open();
         object accountNumber = someCommand.ExecuteScalar();
         someConnection.Close();
         return accountNumber;
      }
   }

   class MalaciousCode
   {
      static void Main(string[] args)
      {
         SqlQueries queries = new SqlQueries();
         queries.UnsafeQuery(args[0], "' OR 1=1 --", "anything");
         // Resultant query (which is always true): 
         // SELECT AccountNumber FROM Users WHERE Username='' OR 1=1

         queries.SaferQuery(args[0], "' OR 1 = 1 --", "anything");
         // Resultant query (notice the additional single quote character):
         // SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
         //                                   AND Password='anything'
      }
   }
}
#using <System.dll>
#using <System.Data.dll>
#using <System.EnterpriseServices.dll>
#using <System.Transactions.dll>
#using <System.Xml.dll>
using namespace System;
using namespace System::Data;
using namespace System::Data::SqlClient;

namespace SecurityLibrary
{
   public ref class SqlQueries
   {
   public:
      Object^ UnsafeQuery(
         String^ connection, String^ name, String^ password)
      {
         SqlConnection^ someConnection = gcnew SqlConnection(connection);
         SqlCommand^ someCommand = gcnew SqlCommand();
         someCommand->Connection = someConnection;

         someCommand->CommandText = String::Concat(
            "SELECT AccountNumber FROM Users WHERE Username='", 
            name, "' AND Password='", password, "'");

         someConnection->Open();
         Object^ accountNumber = someCommand->ExecuteScalar();
         someConnection->Close();
         return accountNumber;
      }

      Object^ SaferQuery(
         String^ connection, String^ name, String^ password)
      {
         SqlConnection^ someConnection = gcnew SqlConnection(connection);
         SqlCommand^ someCommand = gcnew SqlCommand();
         someCommand->Connection = someConnection;

         someCommand->Parameters->Add(
            "@username", SqlDbType::NChar)->Value = name;
         someCommand->Parameters->Add(
            "@password", SqlDbType::NChar)->Value = password;
         someCommand->CommandText = "SELECT AccountNumber FROM Users "  
            "WHERE Username=@username AND Password=@password";

         someConnection->Open();
         Object^ accountNumber = someCommand->ExecuteScalar();
         someConnection->Close();
         return accountNumber;
      }
   };
}

using namespace SecurityLibrary;

void main()
{
   SqlQueries^ queries = gcnew SqlQueries();
   queries->UnsafeQuery(Environment::GetCommandLineArgs()[1], 
      "' OR 1=1 --", "anything");
   // Resultant query (which is always true): 
   // SELECT AccountNumber FROM Users WHERE Username='' OR 1=1

   queries->SaferQuery(Environment::GetCommandLineArgs()[1], 
      "' OR 1 = 1 --", "anything");
   // Resultant query (notice the additional single quote character):
   // SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
   //                                   AND Password='anything'
}

참고 항목

개념

Secure ADO.NET Coding Guidelines