Bir php sayfanın işlevselliği spam önleme

5 Cevap php

The background: Ok, I run a legacy BBG over at ninjawars.net. There is an "attack" that players can make on other players that is initialized via form post. Essentially, we can simplify the situation to pretend that there's a page, lets call it attack.php, with a giant "ATTACK" form post that submits to another php page, lets call it accept_attack.php, ve the second page performs the attack functionality, lets say killing other player 1, 2, or 3. The server runs PHP5, Postgresql, Apache

Sorunlar:

  • Ben o büyük "ATAK" düğmesine basın, ve accept_attack.php bana getirirse, o zaman arkaya tekrar üç kere saldırı, her zaman resubmitting, üç kez yenileyebilirsiniz vurabilir.
  • Ben üç İlk sayfanın sekme ve her sayfada vurmak saldırı kadar açarsanız, ben tek seferde oyuncular 1, 2, ve 3, öldürmek üç anlık saldırıları ile sona, ve ben sadece sürekli tekrarlamak için yenileyebilirsiniz.
  • Veritabanına kurtulur bir "En son saldırı" zamanlayıcı var benim girişimlerine rağmen, oyuncuların hepsi aynı almak, böylece, belki de sadece bir senkronize yeterli şekilde üç nüsha sekme tazeleyerek, etrafında çalışmak mümkün gibi görünüyor böylece zamanlayıcı (örn. 10:00:00:0000 am) ve çıkan işleme devam edin.

Çözüm gerekli:

Peki nasıl üçlü olarak tek seferde önceden olmaktan belli bir senaryonun aynı işlenmesini önlemek?

Php, Sosyal mühendislik ve / veya javascript / jQuery çözümleri (muhtemelen bu siparişle ilgili olarak) tercih.

Edit: Based on the answers, here's what I did to (potentially, before stress testing) solve it: The session answer seemed simplest/most comprehensible to implement, so I used that data store. I tested it ve it seems to work, but there may be ways around it that I'm not awvardır of.

$recent_attack = null;
$start_of_attack = microtime(true);
$attack_spacing = 0.2; // fraction of a second
if(SESSION::is_set('recent_attack')){
    $recent_attack = SESSION::get('recent_attack');
}

if($recent_attack && $recent_attack>($start_of_attack-$attack_spacing)){
    echo "<p>Even the best of ninjas cannot attack that quickly.</p>";
    echo "<a href='attack_player.php'>Return to combat</a>";
    SESSION::set('recent_attack', $start_of_attack);
    die();
} else {
    SESSION::set('recent_attack', $start_of_attack);
}

