Güvenlik Riskleri

Veritabanları genellikle hassas veriler içerir ve tehlikeli işlemlerin gerçekleştirilmesine izin verir. Nette Veritabanı ile güvenli çalışma için temel hususlar şunlardır:

  • Güvenli ve güvensiz API arasındaki farkı anlama
  • Parametrelendirilmiş sorguları kullanma
  • Giriş verilerinin uygun şekilde doğrulanması

SQL Enjeksiyonu Nedir?

SQL enjeksiyonu, veritabanlarıyla çalışırken karşılaşılan en ciddi güvenlik riskidir. Filtrelenmemiş kullanıcı girdisi bir SQL sorgusunun parçası haline geldiğinde ortaya çıkar. Bir saldırgan kendi SQL komutlarını ekleyebilir ve böylece

  • Yetkisiz verileri ayıklayın
  • Veritabanındaki verileri değiştirme veya silme
  • Kimlik doğrulamayı atla
// ❌ TEHLİKELİ KOD - SQL enjeksiyonuna karşı savunmasız
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Bir saldırgan aşağıdaki gibi bir değer girebilir: ' VEYA '1'='1
// Ortaya çıkan sorgu şöyle olacaktır: SELECT * FROM users WHERE name = '' OR '1'='1'
// Bu da tüm kullanıcıları döndürür

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

// ❌ TEHLİKELİ KOD - SQL enjeksiyonuna karşı savunmasız
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");

Güvenli Parametreli Sorgular

SQL enjeksiyonuna karşı temel savunma parametrelendirilmiş sorgulardır. Nette Database bunları kullanmak için çeşitli yollar sunar.

En basit yol soru işareti yer tutucuları kullanmaktır:

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

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

Bu, Veritabanı Gezgini 'nde soru işareti yer tutucuları ve parametreler içeren ifadelerin eklenmesine izin veren diğer tüm yöntemler için geçerlidir.

INSERT, UPDATE veya WHERE cümleleri için değerleri bir dizi içinde aktarabilirsiniz:

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

// ✅ Explorer'da Güvenli INSERT
$table->insert([
	'name' => $name,
	'email' => $email,
]);

Parametre Değer Doğrulaması

Parametrelendirilmiş sorgular güvenli veritabanı çalışmasının temel taşıdır. Ancak, bunlara aktarılan değerlerin birkaç doğrulama seviyesinden geçmesi gerekir:

Tip Kontrolü

Parametrelerin doğru veri türüne sahip olması çok önemlidir – bu, Nette Veritabanını güvenli bir şekilde kullanmak için gerekli bir koşuldur. Veritabanı, tüm girdi verilerinin sütuna karşılık gelen doğru veri türüne sahip olduğunu varsayar.

Örneğin, önceki örneklerde $name beklenmedik bir şekilde dize yerine dizi olsaydı, Nette Veritabanı tüm öğelerini SQL sorgusuna eklemeye çalışacak ve bu da bir hataya neden olacaktı. Bu nedenle, $_GET, $_POST veya $_COOKIE adreslerindeki doğrulanmamış verileri doğrudan veritabanı sorgularında asla kullanmayın.

Format Doğrulama

İkinci seviye, verilerin biçimini kontrol eder; örneğin, dizelerin UTF-8 kodlu olduğundan ve uzunluklarının sütun tanımıyla eşleştiğinden emin olmak veya sayısal değerlerin sütunun veri türü için izin verilen aralıkta olduğunu doğrulamak gibi.

Bu düzeyde, kısmen veritabanının kendisine güvenebilirsiniz – birçok veritabanı geçersiz verileri reddeder. Ancak, davranış değişebilir: bazıları uzun dizeleri sessizce kesebilir veya aralık dışındaki sayıları kırpabilir.

Alana Özgü Doğrulama

Üçüncü seviye uygulamanıza özel mantıksal kontrolleri içerir. Örneğin, seçim kutularındaki değerlerin mevcut seçeneklerle eşleştiğini, sayıların beklenen bir aralıkta olduğunu (örneğin, 0–150 yaş) veya değerler arasındaki ilişkilerin mantıklı olduğunu doğrulamak.

  • Tüm girdilerin uygun şekilde doğrulanmasını otomatik olarak gerçekleştiren Nette Forms'u kullanın.
  • Sunucuları kullanın ve parametre veri türlerini action*() ve render*() yöntemlerinde bildirin.
  • Ya da filter_var() gibi standart PHP araçlarını kullanarak özel bir doğrulama katmanı uygulayabilirsiniz.

