如何防止人们发送ajax表格

时间:2014-05-19 22:33:07

标签: javascript php jquery ajax

我的问题是我有一个类似于Stack Overflow的投票系统。我的问题是,一个人可以垃圾邮件投票按钮,这会使其出现故障,并使其提交的次数超出预期。例如,如果一个帖子上有10个投票,我可以反复点击投票按钮,它会增加两到三个投票而不是一个。同样,我可以使用向下投票按钮执行此操作。我该如何防止这种情况?

的index.php:

<?php
session_start();
require('db.php');
$pid = 2;
$uid = $_SESSION['id'];
$sql = mysqli_query($con, "SELECT * FROM posts WHERE pid = '$pid'"); //check to see how many likes the post has
$r = mysqli_fetch_assoc($sql);
$body = $r['body'];
$likes = $r['likes'];
$sql2 = mysqli_query($con, "SELECT * FROM likes WHERE pid = '$pid' AND uid = '$uid'");      //check to see if user has voted
$n = mysqli_num_rows($sql2);
if ($n == 0) {
    //user hasn't liked or down vote anything yet
    $liked = "no";
} else {
    if ($n > 1) {
        //like scammed
        echo "<script>alert('Stop spamming for votes. You are banned for spam.')</script>";
        exit("You have been banned for spam");
        //This isn't fool proof though, and I don't want to ban people for this. It would be best if I could just prevent the vote scam in the first place
    }
$r = mysqli_fetch_assoc($sql2);
$type = $r['like_type'];
if ($type == '0') {
    $liked = "liked";
} else {
    $liked = "disliked";
}
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <script src="//code.jquery.com/jquery-latest.min.js"></script>
    <style>
        .selected {
            color: red;
        }
    </style>
</head>
<body>
<div class="post">
    <p><?php echo $body; ?></p>
</div>
<div class="likes">
    <a href="javascript:;" class="upvote <?php if ($liked == 'liked') {echo "selected";} ?>" id='up-<?php echo $pid; ?>' onclick="vote('up', '<?php echo $pid; ?>', '<?php echo $uid; ?>', 'up-<?php echo $pid; ?>', 'votes-<?php echo $pid; ?>')">Upvote</a>
    <span id="votes-<?php echo $pid; ?>"><?php echo $likes; ?></span>
    <a href="javascript:;" class="downvote <?php if ($liked == 'disliked') {echo "selected";} ?>" id='down-<?php echo $pid; ?>' onclick="vote('down', '<?php echo $pid; ?>', '<?php echo $uid; ?>', 'down-<?php echo $pid; ?>', 'votes-<?php echo $pid; ?>')">Downvote</a>
</div>
</body>

Javascript投票()功能

function vote(type, pid, uid, id, voteId) {
var vote = $('#'+ id);
if (vote.hasClass('selected')) {
    //user voted for this
    $.post("vote.php", {pid: pid, uid: uid, type: type, vote: 'reset'}, function(d) {
        if (d == '0' || d == '1') {
            vote.removeClass('selected');
            var votes = $('#' + voteId);
            var num = votes.text();
            if (d == '1') {
                votes.text(++num);  
            } else {
                votes.text(--num);
            }
        } else {
            alert('An error occurred')
        }
    });
} else {
    var upVoteId = $('#up-' + pid);
    var downVoteId = $('#down-' + pid);
    if (upVoteId.hasClass('selected') || downVoteId.hasClass('selected')) {
        //user wants to switch votes
        $.post('vote.php', {pid: pid, uid: uid, type: type, vote: 'switch'}, function(data) {
            var votes = $('#' + voteId);
            var num = votes.text();
            if (data == '1') {
                //downvote successful
                votes.text(parseInt(num) - 2);
                vote.addClass('selected');
                upVoteId.removeClass('selected');
            }
            if (data == '0') {
                //upvote successful
                votes.text(parseInt(num) + 2);
                vote.addClass('selected');
                downVoteId.removeClass('selected');
            }
            if (d == 'error') {
                alert('error');
            }
        });
    } else {
        $.post('test2.php', {type: type, pid: pid, uid: uid}, function(d) {
            if (d == "1") {
                //everything good
                $('#' + type + '-<?php echo $pid; ?>').addClass('selected');
                var votes = $("#" + voteId).text();
                if (type == 'down') {
                    //downvote
                    votes = --votes;
                    $('#' + voteId).text(votes);
                } else {
                    votes = ++votes;
                    $('#' + voteId).text(votes);
                }
            } else {
                alert('failed');
            }
        });
    }
}
}
}

