Apakah prosedur tersimpan mencegah injeksi SQL?

83

Benarkah prosedur tersimpan mencegah serangan injeksi SQL terhadap database PostgreSQL? Saya melakukan sedikit riset dan menemukan bahwa SQL Server, Oracle dan MySQL tidak aman terhadap injeksi SQL bahkan jika kita hanya menggunakan prosedur tersimpan. Namun, masalah ini tidak ada di PostgreSQL.

Apakah implementasi prosedur tersimpan dalam inti PostgreSQL mencegah serangan injeksi SQL atau itu sesuatu yang lain? Atau apakah PostgreSQL juga rentan terhadap injeksi SQL walaupun kita hanya menggunakan prosedur tersimpan? Jika demikian, tolong tunjukkan saya contoh (mis. Buku, situs, kertas, dll).

Am1rr3zA
sumber
4
Anehnya, jawaban teratas di sini kebanyakan PL berurusan dengan SQL Server sementara pertanyaannya adalah tentang Postgres . Berikut adalah jawaban terkait untuk Postgres: dba.stackexchange.com/questions/49699/… . Ada beberapa yang lain, coba cari: dba.stackexchange.com/…
Erwin Brandstetter
@ ErwinBrandstetter pertanyaan awal tidak ditandai (oleh OP) dengan postgres dan - dan masih - menyebutkan beberapa DBMS lainnya. Saya kira itulah alasan mengapa berbagai jawaban berfokus pada DBMS lain. Saya sarankan Anda menambahkan satu lagi fokus pada Postgres.
ypercubeᵀᴹ
@ ypercubeᵀᴹ: Saya akan menambahkan jawaban di sini ketika saya menemukan waktu. Sementara itu saya memperbarui dba.stackexchange.com/questions/49699/… menjadi lebih jelas dan komprehensif.
Erwin Brandstetter

Jawaban:

71

Tidak, prosedur tersimpan tidak mencegah injeksi SQL. Berikut ini adalah contoh aktual (dari aplikasi in-house yang dibuat oleh seseorang tempat saya bekerja) dari prosedur tersimpan yang sayangnya mengizinkan injeksi SQL:

Kode server sql ini:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

kira-kira sama dengan postgres:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

Gagasan pengembang adalah membuat prosedur pencarian serbaguna, tetapi hasilnya adalah klausa WHERE dapat berisi apa pun yang diinginkan pengguna, memungkinkan kunjungan dari Bobby Tables yang kecil .

Apakah Anda menggunakan pernyataan SQL atau prosedur tersimpan tidak masalah. Yang penting adalah apakah SQL Anda menggunakan parameter atau string gabungan. Parameter mencegah injeksi SQL; string bersambung memungkinkan injeksi SQL.

Kyralessa
sumber
46

Serangan SQL-Injection adalah serangan di mana input yang tidak dipercaya secara langsung ditambahkan, memungkinkan pengguna untuk secara efektif mengeksekusi kode arbitrer, seperti yang diilustrasikan dalam komik XKCD kanonik ini.

Dengan demikian, kita dapat situasi:

userInput = getFromHTML # "Robert ') Drop table students; -"

Query = "Pilih * dari siswa di mana studentName =" + userInput

Prosedur Tersimpan, secara umum, pertahanan yang baik terhadap serangan injeksi SQL karena parameter yang masuk tidak pernah diuraikan.

Dalam prosedur tersimpan, di sebagian besar DB (dan program, jangan lupa bahwa kueri yang dikompilasi dihitung sebagai prosedur tersimpan) terlihat seperti berikut:

 

buat Stored procdure foo (
pilih * dari siswa di mana studentName =: 1
);

Kemudian, ketika program menginginkan akses, ia memanggil foo(userInput)dan dengan senang hati mengambil hasilnya.

