Preventing JSON domain spoofing - php

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.

Related

Hide parameter sent via POST request

I am making a registration wherein users inputs email as their username and I have to send a random code to their email after a successful registration.
So far, what I got is:
Step 1: User registers with their email.
Step 2: API Generates a random code and inserts it to my database table (tbl_temp_otp).
Step 3: After inserting to the database, the code will be sent to the email provided in the registration.
Step 4: User gets the code, and input it into the registration form then API checks if that code exists and is still in unused
state.
So far, it works.
My issue is that, how can I send the OTP code through POST request without anybody seeing it in the network tab in google chrome?
EDIT based on the comments
I think I explained the issue in a wrong way...
What I really meant was not without anybody seeing it but without the registrant seeing it.
Since, the registrant is able to see the code inserted in the database, I'm concerned that they'll register with a non-existing email address and verify it by looking into the network tab and input the inserted OTP code and still able to completely register with the provided non-existing email address.
There is no way to hide the OTP that is sent through a POST request. The best thing to do, is to make the OTP time-based, and limit the duration that it is valid for.
Anyways, the request that is visible in the network tab is only visible for the respective user, so there is no actual security risk. Hope this clarifies your question. You can continue with the same process.
You can't.
You are sending it by email where the email client needs to read it, then it needs to be transferred to the user's browser so the browser can send it back over the network.
The user's browser and email client belong to the user. They are under the user's control. They can't be used to conceal information from the user.
But that shouldn't matter. It is a one time code designed to prove that the user is who they claim to be. It doesn't matter if the user gets their hands on it.
There's a risk that it might be intercepted between the browser and your server, but you can mitigate that by using HTTPS.
There's a risk that it might be intercepted between the email client and the browser, but there isn't anything you can do about the possibility of the user having malware on their system which is designed to share your one time password. It's a pretty remote possibility in the first place though.
There's a risk that the email will be intercepted, but nothing you can do about that unless you want to force the user to invest in setting up and learning how to use GPG or similar. (Again, it is a small risk).
Re edit:
I'm concerned that they'll register with a non-existing email address and verify it by looking into the network tab
Your one-time code should be associated with the email address that it was emailed to.
It won't show up in the network tab until the user clicks the link in that email. So there is no way for them to get the code without the email address being real.
You've already associated the code with a particular email address in your database, so they can't change their mind and use that code with a different email address.

Qualtrics 3rd Party Add-On Best Practices

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.

How to stop people from flooding PHP API with requests?

I am using a simple PHP API that takes requests and connects to a MySQL DB to store/retrieve user information. Example: Login is done using a HTTP POST to the API with username and password.
How do I prevent people from flooding my API with requests, potentially making thousands of accounts in a few seconds.
Thanks
You could serve a token generated and remembered on the server side which is rendered with your forms and validated when the form is sent back to your server. That prevents the user from just generating new post requests without actually requesting the according form from your server since they need the according token generated on your server to get it through.
Then there is the mentioned captcha which would be way too much for a login form from my point but when it comes to things like registering a new user the captcha in combination with the token sounds very good to me.
UPDATE
I Just found this article which is about floot protection of script execution in general. I think their approach is good as is the ip tracking provided you have the ability to use memcache or something similar to speed the checks up.
First, when registering a user, also save her IP address at the time of registration in the DB.
If the IP already exists within 45 minutes of previous registration, reject the request.
Another method is the Captcha, I personally prefer a different method that I found to be even more effective against robots.
Instead of asking the user to "type what they see in an image", and verify they are humans (or robots with sophisticated image processing),
Add another field (for example city), and make it hidden with javascript.
The robots would submit that field to the server, and humans would not.
Note that the robots must run the javascript in order to know what fields are hidden, and this is a time consuming process that they usually don't do.
(see turing halting problem)

Embedded iframe - Verify source/origin of GET request

