Hazırlanan ifadeler SQL enjeksiyon saldırılarından nasıl korunabilir?


172

Hazırlanan ifadeler SQL enjeksiyon saldırılarını önlememize nasıl yardımcı olur ?

Wikipedia diyor ki:

Hazırlanan ifadeler SQL enjeksiyonuna karşı dayanıklıdır, çünkü daha sonra farklı bir protokol kullanılarak iletilen parametre değerlerinin doğru bir şekilde kaçmasına gerek yoktur. Özgün ifade şablonu harici girdiden türetilmemişse, SQL yerleştirme yapılamaz.

Sebebini çok iyi göremiyorum. Kolay İngilizce ve bazı örneklerde basit bir açıklama ne olurdu?

Yanıtlar:


290

Fikir çok basit - sorgu ve veriler ayrı ayrı veritabanı sunucusuna gönderilir .
Bu kadar.

SQL enjeksiyon sorununun kökü , kod ve verilerin karıştırılmasındadır.

Aslında, SQL sorgumuz meşru bir programdır . Ve böyle bir programı dinamik olarak yaratıyoruz, anında bazı veriler ekliyoruz. Bu nedenle, veriler her SQL enjeksiyon örneğinde gösterildiği gibi program koduna müdahale edebilir ve hatta değiştirebilir (PHP / Mysql'deki tüm örnekler):

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

düzenli bir sorgu oluşturur

SELECT * FROM users where id=1

bu kod

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

kötü niyetli bir sıra oluşturacak

SELECT * FROM users where id=1; DROP TABLE users;

Verileri doğrudan program gövdesine eklediğimizden ve programın bir parçası haline geldiği için çalışır, bu nedenle veriler programı değiştirebilir ve geçirilen verilere bağlı olarak, normal bir çıktıya veya bir tablo userssilinmiş olur.

İken hazırlanmış tablolar halinde programımızı değiştirmez, bu sağlam kalır
nokta bu.

Önce sunucuya bir program gönderiyoruz

$db->prepare("SELECT * FROM users where id=?");

burada veri, parametre veya yer tutucu adı verilen bir değişkenle değiştirilir .

Tam olarak aynı sorgunun herhangi bir veri olmadan sunucuya gönderildiğini unutmayın! Ve sonra verileri , esas olarak sorgunun kendisinden ayrı olan ikinci istekle gönderiyoruz :

$db->execute($data);

bu yüzden programımızı değiştiremez ve zarar veremez.
Oldukça basit - değil mi?

Eklemek zorunda olduğum tek şey, her kılavuzda her zaman atlandı:

Hazırlanan ifadeler yalnızca veri değişmezlerini koruyabilir , ancak diğer sorgu bölümleriyle birlikte kullanılamaz.
Dolayısıyla, dinamik bir tanımlayıcı (örneğin, bir alan adı, örneğin) hazırlanmış bir ifadeyi eklememiz gerektiğinde bize yardımcı olamaz. Ben ettik geçenlerde konuyu izah kendimi tekrar etmeyecek şekilde.


2
"örneğin, PDO varsayılan olarak hazırlanmış ifadeler kullanmaz" - bu tam olarak doğru değildir, çünkü PDO hazırlanmış ifadeleri yalnızca bu özelliği desteklemeyen sürücüler için taklit eder.
pinepain

3
@ zaq178miami: "PDO yalnızca bu özelliği desteklemeyen sürücüler için hazırlanmış ifadeleri öykünür" - tam olarak doğru değildir. MySQL, hazır ifadeleri bir süredir destekliyor. PDO sürücüsü de var. Ama yine de, MySQL sorguları hala varsayılan olarak PDO tarafından hazırlandı, son kontrol ettim.
cHao

9
$spoiled_data = "1; DROP TABLE users;"-> arasında ne fark vardır $query = "SELECT * FROM users where id=$spoiled_data";: $db->prepare("SELECT * FROM users where id=?");-> $data = "1; DROP TABLE users;"-> $db->execute($data);. Aynı şeyi yapmayacaklar mı?
Juha Untinen

14
@Juha Untinen Veriler herhangi bir şey olabilir. Verileri ayrıştırmaz. Bu DATA komutu değil. Bu nedenle $ verileri sql komutları içermesine rağmen yürütülmez. Ayrıca, id bir sayı ise, dize içeriği bir rapor veya sıfır değeri oluşturur.
Soley

21

İşte bir örnek oluşturmak için SQL:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

Inject sınıfı SQL enjeksiyonuna karşı savunmasızdır. Sorgu, kullanıcı girdisi ile birlikte dinamik olarak yapıştırılır. Sorgunun amacı Bob hakkında bilgi göstermekti. Kullanıcı girişlerine göre maaş veya ikramiye. Ancak kötü niyetli kullanıcı, gizli olması gereken Aaron hakkında bilgiler de dahil olmak üzere her şeyin döndürülmesi için, sorgunun bozulmasına neden olan girdiyi, burada bulunan cümleye eşdeğer bir 'veya true' değerine takarak değiştirir.

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Bunu çalıştırırken, ilk vaka normal kullanımda, ikincisi kötü amaçlı enjeksiyonda:

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

SQL deyimlerini kullanıcı girdisinin dize birleştirmesiyle oluşturmamalısınız. Yalnızca enjeksiyona karşı savunmasız olmakla kalmaz, aynı zamanda sunucu üzerinde de önbellekleme etkileri vardır (ifade değişir, bind örneği her zaman aynı ifadeyi çalıştırırken SQL deyimi önbellek isabetine sahip olma olasılığı daha düşüktür).

İşte bu tür bir enjeksiyondan kaçınmak için bir Bağlama örneği:

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Bunu bir önceki örnekle aynı girdi ile çalıştırmak, bu dizeyle eşleşen bir ödeme türü olmadığından kötü amaçlı kodun çalışmadığını gösterir:

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?

Veritabanına bağlanan programdan hazırlanmış bir deyimi kullanmak, db'nin bir parçası olan hazırlanmış bir deyimi kullanmakla aynı etkiye sahip mi? Örneğin, Postgres'in kendi hazırladığı bir ifadesi vardır ve bunu SQL enjeksiyonunu engelleyebilir mi? postgresql.org/docs/9.2/static/sql-prepare.html
Celeritas

@Celeritas Postgresql için kesin bir cevabım yok. Dokümanlara bakıldığında, etkinin aynı olduğu anlaşılıyor. parametreler için adlandırılmış ifadeyi çalıştırırken PREPARE, önceden ayrıştırılmış sabit bir deyim oluşturur (yani deyim girdiden bağımsız olarak değişmeyecektir) EXECUTE. Yana PREPAREyalnızca oturum süreye sahip değil psql'in script aracılığıyla enjeksiyonu önlemek için, performans nedenleriyle içindir gibi, gerçekten görünüyor. Psql erişimi için, saklı yordamlara izin verebilir ve procs içindeki parametreleri bağlayabilir.
Glenn

@Celeritas Yukarıda x86_64 üzerinde PostgreSQL 11.1 kullanarak kod denedim ve yukarıdaki SQLi örneği çalıştı.
Krishna Pandey

15

Temel olarak, hazırlanan ifadelerle potansiyel bir bilgisayar korsanından gelen veriler veri olarak kabul edilir - ve uygulamanızın SQL'i ile karıştırılabilmesi ve / veya SQL olarak yorumlanmasının bir yolu yoktur (iletilen veriler doğrudan uygulama SQL).

Bunun nedeni, hazırlanan ifadelerin verimli bir sorgu planı bulmak için önce SQL sorgusunu "hazırlaması" ve muhtemelen daha sonra bir formdan gelen gerçek değerleri göndermesidir - bu sırada sorgu gerçekte yürütülür.

Daha fazla bilgi burada:

Hazırlanan ifadeler ve SQL Ekleme


6

Cevapları okudum ve yine de Hazırlanan İfadelerin özünü aydınlatan kilit noktayı vurgulama gereğini hissettim. Kullanıcı girdisinin dahil olduğu veritabanını sorgulamanın iki yolunu düşünün:

Saf Yaklaşım

Bir kullanıcı bir SQL deyimi oluşturmak için kullanıcı girişini bazı kısmi SQL dizesiyle birleştirir. Bu durumda kullanıcı, daha sonra yürütmek üzere veritabanına gönderilecek kötü amaçlı SQL komutlarını gömebilir.

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

Örneğin, kötü niyetli kullanıcı girişi SQLStringşuna eşit olabilir:"SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

Kötü niyetli kullanıcı nedeniyle, ikincisinin SQLString( "DROP TABLE CUSTOMERS") zarar vereceği 2 ifade içerir .

Hazırlanan İfadeler

Bu durumda, sorgu ve verilerin ayrılması nedeniyle, kullanıcı girdisi hiçbir zaman SQL ifadesi olarak değerlendirilmez ve bu nedenle hiçbir zaman yürütülmez . Bu nedenle, enjekte edilen kötü amaçlı SQL kodlarının hiçbir zarara neden olmaması gerekir. Yani "DROP TABLE CUSTOMERS"yukarıdaki davada asla idam edilemez.

Özetle, hazırlanan ifadelerle, kullanıcı girişi yoluyla sokulan kötü amaçlı kod yürütülmeyecektir!


Gerçekten mi? Kabul edilen cevap tam olarak bunu söylemiyor mu?
Ortak Duygunuz

@ Yaygın Duygunuz Kabul edilen cevap çok sayıda değerli bilgi ile doludur, ancak veri ve sorgunun ayrılmasının uygulama detaylarının ne anlama geldiğini merak ettim. Oysa kötü niyetli olarak enjekte edilen verilerin (eğer varsa) hiçbir zaman yürütülmeyeceği noktasına odaklanmak başın tırnağına çarpıyor.
N.Vegeta

Ve cevabınızda bulunmayan hangi "uygulama detayları" verilmiştir?
Ortak

nereden geldiğimi görmeye çalışırsanız, amacımın şu olduğunu fark edersiniz: Uygulama ayrıntılarını görmek için kısa bir istek, kötü niyetli kullanıcı girişinin neden herhangi bir soruna neden olmayacağının açık nedenini anlama ihtiyacından kaynaklanmaktadır zarar. Uygulama ayrıntılarını görmeye çok fazla gerek yok. Bu nedenle, uygulama ayrıntılarının hiçbir şekilde kötü niyetli olarak SQL girilmeyecek şekilde iletiyi eve gönderecek şekilde olduğunu fark etmenin nedeni budur. Cevabınız şu soruyu cevaplıyor, nasıl (istendiği gibi)?, Ama diğer milletlerin (benim gibi) neden kısaca cevap vereceğini düşünürdüm?
N.Vegeta

Bunu, zımni bir eleştiri olarak değil, özü açıklayan bir zenginleştirme olarak düşünün (recenlty, kabul edilen yanıtın yazarının kim olduğunu fark etti).
N.Vegeta

5

Hazırlanan bir deyimi oluşturup DBMS'ye gönderdiğinizde, yürütme için SQL sorgusu olarak saklanır.

Daha sonra DBMS bu verileri yürütme için sorgu parametreleri (parametre belirleme) olarak kullanacak şekilde verilerinizi sorguya bağlarsınız. DBMS, önceden derlediğiniz SQL sorgusuna ek olarak bağladığınız verileri kullanmaz; sadece veri.

Bu, hazırlanan ifadeleri kullanarak SQL enjeksiyonunu gerçekleştirmenin temelde imkansız olduğu anlamına gelir. Hazırlanan ifadelerin doğası ve DBMS ile ilişkileri bunu engeller.


4

Gelen SQL Server giriş parametreleri sorgu oluşturmuyor çünkü hazırlanmış deyimi kullanarak enjeksiyon geçirmez kesinlikle. Bu, yürütülen sorgunun dinamik bir sorgu olmadığı anlamına gelir. SQL enjeksiyonuna karşı savunmasız bir ifade örneği.

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

Şimdi, inoutusername değişkenindeki değer 'veya 1 = 1 gibi bir şeyse, bu sorgu artık şöyle olur:

select * from table where username='a' or 1=1 -- and password=asda

Ve geri kalanlar daha sonra yorumlanır --, bu nedenle aşağıdaki gibi hazırlanan ifade örneğini kullanarak asla çalıştırılmaz ve atlanmaz.

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

Yani aslında başka bir parametre gönderemezsiniz, böylece SQL enjeksiyonundan kaçınırsınız ...


3

Anahtar kelime need not be correctly escaped. Bu, tireler, kesme işaretleri, alıntılar vb. Atmaya çalışan insanlar için endişelenmediğiniz anlamına gelir ...

Her şey sizin için halledilir.


2
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

Diyelim ki bir Servlet'te haklısın. Kötü niyetli bir kişi 'filtre' için kötü bir değer geçerse veritabanınızı hackleyebilirsiniz.


0

Temel Neden # 1 - Sınırlayıcı Sorunu

Sql enjeksiyonu mümkündür çünkü dizeleri sınırlamak ve dizelerin parçaları olmak için tırnak işaretleri kullanırız, bu da bazen yorumlamayı imkansız hale getirir. Dize verilerinde kullanılamayan sınırlayıcılarımız olsaydı, sql enjeksiyonu asla olmazdı. Sınırlayıcı probleminin çözülmesi, sql enjeksiyon problemini ortadan kaldırır. Yapı sorguları bunu yapar.

Kök Neden # 2 - İnsan Doğası, İnsanlar Kurnaz ve Bazı Kurnaz İnsanlar Kötü Amaçlı ve Tüm İnsanlar Hata Yapıyor

Sql enjeksiyonunun diğer temel nedeni insan doğasıdır. Programcılar da dahil olmak üzere insanlar hata yapar. Yapısal bir sorguda hata yaptığınızda, sisteminiz sql enjeksiyonuna karşı savunmasız kalmaz. Yapısal sorgular kullanmıyorsanız hatalar sql enjeksiyon güvenlik açığı oluşturabilir.

Yapısal Sorgular SQL Enjeksiyonunun Kök Nedenlerini Nasıl Çözer

Yapısal Sorgular Bir deyime sql komutları koyarak ve verileri ayrı bir programlama deyimine koyarak Sınırlayıcı Sorununu Çöz. Programlama ifadeleri gereken ayrılığı oluşturur.

Yapılandırılmış sorgular, insan hatasının kritik güvenlik açıkları oluşturmasını önlemeye yardımcı olur. İnsanların hata yapmasıyla ilgili olarak, yapı sorguları kullanıldığında sql enjeksiyonu yapılamaz. Yapılandırılmış sorgular içermeyen sql enjeksiyonunu önlemenin yolları vardır, ancak bu yaklaşımlardaki normal insan hatası genellikle en azından sql enjeksiyonuna maruz kalmaya yol açar. Yapılandırılmış Sorgular, sql enjeksiyonundan güvenli değildir. Dünyadaki tüm hataları, neredeyse, diğer tüm programlamalarla aynı şekilde yapılandırılmış sorgular ile yapabilirsiniz, ancak yapabileceğiniz hiçbir şey sql enjeksiyonu tarafından ele geçirilen bir sisteme dönüştürülemez. Bu yüzden insanlar bunun sql enjeksiyonunu önlemenin doğru yolu olduğunu söylemek ister.

Böylece, orada, sql enjeksiyonunun nedenleri ve kullanıldıklarında onları imkansız kılan doğa yapılandırılmış sorgular var.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.