Basically my question is similar to this one:
How to secure php scripts?
with one slight difference, the other side is Shopify.
Background info:
Shopify user bought some credits (Audible style), and wants to know how many he has available. He logs in into his account, and it says there that he has X credits.
That number comes from AJAX call to my server (I have full control), where there is a simple php script which checks the value in db (which is updated using webhooks from Shopify, each of which needs to be verified so they are secure, I think).
The script uses customers ID for a look up, and that value needs to be passed to the script somehow, and that allows someone external to just keep running it until he has all IDs and corresponding credits values.
So, my questions is, how do I stop that? How do I ensure that only authenticated users can check it, and only for their IDs.
There is plenty of info on Shopify docs about securing the connections the other way, i.e. to make sure only correct scripts have access to the Shopify db, but nothing about my problem.
As far as I know I only I only have access to JS on Shopify, which creates the problem, because everything I send to my server is visible to all.
Thanks
EDIT: I just read up on CSRF. I can easily implement checks for origin and headers, but these can be faked, right?
EDIT 2: I got around this problem by using metafields. So, instead of storing all that info on my server's db, I just use Customer Metafields to store the available credits. Webhooks are secure so that's brilliant. It still doesn't solve a problem with the next stage though. Customers will still need to be able to use their credits and get digital products, which are generated by my server. So I still need to verify the requests.
EDIT 3: Comment by #deceze and answer by #Jilu got me thinking. Yes, you are correct, I need to do that, but I don't have access to back-end on Shopify, so I cannot create session. However, what I could do (if I figure out how in js) is hash it. PHP login scripts operate on password_hash. That way you do not store a password in the db. Password get's verified again hash (or whatever you call) in the db, and it's either true or false. If true, you are logged in. So I could try to generate a token using a specific string (make it very long) and user id. Send it with the request, and using password_verify or what not, check it against the users. The one that pops positive is logged in user who requested the information. That is assuming I can hide the string in the Shopify...
Step1: Do a session login system.
Step2: Before the Ajax, generate a random token in your form or request page, put it into a input display none, send it with POST.
Verify each time if the token is set and is the same that you got.
You have now verified if the user is really logged in with session.
And you checked that he is from the right page.
You create a token out of shared secret (both Shopify and server have it), and client ID.
On Shopify:
{% assign my_secret_string = "ShopifyIsAwesome!" | hmac_sha256: "secret_key" %}
My encoded string is: {{ my_secret_string }}
On server:
We gonna be checking received hash value against values in our db, so we need to hash local client IDs using the same algo and key (that probably should be done on receiving the Customer Creation webhook).
Hash IDs using: http://php.net/manual/en/function.hash-hmac.php
$hashed_id = hash_hmac('sha256', '$client_id', 'secret_key');
Compare hash using: http://php.net/manual/en/function.hash-equals.php
$check = hash_equals($hashed_id, $received_id);
Then all that's requires is to loop through the db until you find a match. There may be quicker ways of doing it.
Related
I'm attempting to create a new page every time a form is submitted. It'll be an order status page- one that'll be updated periodically. Basically, I want the user to see a form confirmation page, and I want it to be permanent link (that they can visit later).
My first thought was using variables in the URL, like so:
http://www.example.org/member.php?id=123
And then calling the id using GET
echo $_GET['id'];
http://www.example.org/member.php would be a template, just waiting for the few details which are specific to the user.
Once I have this in place, I could use a simple if statement to check their order status.
For example,
if ($id === "user_id") {
echo "Your order is: Pending";
}
However, this seems like a bad idea, just for the security aspect of it. If someone else guesses a user ID, they can view their order status. Going off of that, here's my first question.
If the user ID is long enough, is this a secure practice?
Otherwise, what are some other methods of doing this? Creating a new page every time the form is submitted feels like a bad practice- people could spam it, and there's a possibility that someone could exploit this to create malicious pages on the site.
Any suggestions? Most major retail sites have order confirmation pages (think ebay.com)- how do they do it? Also, is my suggested URL format secure?
The most ideal scenario is you force users to login prior to submitting the form then provide them with a list of their past orders of which they can check the status providing the user_id of the order matches the id from the session of the logged in user. Give each order in the list a link like yoursite.com/orders/1 then query for an order with an id of one with a user_id matching the logged in users id to ensure they're the only ones that can view it.
If you don't want to have to do any of that and just provide a permanent link to the status page I'd save a long randomly generated string against the order and provide it to the user to check in the future, e.g
yoursite.com/orders/wUk1DhfxMh if you're using a framework with some routing
or yoursite.com/orders.php?code=wUk1DhfxMh if you're not.
Query the database to select the order with the matching code, ensure you prevent MySQL injection and sanitize the $_GET input.
Are you sure you need to make a new page?
You could just have a basic "confirm" page (ex. http://yoursite.com/order/confirm) which uses PHP sessions to create a customized confirm page–
Other than that, IF you make a new page, you should use ID's in the URL and ALSO check the session id. (ex. http://yoursite.com/order/confirm/ABsisnEALnsoSK?yyyy=xxxx) and then ALSO check if the user is logged in.
Lastly, cymath has a good example of async page-creation; although it isn't exactly what you are looking for.
EDIT: It is not page creation, it's like what I said before: one page with extra parameters in the url: a permanent link, just using PHP.
I understood that you are having some doubts about how to make the algorithm of your app, here's what i thought to this case:
Insert the order at your database, get the id of the insertion and give it to the user.
Set the page where the user will check the status to receive a $_GET['id'], check (SELECT) if this id exists in the database.
(if the user exists): get the information you need from the table
you store them. (FETCH_ASSOC or FETCH_OBJECT)
(if the user don't exist):show an error.
If you are experiencing some doubts about how to code CodeSchool is offering free trial on all courses this weekend.
If the user ID is long enough, is this a secure practice?
R: To improve the security of the transactions, try to understand/learn about PDO Class, i think it will get your code to next level if you aggregate some Good Practices and Design Patterns.
For more information, visit PHP's Documentation.
I want to update a row of data in a database using Ajax and PHP; however, I'm struggling with the following issue: the field in the database to update (henceforth the id) is dependent on the page the ajax request is sent from.
I need to get this id to my PHP script that Ajax calls, however:
I don't want to set the id in a data attribute or hidden input on the page because these can both be manipulated by a malicious user.
Similarly, identifying the id using the referring URL is also prone to spoofing as $_SERVER isn't secure.
I can't set the id in a SESSION variable (or COOKIES) because the user could have multiple pages open and the SESSION would only hold the last page id that was opened.
The only solution I can think is to create a map of random tokens to id's in a table in the db and pass that in a SESSION variable (as per #3 above), then check the table for the token and grab the respective id that way. Seems somewhat convoluted though.
Are there any other options or thoughts? Thanks.
This is a problem related to OWASP Top10 A7 (Missing Function Level Access Control).
There might be no issue with putting your ID on the page so the page can send it back - you just need to validate that the actual save request is permitted for the user.
Just think, regardless of whether you put the ID on the page or not, the page does know the base url for performing the action, so they could go ahead and guess IDs anyway.
Simplify your logic. Pass some sort of indicator of what type of id is in use from the client to the server.
If you create overly complex application logic to address a security concern you will probably have more problems with your code than improvements in security.
Use SSL/HTTPS and a WAF (web application firewall - like mod_security).
Imagine the next scenario: a user wants to register to a webpage and fills a form. While he is filling the form, jQuery keeps checking through a regular expression if fields are valid, etc...
Taking the email as the primary key which the user will use after registering to login, the email field needs to be checked with Ajax to let the user know if that email is registered or not. I want to check it with Ajax to avoid sending the full form and emptying it, refreshing page, etc...
So, when the user has ended filling the email field, the Ajax request is sent to the server, something like the next link:
example.com/check.php?email=abcdefg#gmail.com
When check.php receives the email, it asks the database if it exists or not and returns a message like: User already exists if user exists or null if user does not exist.
The question is: if someone digs through my .js and finds out links similar to that, they could use that link to send a large number of requests to find out if those random emails exist. This could lead to heavy use of the database or in the worst cases even crashing and private information leaks.
Someone could do a huge for loop to check emails like:
//Getting the response of the next links
example.com/check.php?email=aaaaaaa#gmail.com // Returns null
example.com/check.php?email=aaaaaab#gmail.com // Returns null
example.com/check.php?email=aaaaaac#gmail.com // Returns null
example.com/check.php?email=aaaaaad#gmail.com // Returns User already exists
------------------------------------------------------------------------------------------------------------------------------------
Since i last accepted the answer, i kept investigating this and found the solution to avoid this behaviour. The following code is for JAVA but the logic can be applied to any other server-side language.
Before doing ANY ajax request to the server, I request a token to the server. This token looks like this fmf5p81m6e56n4va3nkfu2ns8n it is made by a simple method, it can, however, be more complex, but this is good to go.
public String getToken() throws UnsupportedEncodingException {
return new BigInteger(130, new SecureRandom()).toString(32);
}
When requesting the token, the server does not only return the token, but also a small script that in case someone uses browser to inspect element (and browser navbar) and such the script will run and the token will be cleared. Servlet returns something like this:
_html += "<head>"
+ "<script> "
+ "window.onload=function(){\n"
+ " document.body.innerHTML = \"\";\n"
+ " }"
+ "window.location.href='http://mywebsite.com' "
+ "</script>"
+ "</head>"
+ "<body>"
+ "[" + token+ "]"
+ "</body>"
+ "</html>";
First empties the body then navigates back to wherever we want. javascript/jquery will however, catch the entire content as string, then I simply extract the string between [ and ]. This token is only available for the next request, so every AJAX request will have its unique token. On the 2nd reques the token just used is deleted.
After I get the token I append it as parameter to whatever link i request, something like this:
ajaxRequestObjet = $.ajax({
url: "http://localhost:8084/mywebsite.com/servlet", //<-- local tomcat server
method: "POST",
data: "type=AJAX&page=some-article&token=fmf5p81m6e56n4va3nkfu2ns8n"
});
This method works fine against someone who inspects the website manually and try to use the links, but what about java/php/IIS servers that do this automaticly?
For this ask for header! Something like this:
boolean isAjax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
It will be true only and only if XMLHttpRequest exists....
There is one last thing to keep in mind. Make sure 'Access-Control-Allow-Origin' header is NOT present in your app to make sure that any javascript NOT in your server wont get the server resources. If this header does not exist, chrome will return this:
XMLHttpRequest cannot load http://localhost:8084/mywebsite.com/servlet. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
Java server was in tomcat and I had another apache for this tests, this is the small html present in apache which gave the error above:
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script>
ajaxRequestObjet = $.ajax({
url: "http://localhost:8084/mywebsite.com/servlet",
method: "POST",
data: "type=AJAX&page=Token"
});
ajaxRequestObjet.done(function (msg) {
alert(msg);
});
</script>
</head>
<body>
</body>
</html>
While you can not control this 100%... there are a few options..
Try using the same methods that people use with Captcha scripts..
Basically when the user loads the form / page.. You generate a random string/id in their PHP session and store it.. When they send the ajax requests, have your ajax check also append the string/id and require it before allowing a check to perform else return a header of 500 or something..
Using this approach with sessions, you could set a allowed limit of checks (say 5) and once the user has tried more than 5 checks, They are required to reload the page or perform a human check (eg Captcha).. Then it resets their count.. Even allow a total of say 30 within 1 hour / per IP or something.
Also use smart events to trigger when the ajax check is done, eg field/tab change or on a button press.. Or when a valid email is detected.. but say .com.au would trigger twice.
Basically this way, even if someone sniffed your JS files and tried to automate the email checker.. It would require them finding a way to append the string/id that you generate and also limit their amount of requests performed.
Beyond this, there is not to much more you can do easily.. But there are still a few other idea's.
Most of them would work around using a PHP session / cookie.. Say for example if they check and find 3 email addresses.. Then again you set that as a limit and force them to require a manual submission or something.
See how the above suggestion goes for you, any questions do feel free to ask. But may take me a day or two to reply as weekend.. Also research how Captcha scripts work as plenty of source code for them.. As they work on the same idea.
Time Delays will simply look bad / make your site appear slow / bug the user with waiting for a response.
You need to limit the amount of look up's per session / ip address.. Otherwise there is always a way to get past these checks.. Basically once they hit a limit.. Force the user/ip/session to wait a few minutes/hours and verify them with a Captcha script so it can not be scripted...
Javascript Security / Hiding The Source
While you can not do this truly, you can do certain things generate the JS using a PHP page with a JS header.. so <script src='myjscode.php'></script> and this allows PHP to check for a valid session.. So stops external requests to an extent.. But this is mostly useful for allowing JS to be only available behind a membership/login..
Multiple Checks / If Possible In This Case
Depending on your approach, is this for a user to check if they already have an account? If so.. you could combine the email check with something like their name/country/age/dob ... So they would need to select two or three correct matching values before being able to get a check/response from the ajax call?
Maybe not in your case, but just thought would add this as well.
The JavaScript code on your website is executed on the computer of the user, so there is no way you could stop him from digging through your code. Even if you use a code obfuscator (for example, https://www.javascriptobfuscator.com/), the hacker could debug your application and record all requests send to the server.
Everything security-relevant has to happen on the server. You could limit the amount of requests from a specific IP address.
You could protect against brute force attacks with something similar to CSRF tokens:
Assign a server-generated ID to every client session. Each request to check.php should include this ID.
check.php should reject requests that do not include an ID, or include an ID that the server did not generate (to prevent attacks with spoofed IDs). It should also rate limit on ID - if a given ID has made a request in (say) the last second, or a given ID makes more than n requests in a 10 second interval, it should return an error response. This protects against requests from a single session arriving from several IP addresses.
You should also rate limit by IP address to prevent brute-forcing by opening a large number of web application sessions.
There isn't much you can do to prevent an attacker looking up a single, or small number, of specific email addresses - it's an inherent risk with this type of validation.
One approach to resolve this problem could be this:
Suppose you have ajax request calling your server to receive a response from a particular user or client. You can have a table in your database where you provide a unique token for every user or hash value that can be checked every time user makes an ajax request to the server. If the token value matches the user request value than he is a genuine user. You can also record his number of request on the table to ensure he is making legitimate requests. I acknowledge the fact that it may slow down your app performance, but it will be safe option to consider. Note: you need to retrieve the token on your HTML page to send it with ajax.
Please comment to know more. I have been using this approach and there is no problem until now.
Example:
This type of attack can be treated the same as any other brute force attack, where the only effective solution is to use a Captcha. But of course, Captchas are a detriment to UX, so you have to consider if the additional security is worth it, especially for an attack that is very unlikely to happen anyway. That said, you may want to use a Captcha on your registration form anyway, to prevent bots from creating accounts.
This sort of attack has a huge cost for little reward for the attacker. There are billions of possible email addresses to test for. It could only be worth going to great lengths such as this, if the site in question was particularly sensitive, such as some kind of adult site, where the attacker hopes to blackmail users that he finds.
CloudFlare
Not as good as a Captcha solution but the brute force attack might be detected and prevented by CloudFlare's DDoS system. Also, CF can force Tor users to solve a Captcha before accessing your site, which would prevent an attacker from using Tor as a vehicle for the attack.
IP Rate Limiting
Rate limiting on an IP basis has problems because if an attacker decided to undertake a task as huge as this, he will likely be using a Botnet or some other system of multiple machines to launch the attack.
Consider a large organisation such as a University, where all users share the public IP. One of the users launches an attack on your site, and you block his IP, and in the processes blocking everyone else. This countermeasure could actually be used to launch a DoS attack.
Session ID/CRSF Token
Definitely not a solution because the attacker needs to simply make a request to the page first, to obtain the token. It's an additional request to make but only an inconvenience for the attacker.
First of all: I'd URL-encode the mail-address. 'example.com/check.php?email=' . urlencode(abcdefg#gmail.com)
Ad your question: when check.php is called, you can
check, if the user's session and his IP have sent a request during the last seconds
if not, write the user's session, the user's IP plus the current timestamp to a helper-table plus to a cookie and hit your DB
if yes, block the request
But I'm afraid this won't help you from fraud because everyone can check your JavaScript and if someone want's to exploit this, he will find ways.
check.php should depending on the setup either only be accessible internally, or verify from where the connection is made. Take a look at this previous question- I hope it might be what you're looking for. how to verify the requesting server in php?
You could use a CSRF token and exit early from your script if you detect that no or an invalid CSRF token. Almost (if not all) PHP frameworks come with support for this.
Also check this question from the security community: https://security.stackexchange.com/questions/23371/csrf-protection-with-custom-headers-and-without-validating-token
I've got a single page node.js FB Canvas App. Every user action triggers an AJAX POST to my node.js HTTPS server, returning the result.
Now I need a way to send a user token I create from the userId on app boot (this is an AJAX POST too, returning all content + user token). I verify that it is this user by doing a Graph API call (which is required for my boot for another reason) on the server.
Q1 So to create the token what should I use?
Q2 How to send the token with every AJAX call:
POST param?
cookie?
something else?
Q1 I assume that tokens should be unique and secure. That's generally not an easy problem. I would go with following steps:
generate a random number
try to save it into DB (or any other shared storage)
if it already exists in DB go to step 1. if not go to step 4
send the token
Ad.1. To generate a random number use crypto.randomBytes with large enough size param (256 is more then enough) in order to minimize collisions:
http://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
crypto.randomBytes should be secure. There are however some subtleties with it. For example you have to ensure that your machine has enough entropy. It can be a problem when your server does not have keyboard, mouse or mic. You can always add a hardware entropy generator:
http://en.wikipedia.org/wiki/Hardware_random_number_generator
If you don't need it to be secure then you can use crypto.pseudoRandomBytes instead.
Also it is a good idea to create and use your own algorithm (based on crypto of course). For example add a current date to that number, hash it, whatever. Just be careful not to overdo it.
Also remember about cleaning DB from old tokens.
Q2 It doesn't really matter. Whatever suits you. Probably putting it in a cookie is the easiest solution.
If you need this token for preventing from a CSRF attack, I'd recommend to send it into a POST parameter.
I am completly new to jquery and client side programing. I am trying to figure out a way of achiving this:
On the client side I have a hidden user id/profile id, which I wanna to encrypt, but while manipulating the hidden value, I have to decrypt and perform some operation on it.
I have a global array of arrays like below:
var users=[{"u_id":"1234", "u_name":"Test"},{"u_id":"12345", "u_name":"Test1"}];
this array is used by various other compoenets, e.g. when user mouse-over on the profile-id, it will get the details from the above array and display the result to him.
In short, I want to encrypt/decrypt all my global variables inside the script.
any plugin or ways to do will be highly appricated.
Encryption/decryption on the client side is entirely pointless. Encryption is used to hide something from somebody. For that you need a secret (password etc.) that you're not giving to that somebody.
If you want to encrypt and decrypt something on the client, the client will need the secret in order to do the encryption. Therefore, the client has everything it needs to decrypt any encrypted secret. That means any user has everything he needs to decrypt data and can in fact see the process happening (try breakpoints in your browser's Javascript debugger). Therefore, the entire exercise is by definition pointless. It may deter some very unskilled poker-arounder, but anyone with the skill to actually do something with the decrypted data can get it easily.
#deceze: Yes exactly, the example you have posted, lets say user 42, trying to update his age, basicaly user will see 42 as his profile id when he logsin, and then subsequent call he will make for changes he need to pass sessionkey/apikey along with profile id and data, so basically if you will suggest me how I can manage these sessionkey/apikey, so that user/hacker from outside can't missuse.. – Jayaram Pradhan 1 min ago
It's pretty simple:
Require users to be logged in via a regular login mechanism, typically involving a session id. This session identifies the user as securely as it's feasible to do anything securely over the web. Your security focus must be here.
Knowing who the user is, you can validate any and all of his actions. If the user requests to change the profile information of some user, check whether he's allowed to do that or not. No API key or anything needed. The server receives a request for change, the server knows who the requesting user is by the session, the server can decide to accept or reject the request.
In the concrete case of updating one's own profile, if the user is only allowed to update his profile and his profile alone: there needs to be only one action/URL, when a user POSTs data to that action, the server knows who the user is and updates that user's profile. The user doesn't need to submit a user id of the profile he wants to update, in fact he can't submit a user id, only his profile will ever get updated. There's no publicly accessible action that allows him to update anyone else's profile.
There is no 3.
i know no way to perform this on client side, you can serialize and obfuscate the data (like using base 64 encoding on json string).