Bagaimana cara kerja injeksi SQL dari komik “Bobby Tables” XKCD?

1094

Hanya melihat:

XKCD Strip (Sumber: https://xkcd.com/327/ )

Apa yang dilakukan SQL ini:

Robert'); DROP TABLE STUDENTS; --

Saya tahu keduanya 'dan --untuk komentar, tetapi bukankah kata itu DROPdikomentari juga karena itu adalah bagian dari baris yang sama?

Blankman
sumber
16
Jika Anda mendengarkan Stack Overflow Podcast # 31 (27 November 2008), mereka sebenarnya mendiskusikan hal ini.
EBGreen
93
Di MySQL, 'bukan untuk komentar . Bahkan jika itu, tidak ada ruang sebelumnya sehingga hanya dapat mengakhiri string yang mendahuluinya.
Lightness Races in Orbit
45
Sejauh XKCD berjalan, jika ada pertanyaan tentang beberapa komik Anda selalu dapat pergi ke Menjelaskan XKCD dan jawaban Anda sudah tahu. Bahkan ada wiki XKCD , yang sangat membantu untuk beberapa komik rumit seperti XKCD geohashing
Anatoli
13
Saya percaya tautan ini harus direkam di sini: bobby-tables.com
'The
2
beta.companieshouse.gov.uk/company/10542519 adalah pendaftaran untuk konsultan yang bernama; DROP TABLE "COMPANIES"; - LTD
Alex Dupuy

Jawaban:

1117

Itu menjatuhkan meja siswa.

Kode asli dalam program sekolah mungkin terlihat seperti

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

Ini adalah cara naif untuk menambahkan input teks ke dalam kueri, dan sangat buruk , seperti yang akan Anda lihat.

Setelah nilai-nilai dari nama pertama, nama tengah textbox FNMName.Text (yang Robert'); DROP TABLE STUDENTS; --) dan nama terakhir textbox LName.Text (sebut saja Derper) yang bersambung dengan sisa query, hasilnya adalah sekarang benar-benar dua pertanyaan dipisahkan oleh terminator pernyataan (titik koma). Permintaan kedua telah disuntikkan ke yang pertama. Ketika kode mengeksekusi query ini terhadap database, itu akan terlihat seperti ini

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

yang, dalam bahasa Inggris yang sederhana, secara kasar diterjemahkan ke dua pertanyaan:

Tambahkan catatan baru ke tabel Siswa dengan nilai Nama 'Robert'

dan

Hapus tabel Siswa

Segala sesuatu yang melewati kueri kedua ditandai sebagai komentar : --', 'Derper')

Dalam 'nama siswa bukan komentar, itu pembatas string penutup . Karena nama siswa adalah string, maka diperlukan sintaksis untuk menyelesaikan kueri hipotetis. Serangan injeksi hanya berfungsi ketika kueri SQL yang mereka berikan menghasilkan SQL yang valid .

Diedit lagi sesuai komentar cerdik dan04

Akan
sumber
3
Mmm, DI MANA dengan tanda kurung di sekitar argumen agak tidak biasa, tapi setidaknya itu menghindari kesalahan sintaks ... :-)
PhiLho
60
@ Philho: Jika pernyataan asli adalah INSERT, maka tanda kurung akan lebih masuk akal. Ini juga akan menjelaskan mengapa koneksi database tidak dalam mode read-only.
dan04
3
Seperti @ dan04 menjelaskan, tanda kurung lebih masuk akal dengan INSERT. Berpikir mundur, SELECTtoh itu tidak akan berjalan karena Insert of the Little Bobby Tables di tabel sudah akan menjatuhkan meja.
ypercubeᵀᴹ
10
Sebenarnya, dalam contoh ini kueri pertama ("tambahkan catatan baru ...") akan gagal karena Studentsmengharapkan lebih dari hanya satu kolom (pernyataan asli / benar menyediakan dua kolom). Yang mengatakan, kehadiran kolom kedua bermanfaat untuk menunjukkan mengapa komentar diperlukan; dan karena seseorang tidak dapat mengubah nama Bobby, mungkin lebih baik meninggalkan apa adanya dengan sedikit pengamatan daripada ini sebagai catatan kaki.
eggyal
7
Nama belakang Bobby - atau setidaknya nama ibunya, adalah Roberts , per Explain XKCD . Saya tidak yakin bahwa mengoreksi itu akan meningkatkan kejelasan jawaban.
WBT
611

Katakanlah nama itu digunakan dalam variabel, $Name ,.

Anda kemudian menjalankan kueri ini :

INSERT INTO Students VALUES ( '$Name' )

Kode keliru menempatkan apa pun yang diberikan pengguna sebagai variabel.

Anda ingin menjadi SQL :

Masukkan nilai siswa (' Robert Tables`)

Tetapi pengguna yang pintar dapat menyediakan apa pun yang mereka inginkan:

Masukkan ke nilai siswa (' Robert'); DROP TABLE Students; --')

Yang Anda dapatkan adalah:

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

Satu- --satunya komentar sisa baris.

sinoth
sumber
87
Ini jauh lebih baik daripada yang tertinggi, karena menjelaskan kurung penutup.
Tim Büthe
1
Ngomong-ngomong, tidak ada cara bagi direktur sekolah dalam komik untuk menyadari atau XSS karena tabel siswa dihapus, dia tidak bisa tahu siapa yang melakukan ini.
xryl669
@ xryl669 Log sangat membantu dalam situasi seperti ini ... Terkadang semua kueri dicatat, dan terkadang info login lainnya dapat membantu Anda menyimpulkan penyebabnya.
inemanja
165

Seperti orang lain sudah tunjukkan, ');penutupan pernyataan asli dan kemudian pernyataan kedua mengikuti. Sebagian besar kerangka kerja, termasuk bahasa seperti PHP, sekarang memiliki pengaturan keamanan default yang tidak memungkinkan banyak pernyataan dalam satu string SQL. Dalam PHP, misalnya, Anda hanya dapat menjalankan beberapa pernyataan dalam satu string SQL dengan menggunakan mysqli_multi_queryfungsi.

Anda dapat, bagaimanapun, memanipulasi pernyataan SQL yang ada melalui injeksi SQL tanpa harus menambahkan pernyataan kedua. Katakanlah Anda memiliki sistem masuk yang memeriksa nama pengguna dan kata sandi dengan pilihan sederhana ini:

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

Jika Anda memberikan petersebagai nama pengguna dan secretkata sandi, string SQL yang dihasilkan akan terlihat seperti ini:

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

Semuanya baik. Sekarang bayangkan Anda memberikan string ini sebagai kata sandi:

' OR '1'='1

Maka string SQL yang dihasilkan adalah:

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

Itu akan memungkinkan Anda untuk masuk ke akun apa pun tanpa mengetahui kata sandi. Jadi Anda tidak perlu dapat menggunakan dua pernyataan untuk menggunakan injeksi SQL, meskipun Anda dapat melakukan lebih banyak hal yang merusak jika Anda mampu menyediakan beberapa pernyataan.

Johannes Fahrenkrug
sumber
71

Tidak, ' bukan komentar di SQL, tetapi pembatas.

Ibu seharusnya programmer database membuat permintaan yang tampak seperti:

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

(misalnya) untuk menambah siswa baru, di mana $xxx konten variabel diambil langsung dari formulir HTML, tanpa memeriksa format atau keluar dari karakter khusus.

Jadi jika $firstNameberisi Robert'); DROP TABLE students; --program database akan mengeksekusi permintaan berikut secara langsung pada DB:

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

yaitu. itu akan mengakhiri awal pernyataan penyisipan, mengeksekusi kode jahat apa pun yang diinginkan cracker, kemudian berkomentar apa pun sisa kode mungkin ada.

Mmm, saya terlalu lambat, saya sudah melihat 8 jawaban sebelum saya di band oranye ... :-) Topik yang populer, sepertinya.

PhiLho
sumber
39

TL; DR

- Aplikasi menerima input, dalam hal ini 'Nancy', tanpa berusaha - membersihkan input, seperti dengan melarikan diri karakter khusus 
sekolah => MASUKKAN KE DALAM NILAI siswa ( 'Nancy' ); INSERT 0 1
   
  

- Injeksi SQL terjadi ketika input ke dalam perintah basis data dimanipulasi untuk - menyebabkan server basis data mengeksekusi SQL 
school => INSERT INTO students VALUES ( 'Robert' ); Siswa DROP TABLE ; - '); INSERT 0 1 TABEL DROP
      
  
 

- Catatan siswa sekarang hilang - itu bisa lebih buruk! 
school => SELECT * FROM siswa ; 
ERROR :   hubungan "mahasiswa" tidak tidak ada   
GARIS 1 : PILIH * DARI siswa ; ^   
                      

Ini menjatuhkan (menghapus) tabel siswa.

( Semua contoh kode dalam jawaban ini dijalankan pada server database PostgreSQL 9.1.2. )

Untuk memperjelas apa yang terjadi, mari coba ini dengan tabel sederhana yang hanya berisi bidang nama dan tambahkan satu baris:

school => CREATE TABLE siswa ( nama TEXT PRIMARY KEY ); 
PEMBERITAHUAN : CREATE TABLE / PRIMARY KEY akan membuat implisit          indeks "students_pkey" untuk tabel "mahasiswa" MENCIPTAKAN TABLE 
sekolah => INSERT INTO siswa NILAI ( 'John' ); INSERT 0 1    
    
  

Mari kita asumsikan aplikasi menggunakan SQL berikut untuk memasukkan data ke dalam tabel:

INSERT INTO siswa NILAI ( 'foobar' );  

Ganti foobardengan nama siswa yang sebenarnya. Operasi memasukkan normal akan terlihat seperti ini:

- Input: Nancy 
sekolah => INSERT INTO siswa NILAI ( 'Nancy' ); INSERT 0 1   
  

Saat kami menanyakan tabel, kami mendapatkan ini:

school => SELECT * FROM siswa ;   
 nama
-------
 John
 Nancy
( 2 baris ) 

Apa yang terjadi ketika kita memasukkan nama Little Bobby Tables ke dalam tabel?

- Input: Robert '); Siswa DROP TABLE; - 
sekolah => MASUKKAN KE NILAI siswa    ( 'Robert' ); Siswa DROP TABLE ; - '); INSERT 0 1 MEJA DROP   
  
 

Injeksi SQL di sini adalah hasil dari nama siswa yang mengakhiri pernyataan dan termasuk DROP TABLEperintah terpisah ; dua tanda hubung pada akhir input dimaksudkan untuk mengomentari kode sisa yang jika tidak akan menyebabkan kesalahan. Baris terakhir dari output mengonfirmasi bahwa server database telah menjatuhkan tabel.

Penting untuk diperhatikan bahwa selama INSERToperasi aplikasi tidak memeriksa input untuk setiap karakter khusus, dan oleh karena itu memungkinkan input sewenang-wenang untuk dimasukkan ke dalam perintah SQL. Ini berarti bahwa pengguna jahat dapat memasukkan, ke dalam bidang yang biasanya dimaksudkan untuk input pengguna, simbol-simbol khusus seperti tanda kutip bersama dengan kode SQL sewenang-wenang untuk menyebabkan sistem database untuk mengeksekusinya, maka dari itu injeksi SQL  .

Hasil?

school => SELECT * FROM siswa ; 
ERROR :   hubungan "mahasiswa" tidak tidak    ada
GARIS 1 : PILIH * DARI siswa ; ^   
                      

Injeksi SQL adalah database yang setara dengan kerentanan eksekusi kode arbitrer jarak jauh dalam sistem operasi atau aplikasi. Dampak potensial dari serangan injeksi SQL yang sukses tidak dapat diremehkan - tergantung pada sistem database dan konfigurasi aplikasi, ini dapat digunakan oleh penyerang untuk menyebabkan kehilangan data (seperti dalam kasus ini), mendapatkan akses tidak sah ke data, atau bahkan mengeksekusi kode arbitrer pada mesin host itu sendiri.

Seperti dicatat oleh komik XKCD, salah satu cara untuk melindungi terhadap serangan injeksi SQL adalah membersihkan sanitas basis data, seperti dengan melarikan diri dari karakter khusus, sehingga mereka tidak dapat memodifikasi perintah SQL yang mendasarinya dan karena itu tidak dapat menyebabkan eksekusi kode SQL sewenang-wenang. Jika Anda menggunakan query parameter, seperti dengan menggunakan SqlParameterdalam ADO.NET, input akan, setidaknya, secara otomatis dibersihkan untuk menjaga terhadap injeksi SQL.

Namun, membersihkan input pada level aplikasi mungkin tidak menghentikan teknik injeksi SQL yang lebih canggih. Misalnya, ada cara untuk menghindari mysql_real_escape_stringfungsi PHP . Untuk perlindungan tambahan, banyak sistem database mendukung pernyataan yang disiapkan . Jika diimplementasikan dengan benar di backend, pernyataan yang disiapkan dapat membuat injeksi SQL tidak mungkin dengan memperlakukan input data secara semantik terpisah dari sisa perintah.

bwDraco
sumber
30

Katakanlah Anda secara naif menulis metode pembuatan siswa seperti ini:

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

Dan seseorang memasukkan namanya Robert'); DROP TABLE STUDENTS; --

Apa yang dijalankan pada database adalah permintaan ini:

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

Titik koma mengakhiri perintah sisipkan dan memulai yang lain; - komentar keluar dari sisa baris. Perintah DROP TABLE dieksekusi ...

Inilah sebabnya mengapa parameter bind adalah hal yang baik.

Dan Vinton
sumber
26

Kutipan tunggal adalah awal dan akhir dari sebuah string. Tanda titik koma adalah akhir dari sebuah pernyataan. Jadi jika mereka melakukan seleksi seperti ini:

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

SQL akan menjadi:

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

Pada beberapa sistem, selectakan dijalankan pertama diikuti oleh droppernyataan! Pesannya adalah: DONT EMBED VALUES INTO YOUR SQL. Alih-alih menggunakan parameter!

CodeAndCats
sumber
18

Yang ');mengakhiri kueri, itu tidak memulai komentar. Kemudian ia menjatuhkan tabel siswa dan mengomentari sisa kueri yang seharusnya dieksekusi.

Jorn
sumber
17

Penulis database mungkin melakukan

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

Jika student_name adalah yang diberikan, itu melakukan seleksi dengan nama "Robert" dan kemudian menjatuhkan tabel. Bagian "-" mengubah sisa kueri yang diberikan menjadi komentar.

Paul Tomblin
sumber
Itu adalah pikiran pertama saya, tetapi Anda mendapatkan kesalahan sintaksis dengan tanda kurung penutup tambahan, bukan?
PhiLho
3
Itu sebabnya ada - pada akhirnya, menunjukkan teks yang tersisa adalah komentar dan harus diabaikan.
17

Dalam hal ini, 'bukan karakter komentar. Ini digunakan untuk membatasi literal string. Artis komik mengandalkan gagasan bahwa sekolah tersebut memiliki sql dinamis di suatu tempat yang terlihat seperti ini:

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

Jadi sekarang karakter 'mengakhiri string literal sebelum programmer mengharapkannya. Dikombinasikan dengan; karakter untuk mengakhiri pernyataan, penyerang sekarang dapat menambahkan sql apa pun yang mereka inginkan. The - komentar di akhir adalah untuk memastikan sql yang tersisa dalam pernyataan asli tidak mencegah permintaan dari kompilasi di server.

FWIW, saya juga berpikir komik tersebut memiliki rincian penting yang salah: jika Anda berpikir tentang membersihkan input basis data Anda, seperti yang disarankan komik, Anda masih melakukannya dengan salah. Sebagai gantinya, Anda harus berpikir dalam hal mengkarantina input basis data Anda, dan cara yang benar untuk melakukan ini adalah melalui permintaan parameter.

Joel Coehoorn
sumber
16

The 'karakter dalam SQL digunakan untuk konstanta string. Dalam hal ini digunakan untuk mengakhiri string konstan dan bukan untuk komentar.

Rockcoder
sumber
7

Beginilah cara kerjanya: Mari kita anggap administrator sedang mencari catatan siswa

Robert'); DROP TABLE STUDENTS; --

Karena akun admin memiliki hak tinggi menghapus tabel dari akun ini dimungkinkan.

Kode untuk mengambil nama pengguna dari permintaan adalah

Sekarang kueri akan menjadi seperti ini (untuk mencari tabel siswa)

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

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

Kueri yang dihasilkan menjadi

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

Karena input pengguna tidak disanitasi, kueri di atas telah dimanipulasi menjadi 2 bagian

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

DROP TABLE STUDENTS; --

Tanda hubung ganda (-) hanya akan mengomentari bagian yang tersisa dari permintaan.

Ini berbahaya karena dapat membatalkan otentikasi kata sandi, jika ada

Yang pertama akan melakukan pencarian normal.

Yang kedua akan menjatuhkan tabel siswa jika akun memiliki hak istimewa yang memadai (Umumnya admin sekolah akan menjalankan permintaan tersebut dan akan memiliki hak istimewa yang dibicarakan di atas).

Tanpa nama
sumber
SELECT* FROM sutdents ...- Anda lupa "s". Inilah yang Anda jatuhkan. DROP TABLE STUDENTS;
DevWL
4

Anda tidak perlu memasukkan data formulir untuk membuat injeksi SQL.

Tidak ada yang menunjukkan ini sebelumnya sehingga saya mungkin mengingatkan beberapa dari Anda.

Sebagian besar kami akan mencoba untuk menambal input formulir. Tapi ini bukan satu-satunya tempat di mana Anda bisa diserang dengan injeksi SQL. Anda dapat melakukan serangan sangat sederhana dengan URL yang mengirim data melalui permintaan GET; Pertimbangkan contoh penebangan:

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

URL Anda akan terlihat di http://yoursite.com/show?id=1

Sekarang seseorang dapat mencoba sesuatu seperti ini

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

Coba ganti table_name dengan nama tabel asli. Jika dia mendapatkan nama meja Anda dengan benar, mereka akan mengosongkan meja Anda! (Sangat mudah untuk memaksa paksa URL ini dengan skrip sederhana)

Kueri Anda akan terlihat seperti ini ...

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

Contoh kode rentan PHP menggunakan PDO:

<?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!!! :( *************/
...

Solusi - gunakan metode persiapan PDO () & bindParam ():

<?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! :) *************/
...
DevWL
sumber