Vote.php

<?php
session_start();
require('db.php');

if (!isset($_SESSION['id'], $_SESSION['un'])) {
    //not logged in
    header('Location: index.php');
    exit;
} else {
    if (!isset($_POST['uid'], $_POST['pid'], $_POST['type'], $_POST['vote'])) {
        //form not submitted
        header('Location: home.php');
        exit;
    } else {
        $uid = (int)$_SESSION['id'];
        $pid = (int)$_POST['pid'];
        $type = preg_replace('#[^a-z]#', '', $_POST['type']);
        $vote = preg_replace('#[^a-z]#', '',$_POST['vote']); //vote type

        if ($vote == 'reset') {
            //initiate vote reset
            if ($type == 'down') {
                //downvote
                $sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '1' AND pid = '$pid' AND uid = '$uid'"); //delete the downvote
                $sql2 = mysqli_query($con, "UPDATE posts SET likes = likes + 1 WHERE pid = '$pid'");
                if ($sql) {
                    echo "1"; // 1
                    exit;
                } else {
                    echo "error";
                    exit;
                }
            } else {
                //upvote
                $sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '0' AND pid = '$pid' AND uid = '$uid'"); //delete upvote
                $sql2 = mysqli_query($con, "UPDATE posts SET likes = likes - 1 WHERE pid = '$pid'");
                if ($sql) {
                    echo "0"; // 0
                    exit;
                } else {
                    echo "error";
                    exit;
                }
            }
        }

        if ($vote == 'switch') {
            //user wanted to switch vote
            if ($type == 'down') {
                //user had voted up but wants to vote down now
                $sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '0' AND pid = '$pid' AND uid = '$uid'"); //delete the previous vote
                $sql2 = mysqli_query($con, "INSERT INTO likes (pid, uid, like_type, date_liked) VALUES ('$pid', '$uid', '1', now())"); //insert new vote
                $sql3 = mysqli_query($con, "UPDATE posts SET likes = likes - 2 WHERE pid = '$pid'");
                if ($sql AND $sql2 AND $sql3) {
                    //all three queries were successful
                    echo "1";
                    exit;
                } else {
                    echo "error";
                    exit;
                }
            } else {
                //user had voted down but wants to vote up now
                $sql = mysqli_query($con, "DELETE FROM likes WHERE like_type = '1' AND pid = '$pid' AND uid = '$uid'") or die(mysqli_error($con)); //delete the previous vote
                $sql2 = mysqli_query($con, "INSERT INTO likes (pid, uid, like_type, date_liked) VALUES ('$pid', '$uid', '0', now())"); //insert new vote
                $sql3 = mysqli_query($con, "UPDATE posts SET likes = likes + 2 WHERE pid = '$pid'");
                if ($sql AND $sql2 AND $sql3) {
                    //all three queries were successful
                    echo "0";
                    exit;
                } else {
                    echo "error";
                    exit;
                }
            }
        }
    }
}

Test2.php

<?php
require('db.php');

$pid = $_POST['pid'];
$uid = $_POST['uid'];
$type = $_POST['type'];

