2014-01-28 48 views
0

在我的網站上,每頁顯示5個問題(MCQ),當用戶請求新頁面時,我用此頁面的分數調用腳本score_update(),然後向他展示下一個頁。用PHP製作一個安全的評分系統

scoreUpdate()腳本是一樣的東西

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

的問題是,用戶可以刷新頁面,分數可更新兩次或次數,他刷新頁面或者他可以直接調用腳本通過查看源代碼。

我該如何實現這個系統?我需要一個想法。

編輯

這裏是我的數據庫架構

user

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

PS:用戶可以在將來的某個時候再次嘗試同樣的問題。它沒有限制。所以不需要跟蹤他嘗試的問題。只有在他嘗試提出問題並將其打上正確答案時,他才能獲得分數。希望我清楚。

+2

這是一個很好的做法,使用PRG(Post Redirect Get)來(主要)避免這個問題。 –

+0

@Jack:在我的情況下,PRG沒有用,因爲我在後臺更新分數,並且無法將他重定向到頁面。我想實現類似於堆棧溢出的評分系統 –

+0

@InsaneCoder在我的回答中閱讀我的最新評論,如果您不需要一段時間就可以創建一個包含questionID和accountID的表,並使用與accountID和currentQuestionID匹配的WHERE子句獲取結果。如果返回的行大於0,則不能再添加分數,因爲用戶已經評分。 – Steini

回答

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

+0

:我認爲這可能是解決方案。我正在研究它 –

+0

在嘗試其他方法將我的頭撞向牆壁之後,我終於使用了你的建議,並且它是一個神話般的方式,節省了我的服務器的苦差事。 +1 –

+0

高興地幫助:) – edmondscommerce

0

這樣的問題有不同的可能的解決方案。這與訪客櫃檯或民意調查基本相同。

至少您必須將您的信息存儲在某處,如果用戶已經觸發了該腳本並在每次呼叫時重新識別他。

  • 第一個也是最好的方法是登錄並保存在PHP $ _SESSION中的用戶帳戶,或直接在鏈接到user_id/account_id的數據庫中。但是,如果您的網頁現在沒有登錄權限,那麼對於我認爲較小的問題來說,這太過分了。但是如果你已經有一個登錄面板,這是迄今爲止最好的解決方案。
  • 另一種方法是保存一個cookie,如果用戶不同意這一點,那麼最近可能會在某些國家/地區存在法律問題,並且cookie可能會被刪除,因此很容易操作。
  • 你也可以保存用戶IP地址:難以操作(需要重新啓動互聯網等,沒有人會這樣做,十幾次假你的分數計數器),但如果多人共享相同的互聯網連接只有其中之一可以達到一個分數。

它們都有不同的優缺點。根據你是多麼偏執的人,如果你想作弊/濫用,但你的決定取決於你,你也可以結合其中的多個人。

+0

:是的,我有一個登錄系統,第一個方法似乎最好。當用戶登錄時,我從數據庫中選擇最後一個分數並將分數值添加到它並將總數顯示給用戶,以便他看到最新的分數。但是,我何時會將分數更新到數據庫?因爲用戶可能沒有註銷。 –

+0

你應該立即保存它。這完全是數據庫結構的問題。例如:表格分數(account_id,timestamp,question_id,分數);可能看起來像這樣。然後,您只需從SELECT SELECT * FROM'score' WHERE'question_id' = $ currentQuestionID,'account_id' = $ user_id AND'timestamp' + 86400 Steini

+0

:我的數據庫結構就像** user **(id,name,points),問題從表**中提取**(id,title,option_a,option_b,option_c,option_d,answer) –

0

檢查$_SERVER['HTTP_REFERER']的值。

  • 如果這是相同的頁面:被重新加載。(什麼都不做)
  • 如果是以前的:更新數據庫
  • 如果它是另一個域:非法訪問(重定向到的第一個問題)
+0

:我認爲要處理最後一種情況,我還可以使用另一種方法,如在更新後將每個頁面的temp_score設置爲0,以便即使從另一個域添加0,但是您的方法會保存該查詢.Nice和聰明的回答。希望它沒有任何陷阱。在爲我獎勵你之前等待其他人 –

+0

:還有一件事,因爲分數是在客戶端使用JavaScript計算頁面的。有可能用戶可能會改變代碼或手動通過分數。我可以使用壓縮器對代碼進行混淆,但有沒有更好的解決方案或建議。 –

+0

使用AJAX和$ _SESSION的組合我想會做的。通過AJAX將得分發送給服務器,然後計算總得分並將其存儲到會話中。 –

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 1

當用戶投票,其投票置於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'); 
+0

@h:我認爲你讀錯了這個問題。即使我看到你所建議的投票系統,在我的情況下,它限制用戶在他的一生中只嘗試一次這個問題。但正如我所引用的,他可能會嘗試這個問題任何時候,但我唯一想要的是停止更新頁面刷新。 –

+0

啊我不好,道歉! –

2

我會建議保存用戶的數據庫狀態。您應該添加另一個表格才能這樣做。

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

當用戶回答問題時,您可以檢查用戶是否已經回答了此問題。 如果是這樣,更新他的答案,如果這是正確的答案更新他的分數。此方法的工作原理是,如果用戶已經正確回答了問題,則不會再出現相同的問題。

如果您想多次使用問題,我推薦另一種方法。 使用2個表:

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

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

每份問卷都是獨一無二的,包含了一些問題 - 回答每一個問題是空的開始。每次用戶獲得新調查問卷時都會生成新調查問卷,並根據調查問卷保存答案。通過這種方式,您可以確保用戶不能兩次(或更多)提交相同的調查問卷結果。如果這是用戶第一次提交此調查問卷,則可以更新分數,如果不是,則不做任何事情。

爲了確保用戶不會手動更改他的questionnaire_id,可以將其保存在服務器上的會話中,以便用戶無法訪問它。

+0

對於他的問題,簡單的1表解決方案就足夠了......只需使用「INSERT OR UPDATE ANSWER =:answer WHERE user_id =:user AND answer =:answer」 - 如果你想要更多瑣碎的,你可以用'score'替換'answer'字段,如果問題是對還是錯,只保存1或0 - 如果你想向用戶顯示分數,只需查詢sum('score')其中user_id =:用戶 – Falco

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開始頁面來完成。

因此,任何試圖提交第二個答案,同樣的問題會失敗(無論是意外刷新或惡意的企圖),直到QUIZZ結束,你就可以開始回來的問題1.

0

您可以使用一個切換會話變量方法(將其命名爲標誌),這是最簡單並且對重複請求具有良好安全級別的方法。

建立一個叫做updateScore.php腳本。當用戶登錄設置flag=1,這意味着當一個請求出現了更新用,處理它updateScore.php,並在部份腳本的最後使flag=0。當下一個頁面再次出現時,請使flag=1。這樣,您可以交替使用這些值,並在您的腳本中設置最大更新限制,例如,對於您的情況,您有5個問題,因此可以將其設置爲50(每個問題+10)。你可以採取更復雜的標誌值來減少猜測機會。

+0

您的解決方案僅適用於無意中嘗試提交表單兩次的無辜用戶。用戶可以一次提交相同的表單,然後等待服務器上的會話過期,然後再次提交相同的表單。在這個解決方案中,有更多的場景可以讓用戶濫用網站並欺騙評分系統。我會建議構建一個強大的系統,而不是使用此解決方案。 –