Güvenlik Pratikleri
Bu platformun sağladığı güvenlik katmanları ve her birini devreye sokan çağrı. Yanında yayına çıkmış üç hata. Sürekli açık kalan bir kapı, koruduğu değeri yok eden bir filtre, çalıştırılabilir işaretleme olarak sunulan bir yükleme.
Genel Bakış
Aşağıdaki her katman bu kod tabanında var, adı belli tek bir çağrıyla devreye girer ve adı belli tek bir yerde uygulanır. Hiçbiri kendiliğinden çalışmaz. Bir controller kendi girdisini filtrelemez, açık bir form isteyene kadar korunmaz.
Üçüncü taraf kodu en sık bu iki katmanda yanılır. Girdi filtreleme alan ve tip başına olur. Müşteri API'sinde sahiplik istek başına değil, sorgu başına olur.
Ön Koşullar
- Filtre sözlüğünün tamamı için Kullanıcı Girdisini Filtreleme. Bu makale yanlış filtreyi seçmenin sonuçlarını anlatır.
- Güvenli hâle getirilecek bir ekran ve ona kimin ulaştığı. Bir admin operation'ı, açık bir form, bir müşteri ucu ve bir modül ucu farklı katmanları devreye sokar.
- Başka bir sekmede açık duran panel Güvenlik ayarları. Aşağıdaki her eşik bir yapılandırma anahtarı. Modülünüze sabit yazılmış bir sınır, operatörün ayarını yok sayar.
Yapı
Katmanlar
| Katman | Neyi durdurur | Devreye sokan |
|---|---|---|
| Girdi filtreleme | Bir alanın içine sokuşturulan enjeksiyon ve işaretleme | Kodunuz, alan başına tek çağrı, alana uyan tiple |
| Çıktı kodlama | Saklanmış değerler geri gösterilirken çalışır | Admin şablonlarında ve JSON'da kodunuz. Website şablonları kendiliğinden kaçırır |
| Yetki kontrolü | Giriş yapmış bir operatörün rolünün izin vermediği bir şeyi yapması | Operation sarmalayıcısı, operation'ın kendi bildiriminden. Yedek kanca cevap veriyorsa kodunuz |
| API kapsamı | Geçerli bir kimlik bilgisinin kendisine verilmemiş bir uca ulaşması | Çekirdek, rota yalnız-kimlik bayrağını kurmadıysa; kurduysa geçerli her anahtar geçer |
| Sahiplik kapsamı | Bir müşterinin başka bir müşterinin satırlarını okuması | Her sorguda kodunuz, enjekte edilen sahipten, istek gövdesinden asla |
| Form kapıları | Siteler arası gönderim, sel, botlar ve spam içeriği | Kodunuz, sabit sırayla dört ya da beş çağrı, artı iki şablon etiketi |
Adım Adım
Girdiyi Güvenle Okuyun
- Bir süper globale asla dokunmayın. Kaynak önekiyle filtre yardımcısından okuyun.
- Filtre tipini alışkanlıktan değil, alanın ne olduğundan seçin.
- Filtrelemek doğrulamak değildir. Tip filtresi ait olamayacakları çıkardıktan sonra kalanın kullanılabilir olduğunu kontrol edin.
- Her dizi değerini varsayılanla okuyun ve aynı ifade içinde tip dönüşümü yapın. Döngüdeki eksik anahtar her turda bir disk yazımına yol açar.
- Varlık kontrolünü
falseile yazın,nullile asla.
Çağıranı Yetkilendirin
- Normal bir admin operation'ında yetki listesini operation'ın kendi özelliklerinde bildirin. Sarmalayıcı, metodunuz çalışmadan önce onu kontrol eder.
- Yedek kancanın cevapladığı operation'da yetkiyi kendiniz, dinleyicide ilk iş olarak kontrol edin. Sarmalayıcı zaten reddetmiş, ona ait hiçbir şey geçerli değil. Ne yetki listesi, ne demo kipi kapısı, ne de AJAX-dışı isteği reddeden kontrol.
- Bir modül API rotasında kapsam ile yalnız-kimlik bayrağı arasında bilinçli seçin. Yalnız-kimlik, doğru hedef kitleden geçerli her kimlik bilgisini içeri alır. Serbest ekran için makul, yazan hiçbir şey için değil.
- Müşteriye dönük her uçta kimliği enjekte edilen sahipten alın. İstek gövdesindeki bir hesap kimliği çağıranın önerisi, cevabı değil.
Açık Bir Formu Koruyun
- Jeton etiketini form öğesinin içine, captcha etiketini de formun captcha isteyebildiği yere koyun.
- Jetonu diğer kapılardan önce, demo kapısının ardından doğrulayın. Doğrulama varsayılan olarak AJAX başlığını bekler. Normal gönderim yapan form üçüncü argümanla AJAX-dışı bayrağını geçer.
- Önce sert bloğu, sonra captcha zorunluluğunu kontrol edin. Spam kapısı en sonda, alanlar okunduktan sonra gelir.
- İsteği sonda sayın ve captcha çözülüp çözülmediğine göre kalkan penceresini temizleyin ya da kaydedin.
- Formunuzu spam kapısının çağrı listesine ekleyin. Bunu atlayan bir misafir sayfasında dört bot katmanı var ama içerik kuralları yok.
Yüklenen Dosyayı Sunun
- Yardımcı üzerinden akıtın. İçerik tipi başlığını tespit edilen tipten kendiniz asla yazmayın.
- Yükleme dizinine doğrudan erişimi bir kural dosyasıyla kapatın.
- Satır içinde gösterilmesi gereken görsel dizinlerine dokunmayın.
- Üçünü de doğrulayın. Statik yol yasak döner, görsel yolu düzgün cevaplar. Controller ucu işaretlemeyi indirmeye zorlar, düz metni satır içinde gösterir.
Referans
Girdiyi Okumak
// $arg, "SOURCE/key" ya da "SOURCE/key/subkey" olur; kaynaklar: GET/ POST/ REQUEST/ FILES/ SERVER/
// $mod filtre tipidir; $special bazı tiplerin izinli karakter kümesine karakter ekler.
public static function init($arg = NULL, $mod = false, $special = false);
// Tek bir kaynağa ham erişim. İç içe anahtarlar eğik çizgiyle. Olmayan anahtar => false.
public static function GET($arg = '');
public static function POST($arg = '');
public static function REQUEST($arg = '');
public static function SERVER($arg = '');
// İzinli etiket sıyırma; "hclear" tipinin kullandığı ve doğrudan da çağrılabilen metot.
public static function html_clear($arg = NULL, $allow = '');
| Tip | Ondan geriye ne kalır | Ne için kullanılır |
|---|---|---|
password | Her şey. Argümanı dokunmadan döndüren bilinçli bir geçirgenlik | Her türden sır. Başka her tip, onları güçlü kılan karakterleri siler |
hclear | Etiketleri sıyrılmış metin | Başka bir biçimi olmayan serbest metin: bir not, bir konu satırı, bir ad |
rnumbers | Bir tam sayı | Kayıt kimlikleri, sayılar, birazdan bir sorguya koyacağınız her şey |
numbers | Rakamlar ve tire, dize olarak | Telefon numaraları ve referans kodları, baştaki sıfırın önemli olduğu yerler |
amount, rate | Ayraçlı rakamlar ve sırasıyla bir ondalık sayı | Para ve yüzdeler. Kaçak bir harfi elinde tutan düz metin filtresi asla |
email | Yalnız adres karakter kümesi | Adresler, üstüne bir de biçim kontrolü: filtre çıkarır, doğrulamaz |
ip | Adres karakterleri | Adresler ve aralıklar. Bir şey karşılaştırmadan önce gerçek bir ayrıştırma yapın |
route | Harf, rakam, tire, alt çizgi ve nokta; üst dizin geçişi önce silinir | Bir yolun ya da rota anahtarının parçası olacak her şey |
letters_numbers | Harf ve rakamlar, artı üçüncü argümanın izin verdikleri | Biçimini sizin belirlediğiniz tanımlayıcılar: veritabanı adı, modül anahtarı, slug |
domain | Harf-rakam, nokta ve tire; küçük harfe çevrilir. Doğrulama yapmaz: not a domain!!! girdisi notadomain olarak geri döner | Alan adları, üstüne gerçek bir kontrol koyarak; tıpkı adres filtresi gibi. Üçüncü argümanı da yok sayar |
Hem tipli okuyucu hem doğrudan kaynak okuyucuları, olmayan anahtarda false döner. null asla dönmezler. Null'a eşit değil koşulu her istekte doğru, arkasındaki dal koşulsuz çalışır. Bu yayına çıktı. Böyle korunan bir kip işareti her ziyarette bir simülasyon katmanı yükledi. Hiçbir şey sunucuya ulaşmadı, yanıtlar yine de başarılı göründü. Boş dizeyle karşılaştırmadan önce dizeye çevirin.
Yetkilendirme
// coremio/helpers/admin.php - oturumdaki operatör yetkiyi taşıyorsa true.
public static function isPrivilege($privileges): bool;
// coremio/api/Auth/Scope.php - kimlik bilgisinin izinleri, rotanın gereksinimine karşı.
public function __construct(array $granted);
public function allows(string $required): bool;
// coremio/api/Resources/Client/_ClientResource.php - isteğin adına hareket ettiği müşteri.
protected function owner(): int;
protected function assertOwned(mixed $row, string $message = 'Resource not found.'): array;
Grup/Eylem dizeleridir ve üç şekilde eşleşir: birebir dize, sondaki joker ile tüm grup ya da her şey anlamına gelen tek joker. Boş bir gereksinim geçer: kurulmamış bir rota kapsamı açık bir rotadır.
Açık Bir Formu Kapıya Almak
Bağımsız beş katman, bu sırayla. İlk dördü kimin sorduğunu, beşincisi ne gönderdiğini yargılar.
| Sıra | Çağrı | Hayır dediğinde |
|---|---|---|
| 1 | Validation::verify_csrf_token($token, $key, $nonAjax = false) | Başka hiçbir iş yapılmadan hemen reddedin. Anahtar, şablonun bastığıyla aynı olmalı |
| 2 | ProcessRestriction::blocked($action) | Bu adres sert blok penceresinin içinde. Hız sınırı mesajıyla cevaplayın ve durun |
| 3 | Captcha::enabled($area) ya da BotShield::triggered($action) | Captcha gerekiyor. Denemeyi kaydedin ve captcha gerekli durumuyla cevaplayın |
| 4 | Validation::spam_guard($subject, $message, $email, $phone, $ip, $domain) | Boş olmayan sonuç engellendi demektir. Dönen gerekçe loga gider, ziyaretçiye asla |
| 5 | ProcessRestriction::hit($action) artı kalkanı kaydet ya da temizle | Asıl işten sonra. Bunu atlamak, kurulmuş görünen bir sınırın neden hiç ateşlenmediğidir |
// coremio/classes/Validation.php
public static function get_csrf_token($form_index = '', $input = true);
public static function verify_csrf_token($incoming_data = '', $form_index = '', $nonAjax = false);
public static function spam_guard(string $subject = '', string $message = '', string $email = '',
string $phone = '', string $ip = '', string $domain = ''): string;
public static function password_chars_error($password = ''): string;
// coremio/helpers/processrestriction.php - null ip "çağıranınki" demek, proxy'yi bilir.
public static function blocked(string $action, ?string $ip = NULL): bool;
public static function hit(string $action, ?string $ip = NULL): bool;
public static function clear(string $action, ?string $ip = NULL): void;
// coremio/helpers/botshield.php - uyarlanabilir captcha, adres başına sayılır.
public static function active(string $action): bool;
public static function triggered(string $action, ?string $ip = NULL): bool;
public static function record(string $action, ?string $ip = NULL): void;
public static function clear(string $action, ?string $ip = NULL): void;
// coremio/helpers/captcha.php - sabit ayar ve cevap kontrolü.
public static function enabled(string $area = ''): bool;
public function check(): bool;
// coremio/classes/FraudModule.php - yalnız sipariş anında. Boş dize temiz demektir.
public static function run_checks(array $params = []): string;
Çıktı ve Dosyalar
// coremio/classes/Utility.php - her yanıtın içinden geçtiği JSON kodlayıcı.
public static function jencode($string = '', $flags = 0): string|false;
public static function jdecode($string = '', $mode = false);
// coremio/classes/Utility.php - saklı bir dosyayı tarayıcıya vermenin desteklenen tek yolu.
public static function stream_uploaded_file(string $diskPath, string $fileName, array $opt = []): void;
Örnek
Baştan sona bir misafir formu. Şablon iki etiketi gösterir, işleyici beş katmanı sırayla uygular.
<form action="https://dev.wisecp.com/tr/acme-request" method="post" data-results="#acme-out">
<input type="text" name="company">
<input type="email" name="email">
<input type="password" name="panel_password">
<textarea name="note"></textarea>
</form>
public function submit(\Operation $operation): bool
{
// Sarmalayıcı her operation'a bir Operation nesnesi verir. Tanıtım kapısıyla açın:
// bir tanıtım sisteminde herhangi bir yazma gerçekleşmeden önce fırlatır.
$operation->demo();
// 1. Jeton, diğer her kapıdan önce. AJAX olmayan gönderimde üçüncü argüman true.
if (!\Validation::verify_csrf_token((string) Filter::init('POST/token', 'hclear'), 'acme-request'))
return $operation->output(['status' => 'error', 'message' => Language::g('needs/csrf-failed')]);
// 2. Bu adres için sert engel penceresi.
if (\ProcessRestriction::blocked('acme-request'))
return $operation->output(['status' => 'error', 'message' => Language::gc('acme/too-many')]);
// 3. Captcha: operatörün sabit ayarı YA DA uyarlanabilir kalkanın devreye girmesi.
$needCaptcha = \Captcha::enabled('acme-request') || \BotShield::triggered('acme-request');
if ($needCaptcha && !(new \Captcha())->check()) {
\BotShield::record('acme-request');
return $operation->output(['status' => 'captcha_required']);
}
// Alan başına bir çağrı ve tip, alanın NE OLDUĞUNA göre seçilir.
// Sır, geçirgen filtreyi kullanır: herhangi bir metin filtresi, tam da parolayı
// güçlü kılan noktalama işaretlerini sessizce silerdi.
$company = Filter::init('POST/company', 'hclear');
$email = Filter::init('POST/email', 'email');
$note = Filter::init('POST/note', 'dtext');
$password = Filter::init('POST/panel_password', 'password');
// Filtreleme oraya ait olamayacakları çıkardı; kalanın kullanılabilir olup olmadığına doğrulama karar verir.
if (!filter_var($email, FILTER_VALIDATE_EMAIL))
throw new Exception(Language::gc('acme/email-invalid'));
if ($e = \Validation::password_chars_error($password)) throw new Exception($e);
// 4. İçerik ve gönderen kuralları; alanlar okunduktan sonra, asıl işten önce.
if (\Validation::spam_guard('', $note, $email, '', UserManager::GetIP()) !== '')
throw new Exception(Language::g('needs/spam-blocked'));
// ... asıl iş
// 5. Bu isteği sayın ve kalkan penceresini sonuçlandırın.
\ProcessRestriction::hit('acme-request');
if ($needCaptcha) \BotShield::clear('acme-request');
else \BotShield::record('acme-request');
return $operation->output(['status' => 'successful']);
}
Diğer iki ekran, kapıyı kaçınılmaz kılan biçimde.
public function GetNote(array $body = []): array
{
$id = (int) ($body['id'] ?? 0);
// Sahip, kimlik bilgisinden gelir. Gövdedeki bir hesap kimliği çağıranın ÖNERİSİDİR
// ve hesabı seçmek için asla kullanılmaz.
$row = WDB::select('*')->from('acme_notes')
->where('id', '=', $id)
->where('owner_id', '=', $this->owner())
->build('assoc');
// Olmayan da yabancı da 404 döner: hangi kimliklerin var olduğu başlı başına bilgidir.
return ['data' => $this->assertOwned($row)];
}
public function stream_note_file(int $id): void
{
$row = $this->model->note($id);
if ((int) ($row['owner_id'] ?? 0) !== $this->owner()) { http_response_code(404); exit; }
// Bu başlıkları asla tespit edilen tipten elle kurmayın: satır içinde sunulan,
// yüklenmiş bir sayfa bu köken üzerinde, onu açan sonraki operatöre karşı çalışır.
\Utility::stream_uploaded_file(ROOT_DIR . $row['path'], $row['name']);
}
# Yukarıdaki sahiplik kontrolü PHP'de yaşar; web sunucusunun ondan haberi yoktur ve
# aynı baytları doğrudan yoldan sunar. Dosyayı diskten okumak bir dosya sistemi
# işlemidir ve etkilenmez, yalnız doğrudan HTTP yolu kapanır.
# Her iki söz dizimi de yazılır; ağaçtaki her özel yükleme dizini zaten böyle yazılmıştır:
# 2.4 yönergesi tek başına, mod_authz_core olmayan bir sunucuda hata verir.
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order allow,deny
Deny from all
</IfModule>
Tuzaklar
Metin filtreleri noktalama işaretlerini çıkarır, parolayı güçlü kılan şey de noktalama. Birini bir parola alanında çalıştırın, değer sessizce kısalır. Hesap, müşterinin hiç yazmadığı bir değerle açılır, arıza sonradan giriş hatası olarak çıkar. Alan başına filtreleyin, sırlara geçirgen tipi verin.
Controller'ınızdaki her sahiplik kontrolü, dosya yolu doğrudan istenerek atlanır. Sunucu yüklenmiş bir sayfayı sayfa olarak sunar. İki yarı da gerekli. Akış yardımcısı tipi etkisiz hâle zorlar, ret kuralı statik yolu kapatır.
Boşluk testi sıfır dizesi için doğru sonuç verir. Operatörün açıkça kapattığı bir ayar, hiç yapılandırılmamış gibi okunur ve kod varsayılanına düşer. Anahtarı varsayılanla okuyup aynı ifadede tip dönüşümü yapın, sonra karşılaştırın.
Çağıranın seçtiği bir adrese giden dış istek, içerik ağının arkasındaki origin'i adres sahibine ifşa eder. Müşteriye dönük uçlar bunun yerine yüklenen baytları alır. Bir adresi ancak operatör ekranında ya da sağlayıcının imzaladığı bir kaynaktan getirin.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.