What I've understood, CSRF-Attacks works like this:
When you check if a user is logged in to your website you check if a validate cookie is set and matched to the database.
A 3rd part sends the user to a site in your website, for example "changePassword.php"
The site "changePassword.php" will check if the user is logged in by checking the cookie is matching to the database, if it is set, the page will "change the password". It is set since you logged in to the website in another tab therefore the password will be changed.
To prevent this websites are using a Token that is generated random by the webpage, and stored as a session, then your webpage sends this token with the request, as a parameter. Since that make the website know the user really visited the website and performed the action.
The question is:
Instead of sending a token as a parameter. Can you just send the logged in cookie as a parameter?
Since cookies are webiste-specific, I can not see a way another page would be able to send this cookie as a parameter to your website.
Yes you can, assuming "logged in cookie" is a cryptographically secure session identifier.
This method of CSRF defence is known as Double Submit Cookies:
Double submitting cookies is defined as sending a random value in both
a cookie and as a request parameter, with the server verifying if the
cookie value and request value are equal.
Normally this value is separate than the session ID, but there is no reason not to use it if your session IDs are static.
In a CSRF attack, the attacker can only submit the victim's cookies to the target site using the victim's browser. There is no way of the attacker actually reading the cookie value, nor a copy of this value set as a POST parameter. So, double submit cookies is a good CSRF defence.
Note that using a GET parameter is not recommended, as this value can be leaked in the referer header.
I found out why this wont work, if you send your cookie as a parameter there will always be a way for the attacker to use that (jquery?)function, or similar. Therefore you will always have to get the token from the webpage. You could maybe print the cookie and then get it that way, but it would just be easier to make a session token as an hidden input to the website.
Related
I have built up a login on my website where I set a $_SESSION['user'] variable if the login was successful.
Now I protect all the content for logged in users by
if(!isset($_SESSION['user'])) {
header('Location: login.php');
}
This means that if there hasn't been a successful login, you directly come back to the login page.
Now my question: Is this secure? Couldn't a $_SESSION['user'] variable have been set by another website?
This is correct. This is the way generally used by most websites using PHP.
The $_SESSION is a super global variable that is only managed by your server.
What happens:
You call session_start()
A cookie named PHPSESSID (or anything that you name it in php.ini) with a cryptographically secure value will be generated.
PHP declares a variable called $_SESSION, which is internally stored with association to the generated cookie value.
Why it cannot be changed by other websites:
The value of $_SESSION is only stored internally. Not even the client knows its value. It only holds a cookie for session ID, but it doesn't even know what that session ID means, nor what other people's session ID should be.
This session ID cookie cannot be stolen or modified by other websites. By default, the cookie path is set to your own domain, and the client should only send it to you. (If the client wants to send to other websites, it's leaking its own credentials and it is none of your responsibility but the bug of the client browser)
Unless you are running other websites on the same server, this won't be a problem (under normal circumstances).
As a side note, please be reminded that you should add a return; statement after using header("Location: index.php");. This is a common source of bugs, and in this context, it may expose your server to danger, because even though your browser won't display the content after it received the Location header, your server is actually still sending the data that should be generated for the user as if he has logged in.
TL;DR: if you have a script that should not send any data if client is not logged in, not adding the return; statement after header("Location: ..."); will make the server still send the data, but normal browsers will not display it (because it redirects), but if there is someone who tries to view the data sent (using methods as simple as curl without adding the -L option) will easily see them.
What you have done so far is fine and seems not vulnerable, and no attacker setting session variable from other site doesnt effect yours, but take care how you handle session once a session is created. Also generate random session tokens on each login and also change session tokens when passwords are changed.
In general a session itself can be considered safe. The problem is that is possible to steal a session allowing a hacker to have total access to whatever is in that session.
Since PHP stores the session ID as a cookie, a hacker can steal the session simply by using XSS.
Maybe have a look here for further information: Is this a safe use of Session Variables?
You're not likely to face this problem unless there's another login page on the same server. Say, login of admin and front end users.
If you want to strengthen your session and other security components, you could refer to this:
PHP Session Security
No.
Your website creates an unique hash and file on the server machine for the session and the hash is stored in the users browser as a cookie so when it hits your webserver, it could know which file exactly to read.
If any other website sets the same key to the $_SESSION variable it will be only for its hash, which your server wont read.
Are cookies necessary to create a login page with php (that keeps you logged in across several pages), or could a session variable do the trick without use of cookies?
Answer simply is yes.
Sessions rely on a session id.
Sessions in php use a cookie to store this id, but you can change it to append the id to each url instead of saving it in cookies.
ini_set('session.use_cookies', false);
in the config variable url_rewriter.tags, you see which URLs automatically get rewritten to append this id:
"a=href,area=href,frame=src,form=,fieldset="
As Pekka mentions, jQuery requests and special JS/Ajax/jQuery calls are not getting rewritten by default and you have to append the id manually like:
<script>
$.get('/yourpage/?PHPSESSID=<?php echo session_id(); ?>');
</script>
the session name can be obtained via session_name();, default is in the config variable: session.name.
Use ini_get(); or phpinfo(); to see your configuration.
Actually if you are using sessions you can use a cookie or a special GET/POST fields to identify yourself towards the server. The server then using the user id, passed either by GET/POST or a cookie - knows which data set is connected to the current user/client at server side. This way using sessions you can store data at server side with only sending a special user id to the client.
This way you can save login data for each user, thus login functionality can be implemented using sessions in PHP.
And yes, you can solve login with no other cookie just the Session user ID, or use the POST/GET session id.
Typically sessions are more reliable when working with keeping a user logged in. Sessions are stored on the server, whereas cookies are stored client sided. So that falls down to: do you want your login dependent on something the client can control and manipulate?
I've had first hand issues with logins being hacked with cookies, so I suggest sessions.
No, you do not need cookies in order to set up a login system, sessions suffice. However, if you seek a "Remember me" option, you need cookies in order to keep the user logged in beyond the point when the user closes the browser or the session expires.
http://www.php.net/manual/en/features.sessions.php
For maintaining a session with server, you need to identify yourself (your page) to server. So that server can keep track of your page's subsequent request and maintain a session.
So, if you only have username and password option on your login page, then cookies may not be required. Refer to the following link:
Passing the Session ID from page to Server
You can have a special URL which will have identifier as part of URL, which will inform server about your subsequent request.
However, please note that using this type of special URL is not always the recommended approach. Because this is insecure than cookie based session. For example, someone may paste their own link on a chat or in an email, and other person will be entered to your site without username/password.
You can do authentication without cookies (or sessions which are a special case of cookies) but it won't be on a page. This method is called HTTP Authentication.
In our current project, we implement persistent sessions by using cookies. When the user logs in a session hash is generated and sent via a user cookie. On every page load that is checked with the corresponding session entry in the database and the user is authenticated.
In the past if i had to do csrf token check, i would normally have assined a session variable for the particualar session. And on every subsequent ajax calls i would have matched the csrf token sent as a custom header in the ajax request, with the session variable.
However for the present project i cannot find a proper way to do this. A secure hash can be created and can be added as a custom header or as a hidden input field or even as a parameter in the ajax request. But how do i validate it on the client side ?
For the time being i am doing something like str_rot13(base64_encode(some_secret_key)) and sending it as the csrf token.
Then on the server side i am doing the reverse, obtaining the secret_key and matching it with the secret key that i have configured for my app. This however doesn't seem at all secure to me. I have thought of using openssl_random_pseudo_bytes(32) , openssl_random_pseudo_bytes(16) etc.. But again, thats a hash i would be generating. How would I validate it on the server side?!
What can be some best practices that i can adopt, given the user authentication mechanism that i am following?
Good Day,
I am creating a webpage that users login called "index.html" which POSTs data to the "home.php" site when the user submits the form from "index.html." Now that I am inside the "home.php" I can retrieve the posted variables, check them against the database and authenticate the user. Once I am in the "home.php" file, I would like the user to issue a GET request to the "home.php" site to display different data. Is there a way to do that and maintain the authentication?
Right now I am getting a notice saying that the POST variables are undefined. (Obviously, since I am not posting anything)
Notice: Undefined index: pass in C:\xampp\htdocs\home.php on line 7
Thanks,
Matt
It sounds like you want to use sessions.
See: http://www.w3schools.com/php/php_sessions.asp
See: http://www.tizag.com/phpT/phpsessions.php
Once you perform your initial authentication check, which would be the form submission and account verification, you should assign the user some form of session token. This is a token that you can verify is authentic that you use for a short-hand verification for subsequent requests. You can create this token a few ways:
Create a simple table to keep track of authorized session tokens and their expiration date. This ensures that only sessions you create are allowed, tied to a single account, and have a guaranteed expiration date.
Create an encrypted token format so the session token is actually an encrypted data container which you can only read on the server side with a private, rotating key. The token would contain information about the user and expiration and eliminate the need for a server side table.
In addition to the basic information for each token it would also be good to include references to the UserAgent and IPAddress of the initial authentication request so you can ensure there is no session hijacking taking place.
Once you create your token you will want to store it in a cross-request location; which can be either a session or cookie variable. This is primarily a preference, but either way, you should ensure it is only accessible from an HTTP request and not a JS request to prevent XSS (cross site scripting). Check out these artickes on sessions and cookies:
http://www.w3schools.com/php/php_sessions.asp
http://www.w3schools.com/php/php_cookies.asp
Now that you have a token you can use from anywhere in your site you will want to make an authentication handler for each of your pages to check this token and verify it is valid. Once you confirm it is authentic you can use it to figure out which user is viewing the page and what permissions they should have.
Do this
$pass = isset($_POST['pass']) ? $_POST['pass'] : null;
You can for example do that :
echo "<form method='post' action='home.php?parameter1=".$variable1."'>";
Then you have both POST and GET variables.
Edit: But I think I misunderstood you, use SESSION variables to persist the authentication through pages.
Currently, my authentication looks something like this:
Login over SSL:
PHP checks username / password against database.
If they match, a session cookie is generated, sent and stored on the Db
(A session cookie looks like userID:IP:random_characters:timestamp)
Perform action over SSL:
PHP checks cookie against session database.
If the session matches, is under an hour old, and the user id & IP matches it is considered valid.
If the session is valid the action is performed and a new session cookie is generated.
(If the action is a logout, the session cookie is set with an expired time)
If a cookie is hijacked and the IP is spoofed within an hour of the victims previous action, the attacker will be validated and a new session will be generated. Are there other possible issues I'm overlooking?
What's considered the best practices? I'd like to tighten up the security. Thanks!
You could tie the session not only to the IP but also to the user agent. Then a not-so-smart attacker has another obstacle.. not a big one though as he probably has a way to steal more session cookies if he could steal one and then just try various useragents. However, to make it not as easy as changing the useragent and retrying with the same cookie, delete the session to require a new login (using username/password).
Last but not least, there is CSRF: If the user is logged in an external site could make him perform an action. As the user's cookie is valid this action will be performed. You can only circumvent that by passing random tokens via URL. An easy way which even allows browsing with multiple tabs would be using a (random/changing) part of the session ID for that. Random tokens only valid for a single request would be even more secure but they are extremely annoying as they prevent people from using multiple tabs (even though the app could simply store e.g. up to 10 tokens and only invalidate old tokens if there would be more than 10 - that would allow you to use not just the most recent token but also the one from the page you've been on before opening a new tab). But most likely a token valid for the whole session is sufficient; assuming you use the session id or a part of it you have a bigger problem than CSRF if someone can get the session id of other people
Regarding logout, I would not only use an expired time but also an empty value - so if something goes wrong (I've seen both servers and users with horribly incorrect (days+) clocks).