I'm seeking to utilize an iframe to embed some html in customers websites that will list some information from my database using a simple GET request like so:
// customer would copy/paste this code onto their site
// value of key would be unique key for that customer
<iframe src='http://mydomain.php/api?key=1234j1lj1hj124kh' ></iframe>
Now I want to be able to verify that the request is coming from customer that owns the key, and not just anybody who copy/pasted that code onto their page.
I've done some research and found that $_SERVER['HTTP_REFERRER'] can give me this information, but with mostly mixed reviews saying it isn't always reliable (and most of the questions I came across were a couple years old).
QUESTIONS
1.) Is this method of using an iframe/GET request the standard way of achieving this functionality?
2.) Is there a standard, SECURE and RELIABLE way to verify the origin of the GET request?
Unfortunately this is not possible in a secure way.
To answer your questions: In fact this is not a standard functionality itself. I mean, there is no standard secure way of allowing content to be loaded only through iframes from allowed websites.
There are three parties in this communication:
1) Your website
2) Customer website that loads your website's data in an iframe
3) End user visiting customer website
When an end user visits customer web site, he will perform a GET request to your website through the iframe. At this connection, 2nd party above (customer website) is not involved. In this case, there is no reliable way for your website to know whether this request is coming through the iframe or not. Only favor that party 2 does here is adding HTTP_REFERER header to end-user's request. But this header cannot be trusted.
For example, if I want to abuse this and show that content on my website, I can create a proxy page on my application, where I perform a back-end call to your app (adding a valid HTTP_REFERER header) and display results back.
Personally I would never use iFrames for this functionality. I am assuming that this has to be reasonably secure, which is why only your specified customer can view it? If for whatever reason you can't use PHP to embed the content you need to display (through the use of an "included" file for example), I would instead use AJAX which would still use any PHP user verification you have in place to dynamically load content into a secure webpage.
This is because your PHP user verification will (should!) use cookie/session information to determine which customer is viewing the page and therefore decide whether the content should be delivered, since Session variables are determined by a single unique code stored client-side, which match up to as much information as you want to collect about a user server-side (Which could include the last page they visited, which is what the "HTTP_REFERRER" variable would give you, if they came from another page on your website).
'$_SERVER' variables aren't reliable because they rely on the information given to them by the web browser when the request is made, and such information can easily be forged, by most people who have a basic understanding about how headers are sent.
In summary, use a NONCE (cookied), validate IP and user agent.
Steps:
When you deliver the outer frame, generate a unique identifier
(totally random, long string) and return that in a cookie with the
HTML content.
At the same time, note the IP and the user agent string you have
sent that unique identifier to, and store in a DB with the time.
When requesting the inner frame, assuming the same domain, the
cookie will come too. (If a different domain, you'll need to attach
the unique identifier as a visible string, but that's not really of
concern, just uglier)
If the user agent or IP do not match those you stored against the
unique string, or the request is too long (i.e. after an hour, or
whatever is reasonable for your application) or the unique string is used more than once
(or whatever other restrictions you place on it) then reject the
request and invalidate (delete) the unique identifier.
Not 100% foolproof, but just combine more options to make it less and less likely to be abused.

How to restrict to one session (not login) per computer

I'm making a survey site and I'm trying my hardest to avoid user logins - I want people who answer my surveys to be anonymous members of my university, who open a link and answer the questions directly. So I'm tracking questions/surveys finished by the user through session variables
But what I don't want is one user submitting tens of questions/surveys by clearing cookies and thus effectively resetting his/her sessions. Anyone know how to deal with this?
(If anyone thinks of other ways by which people can make multiple submissions, let me know that too! I'm also looking at articles to prevent same users using different browsers)
Never trust the user. Ever.
You have a few options. All have pros/cons
By IP address - limit responses to 1 IP address per computer. This suffers from dynamics IP address problems as well as only response is allowed per computer that holds its IP for long periods
Send single use response token - Send every respondent a unique link. Each link contains a single-use token that may be redeemed to take one survey.
Collect their email address - Redact this information in the results. I'm not sure of your setup, but I thought I'd mention this in case you're just the data middle-man

Categories