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.