Prosedur tersimpan bukanlah pertahanan ajaib terhadap SQL-Injection, karena orang cukup mampu menulis prosedur tersimpan yang buruk . Namun, kueri yang telah dikompilasi sebelumnya, baik yang disimpan dalam database atau dalam program, jauh lebih sulit untuk membuka celah keamanan jika Anda memahami cara kerja SQL-Injection.

Anda dapat membaca lebih lanjut tentang SQL-Injection:

Brian Ballsun-Stanton
sumber
29

Ya, sampai batas tertentu.
Prosedur Tersimpan saja tidak akan mencegah SQL Injection.

Ijinkan saya mengutip tentang SQL Injection dari OWASP

Serangan injeksi SQL terdiri dari penyisipan atau "injeksi" dari permintaan SQL melalui input data dari klien ke aplikasi. Eksploit injeksi SQL yang berhasil dapat membaca data sensitif dari basis data, memodifikasi data basis data (Masukkan / Perbarui / Hapus), jalankan operasi administrasi pada basis data (seperti mematikan DBMS), memulihkan konten dari file yang diberikan saat ini pada file DBMS sistem dan dalam beberapa kasus mengeluarkan perintah ke sistem operasi. Serangan injeksi SQL adalah jenis serangan injeksi, di mana perintah SQL disuntikkan ke input data-pesawat untuk mempengaruhi pelaksanaan perintah SQL yang telah ditentukan.

Anda harus membersihkan input pengguna dan tidak menggabungkan pernyataan SQL, bahkan jika Anda menggunakan prosedur tersimpan.

Jeff Attwood menjelaskan konsekuensi menggabungkan sql di " Beri aku parameterisasi SQL, atau beri aku kematian "

Berikut ini adalah kartun menarik yang muncul di benak saya setiap kali saya mendengar SQL Injection, teks alternatif saya pikir Anda mengerti :-)

Lihat pada Cheat Sheet Pencegahan Injeksi SQL , metode pencegahan dijelaskan dengan rapi ...

CoderHawk
sumber
12

Rangkaian string adalah penyebab SQL Injection. Ini dihindari menggunakan parametrisation.

Prosedur yang tersimpan menambahkan lapisan keamanan tambahan dengan memberlakukan sintaksis yang tidak valid ketika Anda menggabungkan, tetapi tidak "aman" jika Anda menggunakan, katakanlah, SQL dinamis di dalamnya.

Jadi, kode Anda di atas disebabkan oleh rangkaian string ini

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

Untungnya , ini memberikan sintaks yang tidak valid

Parametrising itu akan memberi

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

Ini berarti

  • @UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password = monkey

Sekarang, dalam kode di atas Anda tidak akan mendapatkan baris karena saya menganggap Anda tidak memiliki pengguna x' AND 1=(SELECT COUNT(*) FROM Client); --

Jika proc yang disimpan tampak seperti ini (menggunakan SQL dinamis gabungan ), maka panggilan proc tersimpan parametris Anda akan tetap mengizinkan SQL Injection

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

Jadi, seperti yang diperagakan, rangkaian string adalah musuh utama untuk injeksi SQL

Prosedur yang tersimpan memang menambahkan enkapsulasi, penanganan transaksi, pengurangan izin, dll., Tetapi masih bisa disalahgunakan untuk injeksi SQL.

Anda dapat melihat Stack Overflow untuk informasi lebih lanjut tentang parametrisation

gbn
sumber
10

"Serangan injeksi SQL terjadi ketika masukan pengguna secara tidak benar dikodekan. Biasanya, masukan pengguna beberapa data pengguna mengirimkan dengan permintaan dia, nilai-nilai yaitu di $_GET, $_POST, $_COOKIE, $_REQUEST, atau $_SERVERarray. Namun, input pengguna dapat juga datang dari berbagai lainnya sumber, seperti soket, situs web jarak jauh, file, dll. Oleh karena itu, Anda harus benar-benar memperlakukan semuanya kecuali konstanta (seperti 'foobar') sebagai input pengguna . "

