用PHP创建一个安全的评分系统

时间:2014-01-28 07:23:22

标签: php web

在我的网站上,我每页显示5个问题(MCQ),当用户请求新页面时,我使用此页面的分数调用脚本score_update(),然后向他显示下一页。 / p>

scoreUpdate()脚本类似于

<?php
//connect to database
//update the score
?>

问题是用户可能刷新页面并且分数可能会更新两次或刷新页面的次数,或者他可以通过查看源代码直接调用脚本。

我该如何实施这个系统?我需要一个想法。

修改

这是我的数据库架构

user

------------------------------------------
user_id  | username  |  password  | points
------------------------------------------

PS:用户可能会在将来某个时候再次尝试相同的问题。没有限制。因此,无需跟踪他所尝试的问题。只有当他尝试这个问题并将其敲错时,他才能获得分数。希望我很清楚。

7 个答案:

答案 0 :(得分:2)

我建议在数据库中保存用户的状态。您应该添加另一个表来执行此操作。

-----------------------------------
user_id  | question_id  |  answer
-----------------------------------

当用户回答问题时,您可以检查用户是否已回答此问题。 如果是这样,请更新他的答案,如果是正确的答案,请更新他的分数。如果用户已正确回答,则此方法可以假设您不会再次出现相同的问题。

如果您想多次使用问题,我建议使用其他方法。 使用2个表:

----------------------------
user_id  | questionnaire_id   
----------------------------

------------------------------------------
 questionnaire_id |  question_id | answer
------------------------------------------

每份调查问卷都是独一无二的,并且包含一些问题 - 每个问题的答案在开始时都是空的。每次用户获得新问卷时生成新问卷,并按照问卷保存答案。这样,您可以确保用户无法两次(或更多)提交相同的问卷调查结果。如果这是用户第一次提交此问卷,您可以更新分数,如果没有,则不做任何操作。

要确保用户不会手动更改其questionnaire_id,您可以将其保存在服务器上的会话中,以便用户无权访问它。

答案 1 :(得分:1)

我建议使用表单键,也称为NONCE。

这意味着每次提交时,都会生成一个新的表单密钥(NONCE)。

每个NONCE只能使用一次,NONCE必须有效才能使表单提交起作用。

大多数现代框架都标配了这样的内容。

有关该想法的更深入解释,请参阅此文章: http://net.tutsplus.com/tutorials/php/secure-your-forms-with-form-keys/

对于使用相同技术的表单上的Symfony2 CSRF保护的这一部分: http://symfony.com/doc/current/book/forms.html#csrf-protection

答案 2 :(得分:0)

对于这样的问题,有不同的可能解决方案。与访客计数器或民意调查基本相同。

如果用户已经触发了该脚本并在每次呼叫时重新识别他,那么您必须将信息存储在某处。

  • 第一个也是最好的方法是登录用户帐户并将其保存在PHP $ _SESSION中或直接保存在链接到user_id / account_id的数据库中。但是,如果您的页面现在没有登录,这对于我想的小问题来说太多了。但是,如果您已经有一个登录面板,这是迄今为止最好的解决方案。
  • 另一种方法是保存一个cookie,如果用户之前不同意这一点并且可以删除cookie,那么最近在某些国家可能会出现法律问题,因此很容易操作。
  • 您还可以保存用户的IP地址:难以操作(需要重新启动互联网等等,并且没有人会这么做十几次来伪造您的分数计数器)但是如果多人共享同一个互联网连接只有其中一个能得到一分。

所有这些都有不同的优点和缺点。根据你的偏执程度,如果你想让作弊/滥用变得更难,你也可以将它们中的多个组合起来,但这个决定取决于你。

答案 3 :(得分:0)

检查$_SERVER['HTTP_REFERER']值。

  • 如果页面相同:重新加载。 (什么都不做)
  • 如果是上一个:更新数据库
  • 如果是另一个域:非法访问(重定向到第一个问题)

答案 4 :(得分:0)

考虑以下设置;

users
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| user_id    | smallint(5) | NO   | PRI | NULL    | auto_increment |
| username   | varchar(10) | NO   |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
 ... You'll have more columns, but you get the idea

-

questions
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| qid      | smallint(5)  | NO   | PRI | NULL    | auto_increment |
| question | varchar(10)  | NO   |     | NULL    |                |
| votes    | smallint(5)  | NO   |     | 0       |                |
+----------+--------------+------+-----+---------+----------------+

-

votes
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| qid    | smallint(5) | NO   |     | NULL    |       |
| user_id| smallint(5) | NO   |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+

在此设置中,我是用户ID 1并投票给问题ID

当用户投票时,他们的投票将置于votes

之内
INSERT INTO `votes` (`qid`,`user_id`) VALUES (1, 1);

要检查他们已经投票,只需这样做;

SELECT `user_id` FROM `votes` WHERE (`user_id`=1) AND (`qid`=1);

如果该查询返回任何行,我们知道用户已经投票,我们不应该处理重复投票。

当然,这只会限制我们进行一种投票 - 积极或消极 - 无论您决定追踪哪一种。我们可以调整votes来存储投票类型;

ALTER TABLE votes ADD type ENUM('up', 'down') NOT NULL DEFAULT 'up';

这将使我们的表结构如下;

+---------+-------------------+------+-----+---------+-------+
| Field   | Type              | Null | Key | Default | Extra |
+---------+-------------------+------+-----+---------+-------+
| qid     | smallint(5)       | NO   |     | NULL    |       |
| user_id | smallint(5)       | NO   |     | NULL    |       |
| type    | enum('up','down') | NO   |     | up      |       |
+---------+-------------------+------+-----+---------+-------+

再次,调整查询查询;

SELECT `user_id` FROM `votes` WHERE (`user_id`=1) AND (`qid`=1) AND (`type`='up');

答案 5 :(得分:0)

我看到的最简单的系统是基于跟踪给定quizz的整个生命周期。

如果您存储与用户和此特定quizz相关联的“当前问题编号”,则可以轻松过滤掉重复的回复:

update_score ($question_number, $choice)
  if current question for this quizz and user is not set to $question_number
    ignore request
  else
    set choice for this specific question and update score
    increment current question (possibly reaching the end of the quizz)

当回答最后一个问题时,会显示/记录最终分数,并将“当前问题”重置为0.

如果用户想重试测试,当前问题设置为1,整个过程重新开始。

如果用户想要取消当前测试并重新启动,他/她可以返回quizz起始页面。

因此,任何尝试提交同一问题的第二个答案都会失败(无论是意外刷新还是恶意尝试),直到问答完成后你可以从问题1开始。

答案 6 :(得分:0)

您可以使用切换会话变量方法(将其命名为标记),这是最简单的,并且具有针对重复请求的良好安全级别。

创建一个名为updateScore.php的脚本。当用户登录时设置flag=1,这意味着当下一个请求进行更新时,请在updateScore.php和脚本结尾处理它制作flag=0。再次显示下一页时,请flag=1。这样您可以替换值并在脚本中设置最大更新限制,例如,在您的情况下,您有5个问题,因此您可以将其设置为50(+10每题)。您可以采用更复杂的标志值来减少猜测机会。

相关问题