SQL injection nedir?

SQL injection, veritabanına yapılan saldırıların en yaygın türüdür. Bu saldırı, kötü niyetli bir kullanıcının web uygulaması veya veritabanı arayüzüne girdiği verileri kullanarak, veritabanınızın içeriğini değiştirebileceği veya özel bilgileri çalabileceği anlamına gelir.

SQL injection saldırıları, web uygulamalarının veritabanına sorgular gönderirken kullandığı SQL (Structured Query Language) komutlarının güvenliğini aşarak gerçekleştirilir. Örneğin, bir web uygulaması bir kullanıcının girdiği kullanıcı adı ve şifreyi kontrol etmek için veritabanına bir SQL sorgusu gönderir. Eğer sorgunun güvenliği aşılırsa, kötü niyetli bir kullanıcı, sorgunun içine zararlı bir kod ekleyebilir ve veritabanının içeriğini değiştirebilir veya özel bilgileri çalabilir.

SQL injection saldırılarını önlemek için, web uygulamalarının veritabanına gönderdiği sorguların güvenliğini sağlamak önemlidir. Bu, sorguların parametrelerinin doğru şekilde filtrelenmesi, sorguların doğru bir şekilde yazılması ve veritabanının güncel güvenlik yamalarının yüklü olması gibi önlemlerle gerçekleştirilebilir.

Örneğin, bir web uygulaması bir kullanıcının girdiği kullanıcı adı ve şifreyi kontrol etmek için aşağıdaki SQL sorgusunu veritabanına gönderir:

SELECT * FROM users WHERE username = '$username' AND password = '$password'

Bu sorguda, $username ve $password değişkenleri kullanıcı tarafından girilen kullanıcı adı ve şifre ile değiştirilir. Ancak eğer sorgunun güvenliği aşılırsa, kötü niyetli bir kullanıcı, $username veya $password değişkenlerine zararlı bir kod ekleyebilir. Örneğin, aşağıdaki gibi:

$username = "admin' OR '1'='1"; 
$password = "anything";

Bu durumda, gerçekleşen sorgu şöyle olacak:

SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything'

Bu sorgu, tüm kullanıcıların bilgilerini döndürecektir. Çünkü ‘1’=’1′ her zaman doğru olduğu için sorgunun where kosulunu geçmiştir.

SQL injection saldırılarını önlemek

SQL injection saldırılarını önlemek için, web uygulamalarının veritabanına gönderdiği sorguların güvenliğini sağlamak önemlidir. Bunun için birkaç yol vardır:

1. Parametreleri filtrelemek

Kullanıcı tarafından girilen verilerin filtre edilmesi, sorgular içinde yer alabilecek potansiyel zararlı kodları önler. Örneğin, kullanıcı adı ve şifre gibi girdileri filtrelemek, SQL komutlarını içermemeleri için gereklidir.

$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);

2. Prepared statement kullanmak

Prepared statement, sorguların parçalara ayrılmasını sağlar. Bu sayede, kullanıcı tarafından girilen veriler sorgunun bir parçası olarak değil, ayrı bir değişken olarak kabul edilir. Böylece zararlı kodlar sorgunun içine eklenemez.

$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND password = :password');
$stmt->execute(array(':username' => $username, ':password' => $password));

3. ORM kullanmak

ORM (Object-relational mapping) kullanmak, veritabanı ile iletişim kurarken kodun daha okunaklı ve anlaşılır hale gelmesini sağlar. Aynı zamanda zararlı kodların sorgunun içine eklenmesini engeller.

$user = User::where('username', '=', $username)->where('password', '=', $password)->first();

Bu örnekler sadece SQL injection saldırılarını önleme yollarından bazılarıdır.

Güvenli bir uygulama için alınabilecek önlemler