if ($type == "down") {
    //downvote
    $type = 1;
    $sql = mysqli_query($con, "INSERT INTO likes (uid, pid, like_type, date_liked) VALUES ('$uid', '$pid', '$type', now())");
    $sql2 = mysqli_query($con, "UPDATE posts SET likes = likes - 1 WHERE pid = '$pid'");
    if ($sql) {
        echo '1';
        exit;
    }
} else {
    //upvote
    $type = 0;
    $sql = mysqli_query($con, "INSERT INTO likes (uid, pid, like_type, date_liked) VALUES ('$uid', '$pid', '$type', now())");
    $sql2 = mysqli_query($con, "UPDATE posts SET likes = likes + 1 WHERE pid = '$pid'");
    if ($sql) {
        echo '1';
        exit;
    }
}

这些是我目前使用的网页。我计划将test2.php移至vote.php

在我的数据库中,我有两个表,一个用于存储所有帖子详细信息,包括投票数。第二个表是存储谁投票支持哪个帖子,以及是否是一个upvote或down。

如果我可以提高我的系统效率,请给我提示或建议。

4 个答案:

答案 0 :(得分:1)

Quick SQL hack:在pid,uid上创建一个唯一索引,以便用户只能在帖子上投票一次。

ex:ALTER TABLE vote ADD UNIQUE INDEX pid_uid (pid, uid);

快速JS黑客攻击:在提交时设置一个变量,直到响应时才清除;如果设置了变量,则不提交表单。因此,垃圾邮件点击将无效,因为第一次点击后的每次点击都将被忽略。

例如:

var submitting = false;
function submit_form()
{
    if (!submitting)
    {
        submitting = true;

        // example; insert actual arguments for it to work
        $.post(
            url,
            postData,
            function (data, textStatus)
            {
                submitting = false;
                // handle data here
            },
            "json"
        );
    }
}

答案 1 :(得分:0)

嗯,还有很多改进要做。 首先,您正在运行SQL注入查询。将其移至准备好的陈述。

然后,您可以在插入之前检查用户是否已经为该类型投票,因为您已经拥有uidpidlike_type。这是服务器端。

客户端JavaScript可以禁用点击按钮以防止双击。这将阻止用户向服务器发送许多请求。

这里的目标是让服务器,PHP,如果用户已经投票给该帖子,则处理验证,因为在这种情况下客户端很容易被操纵。

不要忘记,将这些SQL查询移动到安全的地方。

答案 2 :(得分:0)

最终,您只能希望使用服务器端验证来控制多个投票。

Stack Overflow要求用户登录已知帐户才能投票,这使得多次投票更加困难(当然也不是不可能)。

如果您不需要,最佳解决方案取决于您的具体要求。

一个简单的,仅限客户端的解决方案是设置一个cookie,表明用户已投票。如果设置了该cookie,则禁用相应的UI元素。清除cookie或使用InPrivate样式浏览的人很容易绕过它。有人也可以编写自己的忽略cookie的客户端。也许这足以满足您的要求。

天真的服务器端解决方案是每个IP地址只允许一次投票。我不推荐这个,但包括它让你理解为什么。不幸的是,单个用户可以拥有多个IP地址(只需在移动设备上行驶,查看您获得的IP数量),或者单个IP可以代表多个物理计算机(代理服务器)。

可靠的服务器端解决方案将组合IP地址,用户代理和设备的各个方面以生成设备指纹。这是一个复杂的解决方案,超出了大多数网站的需求(但如果您需要,有一些公司提供设备指纹识别)。查看https://panopticlick.eff.org/

<强>摘要

如果您可以要求用户登录投票(如StackOverflow),那通常是最佳解决方案。

如果您不能要求,请使用设备指纹识别,如果它在您的预算中,否则依赖cookie。如果你做了后者,可能仍然值得记录选民的IP地址和用户代理,这样你就可以留意明显的作弊行为。

答案 3 :(得分:0)

一旦他们投票,我会考虑将选民的IP地址存储在mysql表中作为INT。

之后,或者只是向他们展示统计数据,或者通过向上/向下投票为他们提供撤消投票的选项。

查看PHP函数ip2long: http://www.php.net/manual/en/function.ip2long.php

使用它将IP地址转换为INT格式并将其存储在mysql中以引用。

其他资源: http://www.php.net/manual/en/function.long2ip.php http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton