I am trying to make a like/unlike system, when a user clicks like on a post, his/her user id (which is stored in a session) and the post id will be stored in a database through an ajax call.
Then I thought what if some user make a html form with invisible input field (which has one of his post ids) on another domain and give its link to a user who checked remember me later or is viewing my site.
The user will click the button and The form will POST post id to my site, session contains user id and these will be stored in database.
No good solution comes to my mind. Is there any way more reliable than HTTP referrer to prevent this?
Thanks in advance
One way is to insert a secret variable into the HTML which is specific to a user's session. This can prevent cross site forgery.
In PHP you'd generate a random 'key' and store it in the session:
$_SESSION['myFormVar'] = md5(mt_rand());
Then in a form, you'd add as a hidden variable:
<input type="hidden" name="chkVar" value="<?=$_SESSION['myFormVar']?>"/>
You should submit your form via POST and preferably over HTTPS, making it harder (but not impossible) to intercept the value of chkVar.
In the code that processes your posted form, compare the posted chkVar against your session variable. In an ideal world, you'd have a unique chkVar per request, however using one which is the same for an entire session often works fine and guards against most csrf attacks.
You are talking about CSRF exploit.
This is a good security question.
It's generaly managed with a key that is only known by the server.
This key have to be used in all your forms.
Here is a little tutorial to protect against it
The only thing you should be sending is the post id, the user id should be picked up automatically in the script you are calling through AJAX. Assuming you have validated the user is logged in, you have both pieces of information without any further security risk.
Related
I currently have a file using $_GET['id'] = $userProfileNumber, to uniquely produce appropriate user data.
My question is: In order to make sure I redirect the user to their OWN profile should I be referencing "profile.php?=".$ownID in all profile links? Or is there an easier way? Thank you.
$ownID= id from email column upon login, used to represent logged in user.
You could store their ID in a session upon login and have profile.php get the ID from the session rather than the URL parameter using $_GET.
I would divide the answer into two major scenarios.
First scenario: The profile page is public. You are interested that each user would see the profile of his mate. In this case, I would totally agree with your way, as there are lots of users. Even here, on Stackoverflow, you can click on my profile below and witness the user id in the URL (with additional user name which is not required, just for prettiness' sake).
Second scenario: The profile page is private. It is merely intended for the use of the current logged in user. Here, I would go with the idea of Ben Fried - caching the repeated user data as a cookie or in local / session storage and pass it as a HTTP header / payload.
Security issues in this approach: you should consider an authentication mechanism. I.e generating an authenticated token on log in to be stored for a later use. In each request, validate this token and only then retrieve the desired and protected data.
I know two ways to pass parameters between pages:
using POST and <input type="hidden">
using $_SESSION["variable_name"]
I find the second way easier, but I wonder if it is as secure as the first one, because in most tutorials I see, they use the first way.
Is there any important reason to prefer the first way to second?
To understand the difference, let's go through in detail how each of the solutions would work and their security risks. In this example we are going to store the count of page views of the user.
$_SESSION
We start the session, increment the counter and store it in the $_SESSION array:
<?php
session_start();
if(!isset($_SESSION["pageviews"]))
{
$_SESSION["pageviews"] = 0;
}
$_SESSION["pageviews"]++;
?>
When a user visits this page for the first time, PHP will generate a random session identifier that looks like the following, and ask the browser to store this ID in a cookie:
fh4giqncq25ntgs7gjunvj6i33
On the server, it will store and remember that there is a pageviews variable with the value 1 that belongs to the session ID fh4giqncq25ntgs7gjunvj6i33.
The next time the user visits the page, his or her browser will send the previous session ID along with the request (given that the cookie hasn't expired or got deleted). PHP then recognizes this ID, and populates the $_SESSION array with pageviews = 1, then increments it: pageviews = 2
In terms of security, consider the following questions:
Is the user able to read the stored data? No – The only thing the client sees is the random session ID in the cookie; the data itself is stored on the server.
Is the user able to alter or manipulate the stored data? Again, no – If the session ID is altered in the browser, PHP will not be able to tie the browser to the stored data any more. In this worst case scenario the user will get a new session, starting with pageviews = 1.
The main security risk of sessions is session hijacking, when an attacker somehow manages to get the session ID from someone else's browser and then presents it to the server, thereby impersonating the other user. In our example this would not make much sense since we're only storing a page view count; however, most sites use sessions to keep track of which user is logged on from which browser. In that scenario, stealing someone else's session would mean getting access to their account.
Hidden field
In this case we have a form with a hidden field:
<form action="..." method="post">
<input type="hidden" name="pageviews" value="<?php print($pageviews); ?>" />
...
</form>
On the server, we retrieve the pageviews variable from $_POST and increment it:
<?php
$pageviews = #$_POST["pageviews"];
$pageviews++;
?>
So, instead of storing it on the server, we essentially send the data down to the client and expect it back in the subsequent request. Apart from the fact that it only works with POST requests, let's look at the security-related downsides of this solution:
Is the user able to read the stored data? Yes – it goes straight to the browser in the HTML code.
Is the user able to alter or manipulate the stored data? Yes – there is nothing to prevent the user from opening up the developer tools in his or her browser and changing the hidden value to whatever he or she likes. Upon submitting the form, the server gets the altered data.
The problem with <input type="hidden"> is that you just can't trust the client, so you have to verify the data you get in every request. It might be reasonable to do this in some cases, such as filling out multi-page forms, but even that can often be better solved with sessions.
Summary
Persisting data via $_SESSION is generally safer than using <input type="hidden"> because the session data is stored on the server, and thus cannot be tampered with by the client. Only a random session identifier is sent to the browser in a cookie which ties the data on the server to that particular browser.
I am learning about how to prevent CSRF using anti-CSRF tokens. Essentially, the idea is to:-
1) generate a token eg Md5 or Sha1 then store this value in a session variable:-
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
2) All forms include this token value in a POST hidden field
<input type='hidden' name='token' value='$nonce_token' />
Eg what it would look like to user in source code:-
<input type='hidden' name='token' value='9ee66e4e63a06ee4b83a3edde4ecd587' />
3) Once form sent check POST hidden field token value matches token stored in session value
if($_POST['token']==$_SESSION['token']){...ok...}
However, this process seems a little flawed since by including the token value in a hidden POST field an attack can simply just look at the website source code to see the token and then just include this in a malicious generated POST form which my application would thus succeed once received as token value sent would match the token value in my session variable, since I essentially show the token value in my hidden field to the attacker.
Thus, my question is what is the best way around this, as a few ideas I had still seem little flawed:-
1) Using _GET instead but this still has flaws like _POST
2) Changing the token value after x minutes or each request but causes usability issues when going back in browser or fail when user filling in form and token value would become outdated compared to updated session token value as hidden token value would not have updated whilst user filling in a form.
3) Try encrypting hidden POST form token value then decrypting on sending POST but encrypting/decrypting an already hashed value seems complicated especially one way encrypted has values like MD5 etc?
Any ideas would be much appreciated.
What you need to do is make the hidden field the MD5 or SHA1 hash of the session ID with a salt. That way you compare the submitted value with the hash of the session ID plus salt and if they match it is valid. If an attacker can guess the token then they have already stolen the session id and would be pointless to do anymore protecting since login has already been hijacked. It's really as simple as that. Here is some great info per OWASP on how to prevent CSRF https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
However, this process seems a little flawed since by including the token value in a hidden POST field an attack can simply just look at the website source code
No they can't.
Alice runs a website. Bob visits the website. Mallory is attacking Bob's account.
Bob gets a nonce token when he visits Alice's website.
If Mallory visited the site, Mallory would get a different nonce (because Mallory would have a different session).
If Mallory generated a form with malicious data in it (on her website) and tricked Bob into submitting it, then the nonce Mallory put in the form would not match the nonce in Bob's session and the submission would be rejected.
Let's review the attack scenario:
You have a server at example.com and you use CSRF tokens in your forms.
Each CSRF token is unique, specific to a user and only valid for some time.
A malicious third party, Eve, tricks one of your users, Alice, to come to her site, attempting to mount a CSRF attack.
If Eve simply tricks Alice into submitting a form to your server without CSRF token, your server will reject it.
If Eve also has an account on your server and tries to get any token to submit with the form, this will fail because the token is not valid for Alice.
This leaves this scenario: Using Javascript, Eve fetches a form from your server as Alice, then submits this form back, including a valid token. I.e. Eve completely impersonates Alice for the entire process of a regular form submission using Javascript. This is prevented by the Same Origin Policy. Eve's Javascript won't be able to fetch information from your server, Alice's browser will prevent this as it violates the Same Origin Policy.
That is, assuming there are no security holes in the browser which allow Eve to circumvent that policy. This also means that you need to guard against XSS, i.e. against Eve being able to inject one of her scripts into your website, so regular visitors to your site will run Eve's scripts as part of your site, from the same origin.
As a bit of self-promotion, I've just implemented a signature based CSRF token library, which you may want to look at: Kunststube\CSRFP. I'd also like to solicit peer review and criticism of it, while I'm at it.
At first, you have to keep in mind, that you cannot prevent hackers from attacking your application, only you can make things harder.
The idea is come clearly when you thinking about what is the main goal of CSRF attacks, The CSRF is an attack that tricks the victim into loading a page that contains a malicious request. It is malicious in the sense that it inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf, like change the victim's e-mail address, home address, or password, or purchase something. CSRF attacks generally target functions that cause a state change on the server but can also be used to access sensitive data.
So as above, attackers don't make attack directly to your web page, they need bridge, that's it they need a Victim, so they can use victim identity and privileges to execute actions.
when you said:
However, this process seems a little flawed since by including the token value in a
hidden POST field an attack can simply just look at the website source code
it's doesn't make sense, because attacker will not attack himself.
i hope this was help full.
I have designed a website which uses AJAX. I have a PHP page named store.php which accepts data sent by POST method and stores in the database.
How do I implement authentication into store.php? I only want a user logged into my site to use store.php.
Currently I am using htaccess to redirect the requests for store.php but I don't think that is a good solution.
Any AJAX Call to a Server Script will still include the session id in the request. If you are implementing sessions in your site, then start the session and you will be able to see session variables for the currently logged in user.
Store a token associated with the user in your database. Make sure that the token will be unique and not guessable. Also store the same token in a hidden form field so that it gets posted back to the page. Ensure on the server that the token is present in the posted form values and check that it is valid.
The security of Ajax requests is not a simple matter. They can be susceptible to man-in-the-middle attacks and replay attacks, to name a few. I'd recommend reading this book: http://www.amazon.com/Ajax-Security-Billy-Hoffman/dp/0321491939. It will give you lots of good information on the subject.
As for your question, specifically: once your PHP session has been set up, those session cookies will apply to Ajax requests as well.
What you are looking for is a persistent state for the user. The best way to implement this in PHP is to utilize sessions.
There is great documentation on it: http://www.php.net/manual/en/book.session.php
I normally just include the exact same code I use to authenticate on the rest of my site in the ajax-called page. I use SESSION to hold a sessionID, and the rest is handled in my DB. I usually end up just adding a line like this..
require_once('loginProcedures.php');
//Login Authentication takes place here using SESSION ID
if (!$me['validLogin']) die("Invalid Authentication");
//perform actions and echo your result for a valid login.
So, I have a form, to make it a little more secure and potentially help prevent CSRF attacks I want to add a random token value in a hidden field that value is also stored server side in my session data.
When should I issue a new token? Per form? Per page load where there is any form? Per session? I can render it invalid as soon as a form is successfully submitted but I'm wondering when to generate one.
I ask as if I issue it per form or per page do I not risk the chance of a duplicate token value overwriting the existing (valid) token if a user opens a separate window but submitting the first form (with the now overwritten value)?
The simplest way to prevent concurrency issues is to generate it only once per login. The chance of an attacker "guessing" your CSRF is about the same chance (or lower) as them stealing your PHPSESSID. You could also regenerate it whenever the user's access level changes, such as after they change their password or something.
If you want to be really thorough, you can generate and store an array of tokens, one for each form on the website. But if they can steal a CSRF token, they might as well have just stolen a session ID and done some real damage.
If you use one Token per session,
then you need a technique to check
duplicate submitted forms (when user
refreshes a page or click back
button).
If use it per form then if user opens
multiple pages then only the last
form will work.
I would prefer using array of
generated tokens per session, and
when a token is submitted it should be
removed. I read that this approach might be a
concern if you have high volume
traffic website.
I am not sure if you read this article before, but I think it is great resource about CSRF security:
http://shiflett.org/articles/cross-site-request-forgeries