Tema Formlarını Güvenli Hale Getirme
Bir public form çerçeve tarafından korunmaz. Korumaları şablon gösterir, operation uygular.
Genel Bakış
Tema formunu koruyan bir ara katman yoktur. Çifti siz yazarsınız: view jeton ve doğrulama kutusu gösterir, operation onları sabit sırada denetler. İki yarımı form anahtarı birleştirir.
Aynı istek üzerinde beş katman durur. Dördü kimin gönderdiğini yargılar, beşincisi ne gönderildiğini.
Ön Koşullar
- Smarty ya da Twig kullanan bir tema;
veikisinde de vardır. - Gönderimi alacak bir operation (Operation'lar).
- Eşikler operatörün, coremio/configuration/options.php içinde. Bir limiti asla sabit yazmayın.
Yapı
Beş Katman
| Katman | Neyi durdurur | Eksik bırakmak neyi açar |
|---|---|---|
| CSRF | Sizin formunuzdan gelmeyen gönderimleri. | Herhangi bir sayfa, giriş yapmış bir oturumla adresinize gönderim yapabilir. |
| İşlem kısıtlama | Tek adresten gelen tekrarı; limit aşılınca süreli sert blok. | Tek bir adres uç noktayı süresiz meşgul edebilir. |
| Bot kalkanı | Otomasyonu; doğrulamayı eşik aşıldıktan sonra ister. | Otomatik bir istemci doğrulamayla hiç karşılaşmaz; form bir kâhine dönüşür. |
| Captcha (statik) | Operatörün kilitlediği bir alandaki her gönderimi. | Kapatılan alan açık kalır ve panelde ayar açık görünür. |
| Spam kapısı | Yasak kelimeleri, geçici posta kutularını, itibar listelerini. | Hiçbir içerik kuralı çalışmaz; boş kalan liste korumayı sağlam gösterir. |
Her katman tek bir çağrıdır ve kanıtı o çağrıdır.
hit() sayar.
Önce jeton, sonra sert blok, doğrulama, içerik, en sonda iş. Girdiyi erken okumak saldırganı ayrıştırıcınıza ulaştırır.
Hangi Yarım Hangi Parçanın Sahibi
| Parça | Tema tarafı | Sunucu tarafı |
|---|---|---|
| Jeton | gizli bir token input'u koyar. | Aynı anahtarla doğrulanır. |
| Doğrulama | sağlayıcının kutusunu koyar. | Captcha yardımcısı istekten okur. |
| İstek başlığı | fetch çağrısı X-Requested-With gönderir. | Üçüncü argüman aksini söylemedikçe şarttır. |
| Eşikler | Hiçbir şey; tema limit okumaz. | Yardımcılar operatörün yapılandırmasından okur. |
Adım Adım
1. Formu İşaretleyin
- Tek bir anahtar seçip her yerde kullanın: jeton, captcha alanı, kısıtlama eylemi.
- Jetonu formun içine koyun; gönderilen alanların parçası olmalıdır.
- Captcha'yı butonun yanına koyun; o alan kapalıyken hiçbir şey görünmez.
<form action="https://dev.wisecp.com/tr/iletisim" method="post" novalidate data-contact-form>
<input type="text" class="form-control" name="name" required>
<input type="email" class="form-control" name="email" required>
<textarea class="form-control" name="message" rows="6" required></textarea>
<div class="d-flex flex-wrap align-items-center gap-3 border-top pt-3 mt-4">
<button class="btn btn-primary ms-sm-auto" type="submit">Mesaj Gönder</button>
</div>
</form>
2. Handler'ı Koruyun
- İsteğe dokunmadan önce jetonu doğrulayın.
- Bu adres sert bloklu mu diye sorun ve blokluysa durun.
- Doğrulama gerekli mi karar verin: alan kilitliyse ya da kalkan devredeyse gereklidir.
- Girdileri okuyup doğrulayın, sonra spam kapısını çalıştırın.
- İşi yapın, pencereyi kapatın: kısıtlama ve kalkan sayaçlarını güncelleyin.
// 1. Jeton. İstekten hiçbir şey okunmadan önce.
if (!\Validation::verify_csrf_token((string) Filter::init("POST/token", "hclear"), "contact-form"))
throw new \Exception(Language::g("needs/csrf-failed"));
// 2. Sert blok. Bu adres limiti çoktan aştı ve süresini dolduruyor.
if (\ProcessRestriction::blocked("contact-form"))
throw new \Exception(Language::gc("website/contact/rate-limited"));
// 3. Doğrulama. Operatörün kilitlediği alan VEYA kalkanı tetiklemiş adres.
$needCaptcha = \Captcha::enabled("contact-form") || \BotShield::triggered("contact-form");
if ($needCaptcha && !(new \Captcha())->check()) {
\BotShield::record("contact-form");
return $operation->output([
"status" => "captcha_required",
"message" => Language::gc("website/contact/captcha-required"),
]);
}
// 4. İçerik ve gönderen; doğrulamadan sonra, yazmadan önce.
if (\Validation::spam_guard($full_name, $message, $email, $phone, $ip) !== '')
throw new \Exception(Language::g("needs/spam-blocked"));
// 5. ... asıl iş ...
// 6. Pencereyi kapat.
\ProcessRestriction::hit("contact-form");
if ($needCaptcha) \BotShield::clear("contact-form");
else \BotShield::record("contact-form");
3. İsteği Gönderin
- Gövdeyi formdan kurun;
FormDatajetonu ve yanıtı taşır. - AJAX başlığını gönderin, yoksa jeton doğrulaması reddeder.
- Üçüncü sonucu ele alın:
captcha_requiredbir ret değil sorudur. - Doğrulamayı finally dalında tazeleyin; yanıt tek kullanımlıktır.
var fd = new FormData(form); // jeton + captcha yanıtı formun içindeki adlandırılmış input'lardır
fetch(endpoint, {
method: 'POST',
body: fd,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(function (r) { return r.json(); })
.then(function (res) {
if (res.status === 'captcha_required') {
captchaRequired = true; // aklında tut; sonraki boş gönderim yerelde durdurulur
captchaMsg = res.message || captchaMsg;
revealCaptcha();
setAlert(captchaMsg, 'warning');
return;
}
if (res.status !== 'successful') { setAlert(res.message, 'danger'); return; }
captchaRequired = false;
showDoneStep(res);
})
.finally(function () {
if (typeof window.wcpCaptchaRefresh === 'function') window.wcpCaptchaRefresh();
});
Referans
Şablon Fonksiyonları
| Çağrı | Ne basar | Ne zaman hiçbir şey basmaz |
|---|---|---|
|
Gizli bir token input'u: anahtarın oturum sırrıyla HMAC'i. |
Hiçbir zaman. Boş anahtar da bir anahtardır; onu kullanan formlar tek jeton paylaşır. |
|
Sağlayıcının kutusu tema yuvasında; tray verildiyse tepsi içinde. |
Captcha kapalıyken ve kalkan devrede değilken. Satırı onsuz da doğru tasarlayın. |
İkisi Twig için de kaydedilir; orada seçeneklerin sırası vardır: captcha(area, tray, class, force) ve csrf(form).
Yardımcı İmzaları
Argüman sırasını dikkatle okuyun: jeton fonksiyonları anahtarı ikinci sırada alır.
// coremio/classes/Validation.php
// $input = true hazır <input type="hidden" name="token"> döner; false çıplak jetonu döner.
public static function get_csrf_token($form_index = '', $input = true);
// ANAHTAR İKİNCİ ARGÜMANDIR. $nonAjax = true, X-Requested-With şartını kaldırır.
public static function verify_csrf_token($incoming_data = '', $form_index = '', $nonAjax = false);
// '' temiz demektir. Boş olmayan dize, engellenenler listesine zaten yazılmış, operatöre dönük gerekçedir.
public static function spam_guard(string $subject = '', string $message = '', string $email = '', string $phone = '', string $ip = '', string $domain = ''): string;
// coremio/helpers/processrestriction.php
// $ip = null çağıranın adresini kendisi çözer, proxy ve CDN farkındadır. Yalnız mevcut
// ziyaretçininkinden başka bir adresi yargılarken bir değer geçin.
public static function blocked(string $action, ?string $ip = null): bool; // şu an süresini dolduruyor mu
public static function hit(string $action, ?string $ip = null): bool; // bir tane say; true = artık bloklu
public static function clear(string $action, ?string $ip = null): void; // sayacı ve bloğu sıfırla
// coremio/helpers/botshield.php
public static function active(string $action): bool; // bu eylem için kurulu mu
public static function triggered(string $action, ?string $ip = null): bool; // bu adres doğrulama istiyor mu
public static function record(string $action, ?string $ip = null): void; // doğrulamasız bir deneme say
public static function clear(string $action, ?string $ip = null): void; // bir doğrulama çözüldü
// coremio/helpers/captcha.php
public static function enabled(string $area = ''): bool; // operatör bu alanı açtı mı
public static function widget(string $area = '', array $opts = []): string; // bunu çağırır
public function check(): bool; // örnek metodu: (new Captcha())->check()
Widget Seçenekleri
Üç Yanıt Biçimi
Örnek
İletişim formu, iki yarımıyla. contact-form anahtarı iki tarafta da geçer; tek arama çifti kanıtlar.
public function submit(Operation $operation): bool
{
$operation->demo();
if (!\Validation::verify_csrf_token((string) Filter::init("POST/token", "hclear"), "contact-form"))
throw new \Exception(Language::g("needs/csrf-failed"));
if (\ProcessRestriction::blocked("contact-form"))
throw new \Exception(Language::gc("website/contact/rate-limited"));
$needCaptcha = \Captcha::enabled("contact-form") || \BotShield::triggered("contact-form");
if ($needCaptcha && !(new \Captcha())->check()) {
\BotShield::record("contact-form");
return $operation->output([
"status" => "captcha_required",
"message" => Language::gc("website/contact/captcha-required"),
]);
}
$full_name = trim((string) Filter::init("POST/name", "hclear"));
$email = trim((string) Filter::init("POST/email", "email"));
$phone = trim((string) Filter::init("POST/phone", "numbers"));
$message = trim((string) Filter::init("POST/message", "hclear"));
$ip = \UserManager::GetIP();
if (\Validation::isEmpty($full_name))
throw new \Exception(Language::gc("website/contact/error-name"));
if (\Validation::isEmpty($email) || !\Validation::isEmail($email))
throw new \Exception(Language::gc("website/contact/error-email"));
if (\Validation::isEmpty($message) || mb_strlen($message) < 5)
throw new \Exception(Language::gc("website/contact/error-message"));
// İçerik kuralları saklanmak üzere olan değerlerde koşar, ham istekte değil.
if (\Validation::spam_guard($full_name, $message, $email, $phone, $ip) !== '')
throw new \Exception(Language::g("needs/spam-blocked"));
$message_id = $this->model->add([
'full_name' => $full_name,
'email' => $email,
'phone' => $phone,
'message' => $message,
'ip' => $ip,
'cdate' => \DateManager::Now(),
]);
\ProcessRestriction::hit("contact-form");
if ($needCaptcha) \BotShield::clear("contact-form");
else \BotShield::record("contact-form");
return $operation->output([
"status" => "successful",
"message" => Language::gc("website/contact/success"),
"email" => $email,
]);
}
İki genişleme noktası: yazmadan önce veto kancası, sonrasında olay kancası.
// Boş olmayan bir dize döndüren her dinleyici gönderimi o mesajla reddeder.
foreach (\Hook::run('gate:client.contact_submit', $full_name, $email, $phone, $message, $ip) as $veto)
if (is_string($veto) && $veto !== '') throw new \Exception($veto);
// Yazmadan sonra. Dönüş değerleri yok sayılır; bu bir duyurudur, karar değil.
\Hook::run('action:client.contact_submitted', $message_id, $full_name, $email, $phone, $message, $ip);
Tuzaklar
Jeton, anahtarın HMAC'idir. View bir anahtar, handler başkasını kullanırsa ziyaretçi süresi dolmuş oturum mesajı görür. Uyarı da kayıt da yoktur.
Doğrulama, üçüncü argüman kapatmadıkça X-Requested-With ister. Eksik başlık yanlış anahtarla aynı belirtiyi verir.
Bir tepsi, ziyaretçi yazmaya başladığı için de açılabilir. Yalnız captcha_required yanıtının kurduğu bir işaret tutun.
Sonuç kabını her istekten sonra açmak yer tutucuyu sızdırır. Paneli yalnız başarı dalında açın.
Kısıtlama katmanları adresi yargılar, içeriği değil. Kusursuz jetonlu bir form bile spam kapısı çağrılana kadar yasak kelimeleri saklar.
İ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.