I'm making a quiz application, where 4 users join a lobby (finished), and then the leader starts the quiz.
When the quiz is started, questions get randomly selected. Users can answer them, and click submit. Each question is timed, meaning user only has 10 seconds to answer the question.
This is all done through AJAX, since I want the website to be real-time. How exactly would I prevent cheating? User could manually edit the JS file, etc.
I was thinking of getting the exact time when the question gets loaded + the answer time. And if it's not in the span of 10 seconds, he's cheating. Would that work, or is there a better and easier way to do this?
Thank you.
Edit
I thought AntiForgeryToken was right solution to solve your problem. I read a lot of articles to make sure my old answer is correct.
1- Hiding or Encrypting the javascript source code
2- How to Disable HTML view source or Encrypt Html elements programatically?
3- How To Prove That Client Side Javascript Is Secure?
4- ASP.NET MVC - does AntiForgeryToken prevent the user from changing posted form values?
I came to the conclusion:
AntiForgeryToken prevents a malicious site to trick a user to a form that looks the same as the original and post it to the original site. It does not prevent the scenario you are describing.
There's really no way to do this completely client-side. If the person has a valid auth cookie, they can craft any sort of request they want regardless of the code on the page and send it to your server.
You can use HtmlHelper.AntiForgeryToken with salt value.
To use these helpers to protect a particular form, put an Html.AntiForgeryToken() into the BeginForm, e.g.,
#using (Html.BeginForm("Users", "SubmitQuiz"))
{
#Html.AntiForgeryToken()
<!-- rest of form goes here -->
}
This will output something like the following:
<form action="/Users/SubmitQuiz" method="post">
<input name="__RequestVerificationToken" type="hidden" value="saTFWpkKN0BYazFtN6c4YbZAmsEwG0srqlUqqloi/fVgeV2ciIFVmelvzwRZpArs" />
<!-- rest of form goes here -->
</form>
Next, to validate an incoming form post, add the [ValidateAntiForgeryToken] filter to your target action method. For example,
[ValidateAntiForgeryToken]
public ViewResult SubmitQuiz()
{
// ... etc
}
Salt is just an arbitrary string. A different salt value means a
different anti-forgery token will be generated. This means that even
if an attacker manages to get hold of a valid token somehow, they
can’t reuse it in other parts of the application where a different
salt value is required.
You can create different salts for different users like this.
Edit
AntiForgeryToken() prevents tampering with the code using inspection tools like this:
In Client side
1- A new random anti-XSRF token will be generated.
2- An anti-XSRF field token is generated using the security token from step (1).
In Server side (Validating the tokens)
1- The incoming session token and field token are read and the anti-XSRF token extracted from each. The anti-XSRF tokens must be identical per step (2 client side) in the generation routine.
2- If validation succeeds, the request is allowed to proceed. If validation fails, the framework will throw an HttpAntiForgeryException.
For more information this, Please see this article.
Conclusion: Since there's no way to prevent anything on the client side, the only solution that actually sounds okay is having server check everything.
A GET request, which requests the question and logs the time. After that, a JS timer which automatically submits the question if the countdown is finished. The user can also manually submit the answer (obviously). POST of the answer, and the server logs the time of it, compares it to the initial time of the GET request. If it's longer than 10 seconds, it throws and error, and the answer is not counted.
Thank you everyone.
Related
On my website, I have created a comment section for blog posts. Users can write comments, click a button, and an AJAX request will be sent to PHP containing the data in JSON. The PHP will process & validate the data and then insert it into the database. On success, all comments are retrieved from the database and, using JQuery, all of the page's comments are reloaded.
The problem is that anyone can come along and, using their browser's console, forge an AJAX request, fill in their own JSON, and send the request to PHP. If done like this, all that happens is my client-side validation is useless. The server-side validation would still work. However, there's a bigger problem.
for(var i = 0; i < 10000; i++) {
//ajax request
}
The user can very easily insert thousands and thousands of records into my database instantly.
Does anybody have any suggestions on how I can prevent something like this from happening? It must involve creating something on the server side that can't be guessed by a user, and somehow checking against that during an AJAX request. I'm just not sure how exactly to go about this.
Thanks for the help.
The only way for you to be safe in this respect is to add a CAPTCHA.
This will prevent mass / automated posts. One possible library to use is Securimage . It is simple to use and integrate. You can have it running in 10 minutes with your AJAX stuff.
Relying on other means such as cookies or client side validation of some sort is risky, if possible at all. For instnace KA_lin 's solution can be compromised in 5 minutes: a malicious user can be sending forged cookies that will always have a page count of 0 and thus will always be allowed to post. Or even worse, he could create a small program that will post to your page without sending any cookie at all. The above code will create a new cookie and accept his post, every time ...
I would add a session variable containing the number of posts a user makes, given many pages you can form something like $SESSION['page_id_total_nr_comments'] and track this number, add a config variable that let`s the use to add a maximum of X comments per article for example:
function canUserAddComment($pageId){
$maxAllowed =......;
if(!isset($SESSION[$pageId+'_nr_comments'])){
$SESSION[$pageId+'_nr_comments'] = 0;
}
if($SESSION[$pageId+'_nr_comments']< $maxAllowed){
$SESSION[$pageId+'_nr_comments']++;
return true;
}
return false;
}
OR
On save get the number of comments a use already made on the article and decide if ha can make another(still with a config variable)
I am integrating chat application in my website. Chat boxes are managed using a javascript library. They are HTML components to which I append a textarea where the user enters the message to be sent (Facebook-like style of chat).
The messages exchanged are persisted in a MySQL databases. Server side language is PHP under Symfony2 framework.
How can I secure my database in this case? Normally, to prevent CSRF vulnerability, I generate a CSRF token when the form containing textarea is rendered. Symfony2 helps to easily validate against the token. But in my situation, textarea is used without a form. I can wrap my textarea inside a form with hidden input token field, but I don't think it will be appropriate to render a new form (with new token) whenever a new message need to be sent.
Could you please share with me your insights regarding such issue? Are there any tricks for chat applications to prevent CSRF attacks? Any advice is highly appreciated.
One method would be to use a counter approach.
So on first form render, you include a token. Each response includes that token and an incrementing counter (starting at a random number). After the first request, you know on the backend which counter position is valid, and can invalidate the token if you find an invalid counter position. Then after 100 (or 1000 or whatever) requests, force a token refresh (which does a normal request to get a new token).
So the convo would look like:
Client Server
getToken --------------------->
generate new token
<-----------------------token // x4asf3%2f
generateCounter() // 2332523
sendText(text)
{text: text, token: token, counter: counter+1}
-------------------------->
if (!isValidToken(token)) error()
saveCounter(token, counter)
doSomethingWithText(text)
sendText(text)
-------------------------->
if (!isValidToken(token)) error()
if (counter != getCounter(token)+1) error()
doSomethingWithText(text)
This is similar to the syn-ack process that TCP uses.
The important features of an anti-CSRF token are that:
Each user must have a different token. (A user might also have several tokens for different purposes, but no two users may share a token.)
It must not be practical for a malicious user to obtain (or construct) a valid token for another user.
Each request (that could have unwanted effects if done maliciously) must include a token.
The server must not accept the request unless it contains a valid token for the user performing the request.
Requirements 1 and 2 are typically implemented either using a cryptographic message authentication code to generate the tokens, or simply by assigning a randomly generated token to each user (or session) and storing a copy of it on the server.
For traditional HTML forms, one way to implement requirement 3 is to include the token as a hidden field in the form. However, that's not the only way to do this. In particular, if you're submitting requests by Ajax, all you need is to have your Ajax code somehow obtain the token (e.g. from a hidden field, or an HTML data attribute, or simply from a piece of JS code somewhere on the page) and include it in the request.
For example, if you look at the HTML source of this Stack Overflow page you're reading right now, you'll find a piece of JavaScript that looks something like this (un-minified):
<script>
StackExchange.init({
"locale": "en",
// ...snip....
"site": {
"name": "Stack Overflow",
// ...snip....
},
"user": {
"fkey": "0123456789abcdef0123456789abcdef",
// ...snip....
}
});
// ...snip...
</script>
The user.fkey value (which, for obvious reasons, I've changed above) is a random 128-bit anti-CSRF token that is stored in the StackExchange JS object, and is included in every Ajax request made by the scripts on the page.
Upon completion of an ajax call I would like to direct the user to an html page, but at the same time passing a hidden variable (this variable contains sensitive information and should not show up in the URL).
How can I accomplish this?
window.location.href = 'userpage.html?id=14253';
But with the id remaining invisible? Can I POST the id somehow while sending the user to userpage.html?
You should not be checking user credentials on the client side of your website. Regardless of how the ID is being passed to your script, it can be replicated without you being able to check if the request is valid.
To start being even remotely secure with what information is granted to a user, you need to be checking it via the server side. With every request, ensure the user is authenticated to view such data.
If I were you, I would look into using PHP sessions as the first line of defense for checking if a user is authenticated. Doing so will at least keep the information about a user out of a replicable space that can be viewed by the user.
Look up 'php session login tutorial' on Google and you will find plenty of simple tutorials which should get you on the right track.
Example Tutorial
No matter what, the information you pass along is insecure. You can submit a post request using XMLHttpRequest objects if you'd like (or use a framework/library to make AJAX calls) -- but the user could still spoof the data and get different results.
ID enforcement should be done in the backend. Does the requested ID match the ID of the user signed in? No? Don't show it. etc etc.
I allow my users to favorite an update or a forum topic.
So when a user tries to favorite one of these i will send via Ajax 2 things, the item_id(update or topic) as id(ex. 1321313213) and its type("update" or "topic") as string.
However lets say someones tries to favorite an update with the id untouched but the type is changed to "topic"(via firebug or whatever else)...
This should not procceed since this combination is not correct... how can i assure that the item_id being sent is an update or a topic since this ID might co-exist in both tables???
Current solution:
Create a hidden input element and add as value 5 random characters (a-zA-Z0-9) and md5 type name(update or topic)
like:
$random_str = $this->my_model->generateRandomString(5);
<input type="hidden" value="<?php echo $random_str.md5("update"); ?>" id="type" />
so when i try to validate the data to check if it is an update or topic i split the type on the first 5 characters and later and check if the later characters are md5 hashed are update or topic and continue validation
I would like some help in case this can be altered as well...
Your server side script (PHP) must always assume it's getting bogus data. Never rely solely on javascript to handle any sanitization / verification.
If your javascript can determine if the job should be "update" or "topic", I'm sure your PHP can do that as well. Probably using a few more DB queries or some such, but that's the price you've got to pay.
Your are looking at the problem from the wrong perspective. Especially from You server side (PHP) code.
Your server gets data. It gets data which is something like that: user (from session), id and type. Your server needs to ask a question: is it valid data? If it is -- save it to DB; If it is not -- do not save it to DB. It is that simple.
You can look from this perspective: Your client side code is just one way to communicate with Your server. Another way is using web browser + firebug. It is perfectly valid usage of Your server side application. And Your PHP code should not care how request reaches it.
So if Your current code does not allow You in Your PHP code feel comfortable and freely decide if is it update or topic creation than Your need to change Your server side code (and perhaps DB schema) as well.
Your current solution is not good, because if I know how to use firebug I would probably find out that "9d9b68ac2b1de18d3712096354b3c3a5" means "topic" and "3ac340832f29c11538fbe2d6f75e8bcc" means "update".
I think Your are trying to invent Your own CSRF protection. So go on Internet and read about it.
Through several threads I can see that the use of the MVC antiforgery token is overkill on areas of a site where a user is not authenticated.
I have an application that posts some information to mysite.com from site1, site2, site3, etc. Each site has a unique identifier that gets sent in the POST request through an asynchronous Javascript POST. The Javascript that is executed on site1-3, is generated on mysite.com, then returned to the sites with some Javascript variables populated.
So the lifecycle is as follows:
A page on site1 has a Javascript reference to mysite.com.
That link reference is to a controller route that generates Javascript to return to site1.
The end of the JS that is returned contains a POST request that goes back to mysite.com containing Url, browser, etc., details for the visitor of the page on site1.
I can read in the POST parameters just fine in the accepting controller from the JS POST request, however, what I wanted to know is if there is any point in adding an antiforgery token to the parameter list.
If so, I would have to generate it on the initial request, and pass it back as a JS variable in the JS returned to site1, then pass it back along with the form POST in the second request.
Since any processing on mysite.com will only occur if a valid account is found, is there any point in going through this?
If so, how would I generate the antiforgery token on at the controller level?
I would say that it depends on the sensitivity of the data that is being posted. If another user could cause harm (or annoyance) by crafting forged requests and submitting them, then I would say that it would be appropriate. It sounds like you're just collecting some usage information so that's not likely to be the case.
A one-time, random nonce might be a better solution. That would make it difficult to forge a request and prevent erroneous multiple submits, say from the user using a cached copy. Generate a random value (a GUID might work) on mysite.com, inserting it in the database and marking it as unused. Send it back with the POST. Check whether it has been used or not. If not used, then mark it used and perform your logging action. If it has been used already, discard the request as a duplicate submission.
Note that you wouldn't need a POST for this, a simple GET with URL parameters would be sufficient since the nonce will prevent it from being accidentally repeated.