Gerçekte güvenli bir web uygulaması yazmak için daha fazla önlem almak gerekir. Bunlardan bazıları şunlardır:

  • Güncel bir web sunucusu kullanmak: Güncel web sunucuları, daha iyi güvenlik özellikleri sunarlar.
  • Güncel bir web framework kullanmak: Güncel web frameworkleri, daha iyi güvenlik özellikleri sunarlar.
  • Güncel bir sürümde veritabanı yazılımı kullanmak: Veritabanı yazılımının güncel sürümü, daha iyi güvenlik özellikleri sunar.
  • Güncel güvenlik yamalarını kullanmak: Güncel güvenlik yamaları, bilinen güvenlik açıklarını kapatır.
  • Güncel bir güvenlik politikası oluşturmak: Güncel bir güvenlik politikası, güvenli bir ortam oluşturmak için gerekli adımları belirler.
  • Güvenlik taraması yaptırmak: Güvenlik taraması, uygulamanın güvenliği açısından potansiyel açıkları tespit etmek için yapılır.
  • Yetkilendirme ve yetkilendirme kontrolleri: Yetkilendirme, sadece yetkili kullanıcıların belirli işlemleri yapmasını sağlar. Yetkilendirme kontrolleri ise, yetkili kullanıcıların yetkilerini kontrol etmek için yapılır.
  • Güvenli veri depolama: Özel bilgilerin güvenli bir şekilde depolanması gerekir. Bu nedenle verilerin şifrelenmesi veya diğer güvenlik önlemleri alınması gerekir.
  • İzleme ve kayıt tutma: Uygulamanın kullanımını izlemek ve kayıt tutmak, olası güvenlik açıklarının tespit edilmesini ve önlenmesini kolaylaştırır.
  • Güvenlik eğitimi: Tüm çalışanların güvenliği sağlamak için gerekli bilgi ve becerilere sahip olmasını sağlamak için güvenlik eğitimi yapmak önemlidir.

Foreign Key’e Sahip Kolonların Farklı Data Uzunlukta Olanları Nasıl Bulunur?

Oracle veritabanında oluşturulmuş foreign keylerin bulunduğu kolonların farklı data uzunluğuna sahip olanlarını aşağıdaki sql ile bulabilirsiniz.

SELECT acol.owner,
acol.table_name,
acol.column_name,
acol.data_type,
acol.data_length,
acol.data_precision,
bcol.owner,
bcol.table_name,
bcol.column_name,
bcol.data_type,
bcol.data_length,
bcol.data_precision
FROM dba_tab_columns acol, dba_tab_columns bcol
WHERE (acol.owner,
acol.table_name,
acol.column_name,
bcol.owner,
bcol.table_name,
bcol.column_name) IN
(SELECT b.owner b_owner,
b.table_name b_tblname,
b.column_name b_column,
c.owner c_owner,
c.table_name c_tblname,
c.column_name c_column
FROM dba_cons_columns b,
dba_cons_columns c,
dba_constraints a
WHERE b.constraint_name = a.constraint_name
AND a.owner = b.owner
AND b.position = c.position
AND c.constraint_name = a.r_constraint_name
AND c.owner = a.r_owner
AND a.constraint_type = 'R') and (acol.data_length <> acol.data_length or acol.data_precision <> bcol.data_precision );

SQL Tune Nasıl Yapılır

Bu yazıda bir sql’i tune edip sorgu sonucunun hızlandırılması konusunda index ekleme yöntemini açıklayacağım. Burada anlatacağım yöntemi oracle, postgresql, mysql ve diğer veritabanlarında kullanabilirsiniz.

İlk olarak index nedir ve neden eklenmesi gerektiğini açıklamak istiyorum. İndex oluşturulmuş bir ağaç yapısıdır ve birden çok index türü vardır. B-tree index yapısını aşağıdaki şekilde inceleyebilirsiniz. Leaf block dediğimiz yerde ilgili tablonun içerisindeki rowların id leri bulunmaktadır. Bunu adres olarak düşünebiliriz. Branch blocks dediğimiz yer ise datanın belirli bir düzene göre gruplandığı yerdir. Aşağıdaki resimde görebileceğiniz gibi job_id kolonunda 2 değerine sahip satırların id numaraları bir blokta tutulmuş. Bu da job_id = 2 koşulu istendiğinde tabloyu full okumak yerine sadece ilgili bloklara gidip okuma yapacağı anlamına gelmektedir.