Eğer orada olduğunuzu geliştirmek için yollar veya (şeyler yankılanan mantığını iyi ayrılığının olduğunu bana bariz bir, ben bilmek isterim ötesinde. Yararlanılabilir olduğunu yolları bu satırları, toplum-wiki-ed Along .

5 Cevap

Godeke çözümlere benzer. Eğer gizli bir alana sahip bir belirteç oluşturmak olamazdı düğmesine formu "Saldırı" ve bir oturumda bu saklayabilirsiniz? Sonra kabul-attack.php sayfada kontrol olsaydı ['belirteç'] == $ _SESSION ['belirteç'] $ _POST.

böylece kabul-attack.php sayfada buna benzer bir şey olurdu

if($_POST['token'] == $_SESSION['token']){

       echo 'no cheating!';
            // or redirect to the attach page
   }else{
         $_SESSION['token'] = $_POST['token'];
         // then perform the attack
   }

WOMP Post-Redirect-al desen bazı sorunları çözecek olsa da onlar kasten gönderme işlemi oyun varsa, o zaman ben bağlantılı yazıda belirtildiği gibi (tembel karşı haricinde sorunu önlemek şüpheliyim, gönderimler önce 302 yanıt birden olacak yönlendirme henüz olmadı gibi).

Bunun yerine muhtemelen daha kolay üretilmiyor saldırı sayfadaki bazı bilgiler belirteci sokmaktadırlar. Eğer saldırıyı kabul ettiğinizde, bir veritabanı sıra tabloya saldırıyı itin. Özellikle kuyruk zaman saldırı sayfasına gönderilen bilgi belirteci saklamak ve bu belirteç zaten saldırıyı kuyruk önce kullanılmış olup olmadığını kontrol edin.

Belirteçleri basit bir kaynak, bir rasgele sayı üreteci çalışan ve bir tabloya koyarak sonuçları olacaktır. Her saldırı sayfa yük için sonraki numarayı doğru çekin ve bu sayı son zamanlarda dağıtılmış olduğunu doğrulayın. Sen saldırı sayfa yükleri belirteçleri yeniden doldurmanız ve bir sayfa "bayat" gitmeden önce mevcut olmalıdır ne kadar uzun politikasına dayalı herhangi bir "kullanılmayan" belirteçleri iptal edebilir.

Eğer "geçerli" belirteçleri bir sonlu kümesini oluşturmak bu şekilde, saldırı sayfasında (sayfa başına bir) üzerinde bu belirteçleri yayınlamak ve onlar zaten saldırı işleme sayfada kullanılan olmamıştır belirteç doğrulayın. Belirteç tüketilen olmuştur çünkü tekrar saldırıları oluşturmak için oyuncu belirteçleri geçerli olduğunu belirlemek gerekir ... aynı mesajı tekrarlayan başarısız olur. Bir BigInt ve iyi bir sözde rasgele sayı üreteci kullanmak ve arama alanı bu olası aşmak kolay olması için yapar. (Eğer bu yöntem ile başarı sağlamak için belirteç doğrulama ve güncellemeleri etrafında bir işlem gerekir, unutmayın.)

Eğer bir giriş gerektiren kullanıcı hesabı varsa, üretebilir ve (bu adımlar sarılı bir veritabanı işlemi kullanarak, tekrar) kullanıcı tablo üzerinde bu belirteçleri saklayın. Sonra her kullanıcı aynı anda tek bir geçerli belirteç olurdu ve birden fazla gönderimler benzer şekilde yakalanmış olacaktır.

Siz form mesaj için Post-Redirect-Get desen kullanarak en formda yeniden gönderimler önleyebilirsiniz.

Yerine orijinal sonrası gelen attack_accept.php dönen Özetle,, attack_accept.php için tarayıcıyı yeniden yönlendirmek için bir 302 yanıt dönmek. Kullanıcı sayfayı yeniden yükler Şimdi ne zaman, onlar sadece 302 isteğini yeniden ve hiçbir yinelenen form gönderme var.

Başka bir çözüm, sonrası verileri serialize (kendimi JSON gibi) ve daha sonra DB sonucu depolayarak, bunu karma olacaktır.

Kullanıcı aynı bilgileri sunması halinde iki hash veritabanında var olacaktır.

sağlamalarının silinen / güncelleme almak böylece

ayrıca, aynı tabloya bir zaman damgası eklemek gerekir X saat sonra

örnek php pseudo-code:

$hash = sha1(json_encode($_POST));
$results = $db->exec('SELECT timestamp FROM user_posts WHERE user_id=? AND hash=?', $user_id, $hash);

if ($results != null) {
    // check timestamp, allow if over 24 hours ago
    $ok = ($results['timestamp']+3600*24) < now();
} else {
    // no results, allow
    $ok = true;
}

if ($ok) {
    $db->exec('INSERT INTO user_posts (hash, timestamp) VALUES (?, ?)', $hash, now() );
} else {
    // show error page
    echo "your request has been denied!";
}

Not: Bu hala zaman kısa bir süre farklı POST verilerinin sunulması izin verir, ama şu da oldukça kolay kontrol etmek.

Bu çözüm aşmak imkansız olmalıdır:

1) oyuncular masa için bir 'NextAttackToken CHAR(32)' sütununu ekleyin ve bir oyuncuya her bir rasgele oluşturulmuş bir MD5 değeri vermek.

2) attack.php sayfasında, oyuncunun mevcut belirteci ile 'current_token' gizli bir alan ekleyin.

3) accept_attack.php sayfasında, oyuncu aslında saldırı için izin olup olmadığını belirlemek için aşağıdaki mantık kullanın:

// generate a new random token
$newToken = md5(microtime(true).rand());

// player is spamming if he has attacked less than 30 seconds ago
$maxTimer = date('Y-m-d H:i:s', strtotime('-30 seconds'));

// this update will only work if the player is allowed to attack
$query = "UPDATE player SET NextAttackToken = '$newToken'
               WHERE PlayerID = $_SESSION[PlayerID]
               AND PlayerLastAttack < '$maxTimer'
               AND NextAttackToken = '$_GET[current_token])'
         ";
$result = mysql_query($query);
if(mysql_affected_rows($result)) {
    echo "Player is allowed to attack\n";
}
else {
    echo "Player is spamming! Invalid token or submitted too soon.\n";
}

Mysql bir seferde sadece bir masadaki UPDATE gerçekleştirebilir ve aynı anda 100 istenmeyen istekleri olsa bile, mysql tarafından ilk UPDATE belirteci değişecek ve herhangi bir satır etkileyen diğer 99 güncelleştirmeleri durdurmak çünkü bu çözüm işe .