Udostępnij za pośrednictwem


CA2100: Należy przeglądnąć zapytania SQL w poszukiwaniu luk w zabezpieczeniach

TypeName

ReviewSqlQueriesForSecurityVulnerabilities

CheckId

CA2100

Kategoria

Microsoft.Security

Zmiana kluczowa

Niekluczowa

Przyczyna

Metoda ustawia właściwość IDbCommand.CommandText przy użyciu ciąg, który jest zbudowany z argumentem ciągu do metody.

Opis reguły

Ta reguła zakłada, że argument ciągu zawiera dane wejściowe użytkownika.Ciąg polecenia SQL, który jest zbudowany z danych wejściowych użytkownika jest narażony na ataki przez iniekcję SQL.W atakach polegających na wstrzyknięciu kodu SQL złośliwy użytkownik podaje dane wejściowe, które zmieniają wygląd zapytania, w celu próby uszkodzenia lub uzyskania nieautoryzowanego dostępu do źródłowej bazy danych.Typowe techniki obejmują iniekcję pojedynczego cudzysłowu lub apostrofu, który jest ogranicznikiem ciągu literału SQL; dwóch kresek, które oznaczają komentarz SQL; i średnika, który wskazuje, że po nim następuje nowe polecenie.Jeśli dane wejściowe użytkownika muszą być częścią kwerendy, użyj jednej z następujących, wymienionych w kolejności skuteczności, aby zmniejszyć ryzyko ataku.

  • Użyj procedury przechowywanej.

  • Użyj ciągu polecenia sparametryzowanego.

  • Sprawdź poprawność danych wejściowych użytkownika zarówno pod względem typu, jak i zawartość przed budową ciągu polecenia.

Następujące typy .NET Framework implementują właściwość CommandText lub zapewniają konstruktory, które ustawiają właściwość za pomocą argumentu ciągu.

Należy zauważyć, że ta reguła jest naruszona, gdy metoda ToString typu jest używana jawnie lub niejawnie do skonstruowania ciągu kwerendy.Oto przykład.

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

Reguła jest naruszona, ponieważ złośliwy użytkownik może zastąpić metodę ToString().

Reguła jest również naruszana, gdy ToString używane jest jawnie.

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

Jak naprawić naruszenia

Aby naprawić naruszenie tej reguły, należy użyć kwerendy parametrycznej.

Kiedy pominąć ostrzeżenia

Można bezpiecznie pominąć ostrzeżenie od tej reguły, jeśli tekst polecenia nie zawiera żadnych danych wejściowych użytkownika.

Przykład

Poniższy przykład ilustruje metodę UnsafeQuery, która narusza tę regułę, oraz metodę SaferQuery, która spełnia tę regułę, przy użyciu sparametryzowanego ciągu polecenia.

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'
}

Zobacz też

Koncepcje

Security Overview [wd_adonet]