防止发送php页面功能的垃圾邮件

时间:2009-09-24 23:13:03

标签: php javascript concurrent-processing spam-prevention

背景: 好的,我在ninjawars.net上运行了一个遗留BBG。玩家可以对通过表格帖子初始化的其他玩家进行“攻击”。本质上,我们可以简化情况假装有一个页面,让我们称之为attack.php,用一个巨大的“ATTACK”表单发送到另一个php页面,让我们称之为accept_attack.php,第二页执行攻击功能,让我们说杀死其他玩家1,2或3。 服务器运行PHP5,Postgresql,Apache

问题:

  • 如果我点击那个大“攻击”按钮,然后它会把我带到accept_attack.php,我可以再次刷新三次,每次重新提交,再连续三次攻击。
  • 如果我打开第一页的三个标签,并在每个页面上点击攻击,我最终会发生三次瞬间攻击,一次性杀死玩家1,2和3,我可以不断刷新重复。
  • 尽管我尝试将“最近的攻击”计时器保存到数据库,但是玩家似乎能够解决它,可能只是通过以同步的方式刷新三个复制的标签,这样他们就可以全部检索相同的计时器(例如,上午10:00:00:0000),然后继续进行处理。

需要的解决方案:

那么如何防止某个脚本的同一处理一次性重复执行?

Php,社会工程和/或javascript / jQuery解决方案首选(可能大约是那个顺序)。

编辑: 基于答案,这是我做的(可能在压力测试之前)解决它: 会话答案似乎最简单/最容易理解,因此我使用了该数据存储。我测试了它似乎工作,但可能有一些我不知道的方法。

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

如果有方法可以改进那些或可以利用的方式(除了对我来说显而易见的事情之外,回应的东西不是一个很好的逻辑分离,我很想知道。沿着这些方向,社区 - 维基编

5 个答案:

答案 0 :(得分:7)

虽然womp的Post-Redirect-Get模式会解决一些问题,如果他们故意游戏提交过程,那么我怀疑它会阻止问题,除了懒惰(如链接文章中所述,302响应之前的提交由于重定向尚未发生,因此将是多个。)

相反,您可能最好在攻击页面上放置一些不易复制的信息令牌。当您接受攻击时,将攻击推送到数据库队列表中。具体来说,在排队时存储发送到攻击页面的信息令牌,并在排队攻击之前检查该令牌是否已被使用。

一个简单的令牌来源是运行随机数生成器并将它们放入表中的结果。为每个攻击页面加载提取下一个数字,并验证该数字最近是否已分发。您可以在攻击页面加载时重新填充令牌,并根据您的策略使任何“未使用的”令牌到期,以便在“过时”之前页面可用多长时间。

通过这种方式,您可以生成一组有限的“有效”令牌,在攻击页面上发布这些令牌(每页一个),并验证他们的令牌尚未在攻击处理页面上使用过。为了创建重复攻击,玩家必须确定哪些令牌有效...重复相同的帖子将失败,因为令牌已被消耗。使用BigInt和一个体面的伪随机数生成器,搜索空间使它不太容易规避。 (注意,您需要围绕令牌验证和更新的事务,以确保使用此方法成功。)

如果您有需要登录的用户帐户,则可以在用户表上生成并存储这些令牌(同样,使用围绕这些步骤的数据库事务)。然后每个用户一次只有一个有效的令牌,并且会以类似的方式捕获多个提交。

答案 1 :(得分:4)

您可以使用表单帖子的Post-Redirect-Get模式来避免大多数表单重新提交。

简而言之,不是从原始帖子返回attack_accept.php,而是返回302响应以将浏览器重定向到attack_accept.php。现在,当用户重新加载页面时,他们只是重新加载302请求,并且没有重复的表单提交。

答案 2 :(得分:3)

与Godeke的解决方案类似。难道你不能在“攻击”按钮表单上生成一个带有隐藏字段的令牌并将其存储在会话中吗?然后在accept-attack.php页面上,你将检查$ _POST ['token'] == $ _SESSION ['token']。

所以你会在accept-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
   } 

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

答案 3 :(得分:1)

另一种解决方案是序列化发布数据(我自己喜欢JSON)然后散列它,将结果存储在数据库中。

如果用户提交两次相同的信息,则数据库中将存在散列。

您还应该为同一个表添加时间戳,以便在X小时后删除/更新哈希值

示例php伪代码:

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

注意:这仍然允许在短时间内提交不同的POST数据,但这也很容易检查。

答案 4 :(得分:1)

这个解决方案应该无法规避:

1)在玩家表中添加“NextAttackToken CHAR(32)”列,并为每个玩家提供随机生成的MD5值。

2)在attack.php页面上,使用玩家的当前令牌添加隐藏字段'current_token'。

3)在accept_attack.php页面中,使用以下逻辑来确定玩家是否真的被允许攻击:

// 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一次只能在表上执行一次UPDATE,即使在同一时间有100个垃圾邮件请求,mysql的第一个UPDATE将更改令牌并停止其他99个更新影响任何行。