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.
Önerilen Doğrulama Yöntemleri
- 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*()
verender*()
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']);