Tablo full okunmadığı için ilgili sql daha hızlı sonuç üretecektir. Daha hızlı sonuç üretmesi veritabanının fazla efor harcamamasına ve daha performanslı çalışmasına neden olacaktır.

Çok fazla transaction alan veritabanlarında sql’lerin sorgu sonuç süreleri kritik bir öneme sahiptir. Şu şekilde bir hesap yapacak olursak daha anlaşılır olacaktır. Elimizde çalışması 1 sn süren bir sql olsun ve bu sql uygulamadan veritabanına 10 bin kez gönderilsin. Bu işlem veritabanında 1 sn* 10 bin kez çalışacaktır ve veritabanını toplamda 10 bin saniye / 60 = 166 dakika meşgul edecektir. Tabi cpu’nun saniye de milyarlarca işlem yapabildiği için gerçek hayatta bu işlem 166 dakikada sürmeyecektir.

İlgili sql’e index ekleyip çalışma süresini yarıya düşürdüğümüzü varsayarsak toplamda 83 dakika kazanmış olacağız ki bu muazzam bir süre. Bu örnekte saniyeler üzerinden gidiyorum fakat sql’lerin veritabanında çalışma süreleri mili saniyeleri geçmemesi gerekiyor. 0.5 saniye bile bir sorgu için aşırı büyük bir değer.

Şimdi veritabanının bir sorguyu çalıştırırken sonucu getirmek için izlediği yolu göstereceğim. Örnek olarak elimizde aşağıdaki gibi bir sql sorgusu olsun.

select firts_name, last_name from hr.employee where job_id = 2;

Bu sql’i veritabanı şu şekilde yorumlayacaktır:

– job_id kolonunda değeri 2 olan
– hr.employee tablosundaki
– first_name ve last_name kolonundaki tüm verileri getir.

Sorgu bizim aksimize tersten okunarak istenilen dataları getirmek için işlenmeye başlandı. Bu kısımda job_id kolonunda 2 değerine sahip satırlar için tabloyu satır satır okuyup eşleşen kayıtları bulmaya çalışacaktır. İlgili kolonda eğer bir index yok ise explain plan da aşağıda da görebileceğiniz gibi “full table access” yöntemi kullanılacaktır.

OPERATIONOPTIONSOBJECT_NAMEPOSITION
SELECT STATEMENT2
..TABLE ACCESSTABLE ACCESS FULLEMPLOYEES1

Eğer employees tablosunun job_id kolonunda bir index olmuş olsaydı tabloyu full okumak zorunda kalmayacak ve explain plan’ı aşağıdaki gibi olacaktı.

OPERATIONOPTIONSOBJECT_NAMEPOSITION
SELECT STATEMENT2
..TABLE ACCESSINDEX RANGE SCANEMPLOYEES1

Şekilde görüldüğü gibi index range scan yöntemi ile tabloya yapılan erişim sorgu maliyetini düşürmektedir. Elinizdeki sql’leri bu doğrultuda inceleyerek gerekli olabilecek indexleri oluşturabilirsiniz.

Son olarak aşağıdaki gibi bir sql’i ele alalım.

select firts_name, last_name from hr.employee where job_id = 2 and first_name = 'Emrah';

Bu sqli çalıştırırken job_id kolonunda index olmasına rağmen sorgu çalıştırıldığında ilgili index kullanılmayabilir. Bu durumda bileşik index oluşturulması gerekir. İlk oluşturulan basit index tipini ifade etmekteydi. Bu durumda bileşik index oluşturmak zorunda kalınabilirdi. Oracle veritabanında basit ve bileşik index aşağıdaki gibi oluşturulabilir.

# basit index
create index [index_name] on [table_name] (column);
# bileşik index
create index [index_name] on [table_name] (column1,column2);