“Bobby Tables” XKCD çizgi romanından SQL enjeksiyonu nasıl çalışır?


1093

Sadece bakıyor:

XKCD Şeridi (Kaynak: https://xkcd.com/327/ )

Bu SQL ne yapar:

Robert'); DROP TABLE STUDENTS; --

İkisini de biliyorum 've --yorumlar için, ama DROPaynı satırın bir parçası olduğu için kelime de yorum almıyor musunuz?


16
Stack Overflow Podcast # 31'i (27 Kasım 2008) dinlerseniz , aslında bunu tartışırlar.
EBGreen

93
MySQL'de, yorumlar' için değildir . Olsa bile, önünde bir boşluk yoktur, böylece yalnızca ondan önce gelen dizeyi sonlandırabilir.
Yörüngedeki Hafiflik Yarışları

45
XKCD ile ilgili olarak, bazı çizgi romanlar hakkında herhangi bir soru varsa, her zaman XKCD'yi Açıkla'ya gidebilir ve cevabınızı çözebilirsiniz . XKCD geohashing
Anatoli

13
Bu bağlantının buraya kaydedilmesi gerektiğine inanıyorum: bobby-tables.com
Arioch 'The

Yanıtlar:


1116

Öğrenci masasını düşürür.

Okulun programındaki orijinal kod muhtemelen

q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";

Bu, sorguya metin girişi eklemenin naif bir yoludur ve göreceğiniz gibi çok kötüdür .

İlk ad, orta ad metin kutusu FNMName.Text (yani Robert'); DROP TABLE STUDENTS; --) ve soyadı metin kutusu LName.Text (diyelim ki çağıralım Derper) sorgulamanın geri kalanıyla birleştirildikten sonra, sonuç aslında iki sorgu ile birbirinden ayrılır deyim sonlandırıcısı (noktalı virgül). İkinci sorgu birinciye enjekte edildi. Kod bu sorguyu veritabanına karşı yürüttüğünde, şöyle görünür

INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')

basit İngilizce'de kabaca iki sorguyu çevirir:

Öğrenciler tablosuna Ad değeri 'Robert' olan yeni bir kayıt ekleyin

ve

Öğrenciler tablosunu silme

İkinci sorguyu geçen her şey bir yorum olarak işaretlenir : --', 'Derper')

'Öğrencinin adına bu kapanış var, bir yorum değil dize sınırlayıcısı . Öğrencinin adı bir dize olduğundan varsayımsal sorguyu tamamlamak için sözdizimsel olarak gereklidir. Enjeksiyon saldırıları, yalnızca enjekte ettikleri SQL sorgusu geçerli SQL ile sonuçlandığında çalışır .

Dan04 kullanıcısının zeki yorumuna göre tekrar düzenlendi


3
Mmm, argümanların etrafında parantez olan NEREDE oldukça sıradışı, ama en azından bir sözdizimi hatasını önlüyor ... :-)
PhiLho

60
@PhiLho: Orijinal ifade bir INSERTolsaydı, parantez daha anlamlı olurdu. Ayrıca veritabanı bağlantısının neden salt okunur modda olmadığını açıklar.
dan04

3
@ Dan04'un açıkladığı gibi, parantez bir INSERT. Geriye doğru düşünmek SELECT, tablodaki Küçük Bobby Tabloları Ekle'nin zaten tabloyu düşüreceği için zaten çalışmazdı.
ypercubeᵀᴹ

10
Aslında, bu örnekte ilk sorgu ("yeni kayıt ekle ...") başarısız olur çünkü Studentstek bir sütundan daha fazlasını bekler (orijinal / doğru ifade iki sütun sağladı). Bununla birlikte, ikinci sütunun varlığı, yorumlamanın neden gerekli olduğunu göstermek için yararlıdır; ve kişi Bobby'nin ismini değiştiremediğinden, muhtemelen bu gözlemden bir dipnot olarak çok daha azıyla ayrılmak en iyisidir.
eggyal

7
Bobby'nin soyadı - veya en azından annesinin adı , Explain XKCD'ye göre Roberts . Bununla birlikte, düzeltmenin cevap netliğini artıracağından emin değilim.
WBT

611

Diyelim ki ad bir değişkende kullanılmış $Name.

Daha sonra bu sorguyu çalıştırın :

INSERT INTO Students VALUES ( '$Name' )

Kod, kullanıcının değişken olarak sağladığı her şeyi yanlışlıkla yerleştiriyor.

SQL'in olmasını istediniz :

Öğrencilerin DEĞERLERİNE EKLE (' Robert Tables')

Ancak akıllı bir kullanıcı istediği her şeyi sağlayabilir:

Öğrenci Değerlerine Ekleme (' Robert'); DROP TABLE Students; --')

Ne olsun:

INSERT INTO Students VALUES ( 'Robert' );  DROP TABLE STUDENTS; --' )

--Sadece çizgi kalanını yorumlar.


87
Bu, en yüksek oy alandan çok daha iyi, çünkü kapanış parantezini açıklıyor.
Tim Büthe

1
Bu arada, çizgi romandaki okul müdürünün veya XSS'nin öğrenci tablosu silindiği için farkında olmasının bir yolu yoktur, bunu kimin yaptığını bilemez.
xryl669

@ xryl669 Günlükler bu gibi durumlarda çok yardımcı olur ... Bazen tüm sorgular günlüğe kaydedilir ve bazen diğer günlüğe kaydedilen bilgiler suçluyu çıkarmanıza yardımcı olabilir.
inemanja

165

Herkesin daha önce işaret ettiği gibi ');, orijinal ifadeyi kapatır ve ardından ikinci bir açıklama gelir. PHP gibi diller de dahil olmak üzere çoğu çerçeve, varsayılan olarak tek bir SQL dizesinde birden fazla ifadeye izin vermeyen güvenlik ayarlarına sahiptir. Örneğin PHP'de, mysqli_multi_queryişlevi kullanarak yalnızca bir SQL dizesinde birden çok deyim çalıştırabilirsiniz .

Ancak, varolan bir SQL deyimini ikinci bir deyim eklemek zorunda kalmadan SQL enjeksiyonu yoluyla değiştirebilirsiniz. Diyelim ki bu basit seçimle bir kullanıcı adını ve şifreyi kontrol eden bir giriş sisteminiz var:

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

peterKullanıcı adı ve secretparola olarak girerseniz , sonuçta ortaya çıkan SQL dizesi şöyle görünür:

SELECT * FROM users WHERE username='peter' and (password='secret')

Herşey yolunda. Şimdi bu dizeyi şifre olarak sağladığınızı düşünün:

' OR '1'='1

Sonra ortaya çıkan SQL dizesi şöyle olurdu:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

Bu, şifreyi bilmeden herhangi bir hesaba giriş yapmanızı sağlar. Bu nedenle, SQL enjeksiyonunu kullanmak için iki ifade kullanmanıza gerek yoktur, ancak birden fazla ifade sağlayabiliyorsanız daha yıkıcı şeyler yapabilirsiniz.


71

Hayır, 'SQL'de bir yorum değil, bir sınırlayıcıdır.

Anne veritabanı programcısı gibi bir istekte bulundu:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');

(örneğin), $xxxdeğişken içeriğinin biçimi kontrol etmeden veya özel karakterlerden kaçmadan doğrudan bir HTML formundan çıkarıldığı yeni öğrenciyi eklemek için .

Yani veritabanı programı $firstNameiçeriyorsa Robert'); DROP TABLE students; --doğrudan DB üzerinde aşağıdaki isteği yürütür:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');

yani. insert deyimini erken sonlandıracak, krakerin istediği her türlü kötü amaçlı kodu yürütebilecek, daha sonra kodun kalan kısmını yorumlayabilecek.

Mmm, çok yavaşım, turuncu bantta benden önce 8 cevap görüyorum ... :-) Popüler bir konu, öyle görünüyor.


39

TL; DR

- Uygulama girişimi kabul eder, bu durumda 'Nancy', denemeden
- özel karakterlerden kaçmak gibi girdiyi sterilize edin- özel karakterlerden kaçmak gibi girdiyi sterilize edin
okul => Öğrencilerin DEĞERLERİNE EKLE ('Nancy');=> INSERT INTO öğrencilerin DEĞERLER ( 'Nancy' );   
INSERT 0 1INSERT 0 1  

- SQL enjeksiyonu, bir veritabanı komutuna giriş,- SQL enjeksiyonu, bir veritabanı komutuna giriş,
- veritabanı sunucusunun rastgele SQL çalıştırmasına neden olur- veritabanı sunucusunun rastgele SQL çalıştırmasına neden olur
okul => Öğrencilerin DEĞERLERİNE EKLE ('Robert'); DROP TABLO öğrencileri; - ');=> INSERT INTO öğrencilerin DEĞERLER ( 'Robert' ); DROP TABLO öğrencileri ; - ');      
INSERT 0 1INSERT 0 1  
DÜŞME TABLOSUDÜŞME TABLOSU 