Saya telah menyelidiki secara menyeluruh tentang hal ini baru-baru ini dan ingin berbagi dengan orang lain materi yang cukup menarik, sehingga membuat pos ini lebih lengkap dan instruktif untuk semua orang.



Dari YouTube


Dari Wikipedia


Dari OWASP


Dari Manual PHP


Dari Microsoft dan Oracle


Stack Overflow


Pemindai injeksi SQL

Ilia Rostovtsev
sumber
2

Prosedur yang tersimpan tidak secara ajaib mencegah injeksi SQL, tetapi prosedur ini membuatnya mencegahnya menjadi jauh lebih mudah. Yang harus Anda lakukan adalah sesuatu seperti yang berikut ini (contoh Postgres):

CREATE OR REPLACE FUNCTION my_func (
  IN in_user_id INT 
)
[snip]
  SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]

Itu dia! Masalahnya hanya muncul ketika membentuk kueri melalui rangkaian string (yaitu SQL dinamis), dan bahkan dalam kasus tersebut Anda mungkin dapat mengikat! (Tergantung pada database.)

Cara menghindari injeksi SQL dalam kueri dinamis Anda:

Langkah 1) Tanyakan pada diri Anda apakah Anda benar-benar membutuhkan permintaan dinamis. Jika Anda menyatukan string hanya untuk mengatur input, maka Anda mungkin melakukan kesalahan. (Ada pengecualian untuk aturan ini - satu pengecualian adalah untuk melaporkan permintaan pada beberapa database, Anda mungkin memiliki masalah kinerja jika Anda tidak memaksanya untuk mengkompilasi permintaan baru dengan setiap eksekusi. Tetapi teliti masalah ini sebelum Anda langsung membahasnya. )

Langkah 2) Meneliti cara yang tepat untuk mengatur variabel untuk RDBMS khusus Anda. Sebagai contoh, Oracle memungkinkan Anda melakukan hal berikut (mengutip dari dokumen mereka):

sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE ' 
           || v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!

Di sini Anda masih belum menyatukan input. Anda mengikat dengan aman! Hore!

Jika database Anda tidak mendukung sesuatu seperti di atas (semoga tidak satupun dari mereka yang seburuk ini, tapi saya tidak akan terkejut) - atau jika Anda masih benar-benar harus menyatukan input Anda (seperti dalam kasus "kadang-kadang" melaporkan kueri pelaporan sebagai Saya mengisyaratkan di atas), maka Anda harus menggunakan fungsi melarikan diri yang tepat. Jangan menulisnya sendiri. Misalnya postgres menyediakan fungsi quote_literal (). Jadi, Anda akan menjalankan:

sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);

Dengan cara ini jika in_name adalah sesuatu yang licik seperti '[snip] atau 1 = 1' (bagian "atau 1 = 1" berarti memilih semua baris, yang memungkinkan pengguna untuk melihat gaji yang seharusnya tidak seharusnya!), Maka quote_literal menyimpan pantat Anda dengan membuat string yang dihasilkan:

SELECT salary FROM employees WHERE name = '[snip] or 1=1'

Tidak ada hasil yang ditemukan (kecuali jika Anda memiliki beberapa karyawan dengan nama yang sangat aneh.)

Itulah intinya! Sekarang biarkan saya meninggalkan Anda dengan tautan ke posting klasik oleh guru Oracle Tom Kyte tentang masalah SQL Injection, untuk mengarahkan poinnya: Linky

MWDB
sumber
Jangan lupa untuk menyebutkan quote_ident()- tetapi secara umum cara termudah untuk menulis SQL dinamis tahan injeksi adalah dengan menggunakan format()dan menggunakan placeholder %Iuntuk pengidentifikasi dan %Luntuk literal. Dengan begitu SQL jauh lebih mudah dibaca daripada versi yang setara menggunakan ||dan quote_....()fungsinya
a_horse_with_no_name