Güvenlik Riskleri

Veritabanları genellikle hassas veriler içerir ve tehlikeli işlemlere izin verir. Nette Veritabanı bir dizi güvenlik özelliği sağlar. Ancak, güvenli ve güvenli olmayan API'ler arasındaki farkı anlamak çok önemlidir.

SQL Enjeksiyonu

SQL enjeksiyonu, veritabanlarıyla çalışırken karşılaşılan en ciddi güvenlik riskidir. Kontrol edilmemiş kullanıcı girdisi bir SQL sorgusunun parçası haline geldiğinde ortaya çıkar. Bir saldırgan kendi SQL komutlarını enjekte ederek veritabanındaki verileri elde edebilir veya değiştirebilir.

// ❌ GÜVENLİ OLMAYAN KOD - SQL enjeksiyonuna karşı savunmasız
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Saldırgan şöyle bir şey girebilir: ' VEYA '1'='1
// Ortaya çıkan sorgu şöyle olacaktır:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// Bu, tüm kullanıcıları döndürür!

Aynı durum Veritabanı Gezgini için de geçerlidir:

// ❌ GÜVENLİ OLMAYAN KOD
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");

Güvenli Parametrelendirilmiş Sorgular

SQL sorgularına değer eklemenin güvenli yolu parametrelendirilmiş sorgulardır. Nette Veritabanı bunları kullanmak için çeşitli yollar sunar.

Yer Tutucu Soru İşaretleri

En basit yöntem yer tutucu soru işaretleri kullanmaktır:

// ✅ Güvenli parametrelendirilmiş sorgular
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);

// ✅ Explorer'da güvenli durum
$table->where('name = ?', $_GET['name']);

Aynı durum, Veritabanı Gezgini'nde yer tutucu soru işaretleri ve parametreler içeren ifadelerin eklenmesine izin veren diğer tüm yöntemler için de geçerlidir.

Değerler skaler tipte olmalıdır (string, int, float, bool) veya null, $_GET['name'] bir dizi ise, Nette Database tüm elemanlarını SQL sorgusuna dahil edecektir, bu da istenmeyen bir durum olabilir.

Değer Dizileri

INSERT, UPDATE veya WHERE cümleleri için değer dizileri kullanabiliriz:

// ✅ Güvenli INSERT
$database->query('INSERT INTO users', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
]);

// ✅ Güvenli Güncelleme
$database->query('UPDATE users SET', [
	'name' => $_GET['name'],
	'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);

Nette Veritabanı, parametrelendirilmiş sorgulardan geçirilen tüm değerleri otomatik olarak kaçar. Ancak, parametrelerin doğru veri türüne sahip olduğundan emin olmalıyız.

Dizi Anahtarları Güvenli Bir API Değildir

Dizilerdeki değerler güvenliyken, aynı şey anahtarlar için söylenemez:

// ❌ GÜVENLİ OLMAYAN KOD - anahtarlar SQL enjeksiyonu içerebilir
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);

INSERT ve UPDATE komutları için bu kritik bir güvenlik açığıdır – bir saldırgan veritabanındaki herhangi bir sütunu ekleyebilir veya değiştirebilir. Örneğin, is_admin = 1 adresini ayarlayabilir veya hassas sütunlara rastgele veri ekleyebilirler.

WHERE koşullarında, bu daha da tehlikelidir çünkü veritabanı hakkında kademeli olarak bilgi almak için bir teknik olan SQL numaralandırmasına izin verir. Bir saldırgan $_GET adresine bu şekilde enjekte ederek çalışan maaşlarını keşfetmeye çalışabilir:

$_GET = ['salary >', 100000];   // maaş aralıklarını belirlemeye başlar

Ancak asıl sorun, WHERE koşullarının anahtarlardaki SQL ifadelerini desteklemesidir:

// Anahtarlarda operatörlerin meşru kullanımı
$table->where([
    'age > ?' => 18,
    'ROUND(score, ?) > ?' => [2, 75.5],
]);

// ❌ GÜVENLİ DEĞİL: saldırgan kendi SQL'ini enjekte edebilir
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // saldırganın yönetici maaşlarını elde etmesini sağlar

Bu bir kez daha SQL enjeksiyonudur.

Sütunları Beyaz Listeye Alma

Kullanıcıların sütun seçmesine izin vermek istiyorsanız, her zaman bir beyaz liste kullanın:

// ✅ Güvenli işleme - sadece izin verilen sütunlar
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));

$database->query('INSERT INTO users', $values);

Dinamik Tanımlayıcılar

Dinamik tablo ve sütun adları için ?name yer tutucusunu kullanın:

// ✅ Güvenilir tanımlayıcıların güvenli kullanımı
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);

// ❌ GÜVENLİ DEĞİL - asla kullanıcı girişi kullanmayın
$database->query('SELECT ?name FROM users', $_GET['column']);

?name sembolü yalnızca uygulama kodunda tanımlanan güvenilir değerler için kullanılmalıdır. Kullanıcı tarafından sağlanan değerler için yine bir beyaz liste kullanın.

versiyon: 4.0