- Öğrenci kayıtları artık bitti - daha da kötü olabilirdi!- Öğrenci kayıtları artık bitti - daha da kötü olabilirdi!
okul => SEÇ * öğrencilerden;=> SEÇ * DAN öğrenciler ;   
HATA: "öğrenciler" ilişkisi mevcut değil:   İlişkisi "öğrenciler" does not var
LINE 1: Öğrencilerden SELECT *;1 : SEÇİN * GELEN öğrenciler ;   
                      ^^

Bu, öğrenci masasını düşürür (siler).

( Bu yanıttaki tüm kod örnekleri bir PostgreSQL 9.1.2 veritabanı sunucusunda çalıştırılmıştır. )

Neler olduğunu netleştirmek için, bunu yalnızca ad alanını içeren basit bir tabloyla deneyelim ve tek bir satır ekleyelim:

okul => TABLO öğrencileri OLUŞTUR (adı METİN PRİMER ANAHTARI);=> TABLO öğrencileri OLUŞTUR ( adı METİN PRİMER ANAHTARI );   
DİKKAT: CREATE TABLE / PRIMARY KEY, "öğrenciler" tablosu için örtük dizin "students_pkey" oluşturur: CREATE TABLE / İLKÖĞRETİM KEY olacak oluşturmak örtülü endeksi "students_pkey" için tablo "öğrenci"          
TABLO OLUŞTURTABLO OLUŞTUR 
okul => Öğrencilerin DEĞERLERİNE EKLE ('John');=> INSERT INTO öğrencilerin DEĞERLER ( John ' );   
INSERT 0 1INSERT 0 1  

Uygulamanın tabloya veri eklemek için aşağıdaki SQL'i kullandığını varsayalım:

INSERT INTO öğrencilerin DEĞERLER ( 'filanca' );  

foobarÖğrencinin gerçek adıyla değiştirin . Normal bir takma işlemi şöyle görünecektir:

- Giriş: Nancy 
okul => INSERT INTO öğrencilerin DEĞERLER ( 'Nancy' ); INSERT 0 1   
  

Tabloyu sorguladığımızda şunu elde ederiz:

Okul => SEÇ * DAN öğrenciler ;   
 isim
-------
 John
 Nancy
( 2 sıra ) 

Küçük Bobby Tables'ın adını masaya eklediğimizde ne olur?

- Girdi: Robert '); DROP TABLO öğrencileri; - 
Okul => INSERT INTO öğrencilerin DEĞERLER ( 'Robert' ); DROP TABLO öğrencileri ; - '); INSERT 0 1 DÜŞME TABLOSU      
  
 

Buradaki SQL enjeksiyonu, ifadeyi sonlandıran ve ayrı bir DROP TABLEkomut içeren öğrencinin adının sonucudur ; girişin sonundaki iki tire, aksi takdirde hataya neden olan kalan kodları yorumlamak üzere tasarlanmıştır. Çıktının son satırı, veritabanı sunucusunun tabloyu bıraktığını doğrular.

INSERTİşlem sırasında uygulamanın herhangi bir özel karakter için girişi denetlemediğini ve bu nedenle SQL komutuna rastgele giriş girilmesine izin verdiğini fark etmek önemlidir. Bu, kötü niyetli bir kullanıcının, normalde kullanıcı girişi için amaçlanan bir alana, veritabanı sisteminin çalıştırmasını sağlamak için rastgele SQL koduyla birlikte tırnak işaretleri gibi özel simgeler, dolayısıyla SQL  enjeksiyonu ekleyebileceği anlamına gelir .

Sonuç?

Okul => SEÇ * DAN öğrenciler ; 
HATA :   ilişkisi "öğrenciler" yok değil var   
SATIR 1 : SEÇİN * GELEN öğrenciler ; ^   
                      

SQL yerleştirme , bir işletim sistemindeki veya uygulamadaki uzaktan rasgele kod yürütme güvenlik açığına eşdeğer veritabanıdır . Başarılı bir SQL enjeksiyon saldırısının potansiyel etkisi göz ardı edilemez - veritabanı sistemine ve uygulama yapılandırmasına bağlı olarak, bir saldırgan tarafından veri kaybına (bu durumda olduğu gibi) neden olmak, verilere yetkisiz erişim sağlamak ve hatta yürütmek için kullanılabilir ana makinenin kendisinde rastgele kod.

XKCD çizgi romanının belirttiği gibi, SQL enjeksiyon saldırılarına karşı korunmanın bir yolu, altta yatan SQL komutunu değiştirememeleri ve dolayısıyla keyfi SQL kodunun yürütülmesine neden olmaması için özel karakterlerden kaçmak gibi veritabanı girişlerini sterilize etmektir. SqlParameterADO.NET'te kullanmak gibi parametreli sorgular kullanırsanız , giriş en azından SQL enjeksiyonuna karşı koruma sağlamak için otomatik olarak sterilize edilir.

Ancak, girdileri uygulama düzeyinde dezenfekte etmek daha gelişmiş SQL enjeksiyon tekniklerini durduramayabilir. Örneğin , mysql_real_escape_stringPHP işlevini atlatmanın yolları vardır . Ek koruma için, birçok veritabanı sistemi hazırlanmış ifadeleri destekler . Arka uçta düzgün bir şekilde uygulanırsa, hazırlanan ifadeler veri girişlerini komutun geri kalanından anlamsal olarak ayrı tutarak SQL enjeksiyonunu imkansız hale getirebilir.


30

Diyelim ki böyle bir öğrenci oluşturma yöntemi yazdınız:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

Ve birisi isme giriyor Robert'); DROP TABLE STUDENTS; --

Veritabanında ne çalıştırılır bu sorgu:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

Noktalı virgül, insert komutunu sonlandırır ve bir başkasını başlatır; - satırın geri kalanını yorumlar. DROP TABLE komutu yürütülür ...

Bu yüzden bağlama parametreleri iyi bir şeydir.


26

Tek tırnak, bir dizenin başlangıcı ve bitmesidir. Noktalı virgül bir ifadenin sonudur. Eğer böyle bir seçim yapıyorlarsa:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

SQL şöyle olur:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

Bazı sistemlerde, selectilk önce bu dropifadeyi takip eder ! İleti şudur: SQL'İNİZE EMBED DEĞERLERİ DONT. Bunun yerine parametreleri kullanın!


18

');Uçları sorgu, bu bir yorum başlamaz. Daha sonra öğrenci tablosunu bırakır ve yürütülmesi gereken sorgunun geri kalanını yorumlar.


17

Veritabanının yazarı muhtemelen

sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);

Öğrenci_adı verilen ise, "Robert" adıyla seçim yapar ve sonra tabloyu bırakır. "-" bölümü, verilen sorgunun geri kalanını bir yoruma dönüştürür.


Bu benim ilk düşüncemdi, ama sondaki kapanış paranteziyle bir sözdizimi hatası alıyorsunuz, değil mi?
PhiLho

3
Bu yüzden sonunda - kalan metnin bir yorum olduğunu ve yok sayılması gerektiğini belirten bir - vardır.

17

Bu durumda, 'bir yorum karakteri değildir. Dize değişmezlerini sınırlamak için kullanılır. Çizgi roman sanatçısı, söz konusu okulun şuna benzer bir yerde dinamik bir sql'ye sahip olduğu fikrini kullanıyor:

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

Şimdi 'karakter, programcı beklemeden önce dizgi değişmezini sonlandırır. İle birlikte; bir saldırgan artık istedikleri sql'i ekleyebilir. Sondaki - açıklaması, orijinal deyimde kalan sql değerinin sorgunun sunucuda derlenmesini engellemediğinden emin olmaktır.

FWIW, ayrıca söz konusu çizgi romanın önemli bir detayın yanlış olduğunu düşünüyorum: Çizgi romanın önerdiği gibi, veritabanı girişlerinizi sterilize etmeyi düşünüyorsanız , hala yanlış yapıyorsunuz. Bunun yerine, veritabanı girişlerinizi karantinaya almayı düşünmelisiniz ve bunu yapmanın doğru yolu parametreli sorgulardır.


16

SQL'deki 'karakter dize sabitleri için kullanılır. Bu durumda, açıklama için değil, dize sabitini sonlandırmak için kullanılır.


7

Şöyle çalışır: Yönetici öğrencinin kayıtlarını aradığını varsayalım

Robert'); DROP TABLE STUDENTS; --

Yönetici hesabının yüksek ayrıcalıklara sahip olması nedeniyle, tabloyu bu hesaptan silmek mümkündür.

İstekten kullanıcı adını alma kodu:

Şimdi sorgu böyle bir şey olurdu (öğrenci tablosunda arama yapmak için)

String query="Select * from student where username='"+student_name+"'";

statement.executeQuery(query); //Rest of the code follows

Ortaya çıkan sorgu

Select * from student where username='Robert'); DROP TABLE STUDENTS; --

Kullanıcı girişi dezenfekte edilmediğinden, yukarıdaki sorgu 2 parçaya dönüştürülmüştür

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

Çift tire (-) yalnızca sorgunun kalan kısmını yorumlayacaktır.

Varsa parola kimlik doğrulamasını geçersiz kılabileceğinden bu tehlikelidir

İlki normal aramayı yapar.

İkincisi, hesabın yeterli ayrıcalıklara sahip olması durumunda masa öğrencisini bırakacaktır (Genellikle okul yöneticisi hesabı bu sorguyu çalıştıracak ve ayrıcalıkların yukarıda konuşulmasını sağlayacaktır).


SELECT* FROM sutdents ...- "s" yi unuttun. Bıraktığın şey bu. DROP TABLE STUDENTS;
DevWL

4

SQL enjeksiyonu yapmak için form verilerini girmenize gerek yoktur.

Daha önce kimse bunu işaret etmedi, bu yüzden bazılarını uyarabilirim.

Çoğunlukla form girişini düzeltmeye çalışacağız. Ancak bu, SQL enjeksiyonu ile saldırı alabileceğiniz tek yer değil. GET isteği ile veri gönderen URL ile çok basit bir saldırı yapabilirsiniz; Aşağıdaki örneği düşünün:

<a href="/show?id=1">show something</a>

URL'niz http://yoursite.com/show?id=1 şeklinde görünecektir

Şimdi birisi böyle bir şey deneyebilir

http://yoursite.com/show?id=1;TRUNCATE table_name

Table_name öğesini gerçek tablo adıyla değiştirmeyi deneyin. Eğer masa isminizi doğru alırsa masanızı boşaltırlar! (Bu URL'yi basit bir komut dosyası ile zorlamak çok kolaydır)

Sorgunuz şöyle görünecektir ...

"SELECT * FROM page WHERE id = 4;TRUNCATE page"

PDO kullanan PHP güvenlik açığı kodu örneği:

<?php
...
$id = $_GET['id'];

$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch(); 
/************* You have lost your data!!! :( *************/
...

Çözüm - PDO prepar () & bindParam () yöntemlerini kullanın:

<?php
...
$id = $_GET['id'];

$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...
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.