Kolonlarla Güvenli Çalışma

Önceki bölümde, parametre değerlerinin nasıl doğru bir şekilde doğrulanacağını ele aldık. Ancak, SQL sorgularında dizileri kullanırken, anahtarlarına da aynı dikkat gösterilmelidir.

// ❌ TEHLİKELİ KOD - dizi anahtarları sterilize edilmemiş
$database->query('INSERT INTO users', $_POST);

INSERT ve UPDATE komutları için bu büyük 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 (Toplu Atama Güvenlik Açığı olarak bilinir).

NEREDE koşullarında, operatör içerebildikleri için daha da tehlikelidir:

// ❌ TEHLİKELİ KOD - dizi anahtarları sterilize edilmemiş
$_POST['salary >'] = 100000;
$database->query('SELECT * FROM users WHERE', $_POST);
// sorguyu çalıştırır WHERE (`salary` > 100000)

Bir saldırgan, çalışan maaşlarını sistematik olarak ortaya çıkarmak için bu yaklaşımı kullanabilir. Önce 100.000'in üzerindeki, sonra 50.000'in altındaki maaşlar için bir sorguyla başlayabilir ve aralığı kademeli olarak daraltarak tüm çalışanların yaklaşık maaşlarını ortaya çıkarabilirler. Bu saldırı türüne SQL numaralandırma adı verilir.

where() ve whereOr() yöntemleri daha da esnektir ve hem anahtarlarda hem de değerlerde operatörler ve fonksiyonlar dahil olmak üzere SQL ifadelerini destekler. Bu, bir saldırgana karmaşık SQL enjeksiyonu gerçekleştirme olanağı verir:

// ❌ TEHLİKELİ KOD - saldırgan kendi SQL'ini ekleyebilir
$_POST = ['0) UNION SELECT name, salary FROM users WHERE (1'];
$table->where($_POST);
// sorguyu çalıştırır WHERE (0) UNION SELECT name, salary FROM users WHERE (1)

Bu saldırı orijinal koşulu 0) ile sonlandırır, UNION kullanarak users tablosundan hassas verileri elde etmek için kendi SELECT adresini ekler ve WHERE (1) kullanarak sözdizimsel olarak doğru bir sorgu ile kapatır.

Sütun Beyaz Listesi

Sütun adlarıyla güvenli bir şekilde çalışmak için, kullanıcıların yalnızca izin verilen sütunlarla etkileşime girebilmelerini ve kendi sütunlarını ekleyememelerini sağlayan bir mekanizmaya ihtiyacınız vardır. Tehlikeli sütun adlarını tespit etmeye ve engellemeye çalışmak (kara listeye almak) güvenilir değildir; bir saldırgan her zaman tehlikeli bir sütun adı yazmak için tahmin etmediğiniz yeni bir yol bulabilir.

Bu nedenle, mantığı tersine çevirmek ve izin verilen sütunların açık bir listesini tanımlamak (beyaz liste) çok daha güvenlidir:

// Kullanıcının değiştirmesine izin verilen sütunlar
$allowedColumns = ['name', 'email', 'active'];

// Girdiden tüm yetkisiz sütunları kaldırın
$filteredData = array_intersect_key($userData, array_flip($allowedColumns));

// ✅ Artık aşağıdaki gibi sorgularda kullanmak güvenlidir:
$database->query('INSERT INTO users', $filteredData);
$table->update($filteredData);
$table->where($filteredData);

Dinamik Tanımlayıcılar

Dinamik tablo ve sütun adları için ?name yer tutucusunu kullanın. Bu, tanımlayıcıların verilen veritabanı sözdizimine göre uygun şekilde kaçmasını sağlar (örneğin, MySQL'de backtick kullanımı):

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

Önemli: ?name sembolünü yalnızca uygulama kodunda tanımlanan güvenilir değerler için kullanın. Kullanıcı tarafından sağlanan değerler için yine bir beyaz liste kullanın. Aksi takdirde, güvenlik açıkları riskiyle karşı karşıya kalırsınız:

// TEHLİKELİ - asla kullanıcı girişi kullanmayın
$database->query('SELECT ?name FROM users', $_GET['column']);
versiyon: 4.0