I'm developing a javascript memory card game. At the end the user has the ability to submit his score to a database. The best 4 scores will win a prize.
At the end of the game a lightbox opens containing a form, where the user inputs his firstname, lastname and an email address. The score will then be posted to an api via ajax. Also the score is included in the post request.
An experienced user can easily lookup the score submit request in the browser dev console and could easily send a fake score via curl or an own build ajax request.
What would be a good way to prevent cheating here?
Generate a UUID for the user's session and store it in their cookies or JS local storage or something. Then save the score tied to this UUID in the db. Then save the name and email also tied to the UUID.
Basically don't calculate/send the raw score on the client side. Send the raw answers from the game to the server and let the server calculate and save the score.
you may try something like this
if(#isset($_SERVER['HTTP_REFERER']) &&
$_SERVER['HTTP_REFERER']=="http://yourdomain/ajaxurl")
{
//Request identified as ajax request
}
This will make sure the referring site is yours. I've heard this can be hacked by modifying headers... but it is only a game, and if they can get passed XSS and checking referring http and can generate their own headers, they deserve to win....
Related
Here's the scenario. A customer already has an eCommerce site where they are collecting shipping address info and credit card data. However, they sign up with a SaaS service that allows them to easily change their credit card form to also collect fullnames and emails (not credit card info) into a marketing system for other purposes. So, they paste a jQuery snippet into their page from the SaaS service and add some "data-" attributes to their form and form field tags so that the SaaS service knows what to intercept and where to what SaaS account to post the data.
Okay, but then a security problem occurs. Let's say we have two separate customers in that SaaS system. One is named Jack and owns jack.com, and his account ID is 100001. The other is Nancy, nancy.com, and 100002. Jack could add the snippet into his form and add some "data-" attributes, but then screw up and set his account ID in one of those data- attributes to 100002 instead of 100001. This would mean that Nancy would suddenly see Jack's data in her account. Not good! The fix, of course, is to have some setting in Jack's SaaS account so that he only accepts data from jack.com, and Nancy only accepts data from nancy.com.
But then a potential exploit happens. All a hacker has to do is create a spoof page on his own server where he forms similar JSON and, via a /etc/hosts file change on his workstation, makes it appear that he's jack.com. He could then fire in thousands upon thousands of bogus marketing form information into Jack's account because the SaaS service thinks it's coming from jack.com.
Is there anything I can do in the jQuery code, or the PHP code used by the SaaS service, to ensure that a hacker can't spoof like that and that only Jack's real customer data gets sent?
ANSWERED QUESTIONS
Q1. "How do you identify Jack and Nancy? By domain?"
A1. Jack has his domain jack.com. Nancy has her domain nancy.com. Each use this marketing SaaS service, but each are also co-opting an eCommerce form they already had before they signed up for the SaaS service. They were told in the SaaS docs, "Just drop the scraper.js in your form page and add these data- tags to your form tag and html input, textarea, and select tags so that the scraper.js can intercept those form submits temporarily, glean the marketing data (full name and email, let's say) from that form, and then let that form submit on its workflow that you already had. The data- attributes would identify which account is to be used, as well as which marketing campaign and sub-campaign where this data should be stored at the SaaS service." However, in the jQuery of scraper.js, it was going to pass in the JSON the location.href property so that it knows what domain was used -- jack.com or nancy.com, in this particular instance. Trouble is -- the location.href can be spoofed by a hacker who sets up an /etc/hosts file entry of 127.0.0.1 for jack.com on his workstation and runs a copy of the same JSON code.
Q2. "What if you could use a callback mechanism from SaaS.com to jack.com? So, one of your data- attributes would specify the callback function to receive data back from SaaS.com, and then only save data when it gets the right response?"
A2. Now that's an interesting take. Yeah, so I could like drop an extra PHP page on jack.com that emits "OK". When SaaS.com receives a JSON data post from jack.com, it sends a request to that second PHP callback page on jack.com with file_get_contents() to ensure it not only gets the OK response back, but also gets a match on IP address and SSL certificate data. If the two are different, then most likely the request was a bogus hacker request and the transaction can be security logged and rejected. (I can do the IP address verification easily, but am not certain how to verify the same two SSL certificates in PHP, if that's even allowed or possible.) Of course, IP addresses can be spoofed.
Another layer of security on this is that this second PHP page can use a public/private key exchange communication check between SaaS.com and jack.com instead of simply emitting "OK".
Q3. "Why would you want to intercept payment information and send that off through Javascript?"
A3. Absolutely not. Never specified sending payment information in this question. Was saying something like Full Name and Email, instead. Yes, SSL communication would need to be used as well in order to send that data securely. And, we'd have to use JSONP to get around the CORS problem.
Q4. "Wouldn't that require jack.com to have everything saved in a database for verification? If so, why bother with the SaaS app?"
A4. Nope. Not at all. Check out the answer A2. With that mechanism, the SaaS app receives the data, but doesn't trust it until 2 things happen:
It calls back to jack.com to a second page, and ensures that the response it gets back has the same IP address as the one that sent the form data in the first place.
It does a public/private key exchange check (which the hacker can't spoof obviously unless they have server access) in that second page to ensure that IP spoofing wasn't occurring.
Q5. Wait a minute. A2 and A4 have a problem. The IP address of the initial sending request will be from the user's workstation, not the server. So, you can't validate IP that way. You'll have to use another mechanism to validate that someone completed the form at jack.com and that it wasn't from a hacker spoofing jack.com.
A5. You're absolutely right. Forgot about that I guess because I'm slightly distracted on another project. I'll have to give this some more thought.
TL;DR: If you're using a purely client-side integration (just javascript), there's no way to completely secure the request.
Accidentally/intentionally sending data to the wrong client
You can mitigate this by using non-sequential, random UUIDs as account IDs. For example, if an account ID looks like 100001, then someone might try using the account ID 100002; however, if the account ID looks like c3f80e491d44cd91664a0459a0777ed01, it's statistically unlikely that someone will be able to send data to an unknown account.
Intentional spam/fictional submissions
This is a problem with any form that stores data on the internet; I'm not aware of any way around this without help from some server side code.
You can generate a one-time token to be included with the json payload - this can be something like a dated JWT token secured via HMAC, or a set message encrypted with a shared secret key which would then be de-duped by the SAAS server.
If you're going to start involve server-side programming, then this additional negotiation process becomes a bit irrelevant - it's far easier to the just give the e-commerce site an API key, and let them post the customer information over when they receive an order.
I am working with Qualtrics, which is just a web-based survey engine. The request is that, once users finish the survey, they will receive a small prize in the form of a gift code. Since I can't host the prize code data on the Qualtrics platform, my idea was to store the prize codes on a remote server. When the user finishes the survey they will be directed to my server (https), to a PHP script that will give out the prizes. On the surface this is possible, because as one piece of customization they allow to re-direct to a URL upon completion of the survey.
The problems that I am faced with, regarding my PHP script that gives out the prizes are as follows:
1) Make sure visitors have COME FROM the survey and have actually
finished the survey.
2) Only give out 1 prize per participant per survey.
It is difficult to address #1 because it seems like after the survey is complete, you just get a basic re-direct to my site. It would be possible to add GET data to the URL, its very easily readable and doesn't offer security. Even encrypting the GET data doesn't seem feasible because a hacker could just copy the data string once they see it.
The idea I had to address #2 was to check the user's IP address using PHP, and store the address in my DB after a 1-way encryption. This way the IP would be unknown, but I could check for duplicates, only allowing 1 prize per IP.
So the question is, is it even possible to do what I am trying to do with just a simple URL re-direct? Is my idea for addressing #2 acceptable?
Thanks.
There are probably many ways this could be handled. Two that come to mind:
1) At the end of survey, Qualtrics creates an embedded data field called ResponseID which is in the Qualtrics data. You can include the ResponseID in the redirect then have your php script call the Qualtrics REST API, and try to retrieve the response with that ResponseID to make sure it exists.
2) Just before the end of survey, you could do a web service call to a script that creates a unique id, store the id on the server side, and return it to the survey as an embedded data field. Then pass the unique id in your redirect and make sure it matches a unique id you stored.
EDIT based on comment below:
You can add custom parameters to your redirect by piping in parameter values like this:
http://mywebsite.com/myscript.php?rid=${e://Field/ResponseID}
If you are able generate special request parameter for redirect url and make it for every user unique, then you could invent some algorithm, and encrypt user ID with it and pass this ID as parameter in redirect url.
So what I am trying to accomplish is this; I have people cold-calling potential customers. When the caller gets a potential client on the phone and has them interested, they transfer the client to a sales agent to seal the deal.
During the initial conversation, the cold-caller collects some info in a form I have online such as name, phone number, address, etc. I need the cold caller to be able to transfer those values to the sales agent when they pass the phone call over.
The cold-caller and the sales agent are in two different buildings across town from each other, and the sales agent uses an online form to collect data as well. Capturing the values is not the problem, it is the pass from one agent to another that I cannot figure out.
I thought maybe the cold-caller could post the form to a Google spreadsheet, but I do not know how to get those values to populate on the second form when the call is passed. I need the transfer to happen within a few seconds, so sending a URL with the captured values in an email won't work due to email being unreliable in the speed department.
Anyone have any thoughts on this? I can use HTML, jQuery, or whatever. I would need help if it has to be done in AJAX....
Thanks!
Ok.
This is customer side:
Customer put data into form, send this data to server.
Server must save this data for agent.
This is agent side:
Browser send check requests (maybe automatically, maybe agent need click some button) for new data from customers.
If exists new data you should place this data into form on agent page.
$('#first-form').submit(function() {
$.post('/another-destination', $(this).serialize(), function(r) {
alert('Data sended to another destination!');
});
return true;
});
Looks like you are wanting to use HTML5 and Javascript WebSockets to be able to push data from one browser to another.
Ajax requires polling, meaning a query has to ask the server for new information.
This can be accomplished by saving and comparing the last updated timestamps from a session variable.
Websockets push data to a connected peer when it is updated allowing for data to automatically populate as it is submitted, much like a real-time chat system.
If you are stuck on PHP you should take a look at Ratchet since it is fairly straightforward and minimalistic to implement in comparison to many of the alternatives.
http://socketo.me/
Chat demo using ratchet http://socketo.me/demo
Add in a RDBMS to create, read, and update the data and you have a powerful real-time application.
What I'm doing at the moment is creating a row in a table for each Facebook request that gets sent. Then, every time a user opens up the FB friend picker to send a request I make a call to a php file that requests information from that table and returns with a list of the FB user ids of all the people they have sent a request to in the last 24 hours. I do this for each type of request the user can send.
The issue I'm having at the moment is that if the user initiates a request, sends them off to a number of people, and then immediately opens the FB friend picker again the previous request action's records have not yet all been added to our internal table. Thus, the players, if they go fast enough, can send multiple requests to the same FB friends.
Is there a way, on the FB side, to limit this behavior Or is this entirely up to the developer to constrain? For either case, is there a recommended method by which I should achieve this behavior? Thank you.
Update
It occurred to me that our DB is keeping multiple requests from being entered on a per-user-per-24-hour period. What I do now is simply allow the second request to be made on the FB side and when the code attempts and fails to enter the second row into our DB it makes a FB Graph call that uses the app's auth_token to delete the request from Facebook itself. This means that it will show up for a moment on the receiving player's request page on Facebook but since it isn't linked with a row in the internal DB the user won't receive any reward for clicking-thru anyway.
Thanks for the suggestions, though, everybody. #Gil Birman I went ahead and accepted your answer since it's perfectly valid, even if it's not what I ultimately used to fix the problem. Thanks!
There are several ways to solve the lag problem you mentioned, one way would be to disable your send request button via javascript as soon as it is pressed. In your javascript code, instead of immediately displaying the send request dialog via FB.UI, send a json request to your server. Only when the server responds should you display the fb send request dialog. Also, the response that the server sends should include the list of friends to exclude. After the fb request is sent your javascript code should send one more json request to the server to indicate what rows in the database needs to be updated. Only when the server responds this second time should you finally re-enable your send request button.
However, there is no way to actually limit the # number of requests that your user can send. That is, no matter how well you design your javascript/php code, your user could still theoretically invoke the request dialog via the Javascript console to completely bypass your attempts to secure the app.
i am using Ajax to send JSON data over to my server for a php script to parse.
for example this is the JSON which will be outputted:
http://myserver.com/parse.php?user=123&point=100&callback......
and the "parse.php" script will GET the points in the URL and give it to that user which the user id is stored in the user=123.
However , if a person directly use "http://myserver.com/parse.php?user=123&point=100&callback......" on their browser without letting my javascript to initiate it , they can cheat their points , which they can set to any amount they like in the URL.
So how can i protect/authenticate the JSON request??
If you're passing the user points via an AJAX request or something similar client-side, then you simply cannot protect your application against cheating. Your user is telling the script he/she scored 100 points, how can you be sure that's the fair number? Pass over the initial data you're using to compute your score, like valid answers for the questions or whatever it is you're measuring.
If you have a login mechanism in your application, you could check for the valid credentials on the server-side when executing your script, without actually passing the user identifier via GET/POST.
Lastly, in your method you can check for the AJAX header and the referer ($_SERVER['HTTP_X_REQUESTED_WITH'] and $_SERVER['HTTP_REFERER']) to make sure the request comes from your JS code, but this is really just a minor tweak which is easy to compromize. Also, not every browser passes the referer header (depends on privacy settings), so further problems may arise.
Require Users to be logged in to invoke parse.php. If the request doesn't supply a valid session id, refuse to take action.
Don't put any game data and logic in client side.
Never trust the client. You always must calculate server-side.
More infos (don't rely on link title, there is a lot infos in answers) : https://gamedev.stackexchange.com/questions/3695/how-do-you-prevent-your-javascript-html5-web-game-from-being-copied-or-altered