Pergunta

O fundo: Ok, eu corro um BBG legado sobre a ninjawars.net. Há um "ataque" que os jogadores podem fazer em outros jogadores que é inicializado através do formulário post. Essencialmente, podemos simplificar a situação para fingir que há uma página, vamos chamá-lo attack.php, com um gigante "ataque" formulário de postagem que submete a outra página php, vamos chamá-lo accept_attack.php, ea segunda realiza página do ataque funcionalidade, permite dizer que matar outro jogador 1, 2 ou 3. O servidor executa PHP5, PostgreSQL, Apache

Os problemas:

  • Se eu bater esse grande botão "ataque", e isso me traz à accept_attack.php, então eu posso bater refresh três vezes, submeter novamente a cada vez, para atacar novamente três vezes seguidas.
  • Se eu abrir três guias da primeira página, e ataque hit em cada página, eu acabar com três ataques instantânea que os jogadores matam 1, 2 e 3 de uma só vez, e posso apenas atualizar continuamente a repetir.
  • Apesar de minhas tentativas de ter um temporizador "mais recente ataque" que fica guardado no banco de dados, os jogadores parecem ser capazes de trabalhar em torno dele, talvez apenas atualizando três guias copiado de uma forma bastante sincronizada, de modo que todos possam recuperar a mesma temporizador (por exemplo, 10: 00: 00: 0000 am). e, assim, prosseguir com o processamento resultante

A solução necessário:

Então, como posso evitar que o mesmo processamento de um determinado roteiro de ser pré-formada de uma só vez em triplicado?

PHP, engenharia social, e JavaScript soluções / ou / jQuery preferido (provavelmente em cerca de ordem).

Edit: Com base nas respostas, aqui está o que eu fiz para (potencialmente, antes do teste de stress) resolvê-lo: A resposta sessão parecia mais simples / mais compreensível para implementar, então eu usei que armazenam dados. Eu testei-o e parece trabalho, mas pode haver maneiras em torno dele que eu não estou ciente de.

$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);
}

Se lá está maneiras de melhorar isso ou formas que que é explorável (além do óbvio para mim que ecoando material não é uma boa separação da lógica, eu adoraria saber. Ao longo destas linhas, comunitária wiki-ed.

Foi útil?

Solução

Assim como as soluções da Godeke. você não poderia gerar um token com um campo oculto no formulário botão "Attack" e loja que em uma sessão? Em seguida, na página de aceitar-attack.php você iria verificar se o $ _POST [ 'simbólico'] == $ _SESSION [ 'simbólico'].

então você teria algo parecido com isso na página de aceitar-attack.php

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

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

Outras dicas

Apesar do womp Post-Redirect-Get padrão irá resolver alguns problemas, se eles são jogos deliberadamente o processo de submissão, então eu duvido que ele vai evitar o problema, exceto contra o preguiçoso (como observado no artigo ligado, apresentações anteriores da resposta 302 será múltiplo como o redirecionamento ainda não aconteceu).

Em vez disso você está provavelmente melhor colocar alguma informação de prova na página de ataque que não é facilmente reproduzida. Quando você aceita o ataque, empurre o ataque em uma tabela de fila de banco de dados. Especificamente, armazenar as informações enviadas token para a página ataque quando filas de espera e verificar para ver se aquele sinal já foi usado antes de enfileirar o ataque.

Uma simples fonte de fichas seriam os resultados da execução de um gerador de números aleatórios e colocá-los em uma tabela. Puxe o próximo número para cada carregamento da página ataque e verificar se esse número havia sido distribuído recentemente. Você pode repovoar as fichas na página cargas de ataque e expirar qualquer "não utilizado" tokens com base em sua política por quanto tempo uma página deve estar disponível antes de ir "obsoleto".

Desta forma, você gerar um conjunto finito de símbolos "válido", você publicar esses símbolos na página de ataque (um por página) e de verificar que eles símbolo ainda não tenha sido usado na página de processamento de ataque. Para criar ataques repetir o jogador teria que determinar o que os tokens são válidos ... repetindo o mesmo posto irá falhar porque o token foi consumido. Use um BigInt e um gerador de números pseudo-aleatórios decente e o espaço de busca faz com que seja improvável que seja fácil de contornar. (Nota, você vai precisar de uma transação em torno da validação do token e atualizações para garantir o sucesso com este método.)

Se você tiver contas de usuários que exigem um login, você pode gerar e armazenar esses tokens na mesa do usuário (mais uma vez, usando uma transação base de dados envolvendo essas etapas). Em seguida, cada usuário teria um único token válido em um momento e submissões múltiplas seria pego em uma maneira similar.

Você pode evitar a maioria dos formulários re-submissões usando a Post-Redirect-get padrão para mensagens do formulário.

Em poucas palavras, ao invés de retornar attack_accept.php do post original, retornar uma resposta 302 para redirecionar o navegador para attack_accept.php. Agora, quando recarrega usuário da página, eles simplesmente recarregar o pedido 302 e não há submissão do formulário duplicado.

Outra solução, seria para serializar os dados post (i como JSON eu) e, em seguida, hash, armazenando o resultado no DB.

Se os submete usuário a mesma informação duas vezes o hash vai existir no banco de dados.

Você também deve adicionar um timestamp para a mesma mesa, de modo que os hashes pode ficar excluído / atualizado após X horas

Exemplo de php pseudo-código:

$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!";
}

Nota:. Isso ainda vai permitir a apresentação de diferentes dados POST em um curto período de tempo, mas isso é também muito fácil de verificar

Esta solução deve ser incontornável:

1) Adicionar uma coluna 'NextAttackToken CHAR(32)' à sua mesa os jogadores, e dar a cada um jogador um valor MD5 gerado aleatoriamente.

2) Na página attack.php, adicionar um campo oculto 'current_token' com token atual do jogador.

3) Na página accept_attack.php, use a seguinte lógica para determinar se o jogador é realmente permitido atacar:

// 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";
}

Esta solução funciona porque mysql só pode executar uma UPDATE na tabela de cada vez, e mesmo se existem 100 pedidos de spam exatamente ao mesmo tempo, a primeira atualização por mysql vai mudar o token e parar os outros 99 atualização de afetar nenhuma linha.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top