I am working with CORS on a website.
Code sample:
header("Access-Control-Allow-Origin: *");
session_start();
$session_id = session_id();
This code is working fine, but it's returning a new session id every time.
How can I maintain the session data in this situation?
Although you allow access from external domains with the Access-Control-Allow-Origin header, the session itself is cookie based.
If the script which makes the request is delivered from the external domain, it will not be able to read the cookie and pass it to your server.
Solution: Deliver the JS which makes the call from the same context (i.e. same protocol/domain/port) as the AJAX service. Also make sure that the cookie itself is not restricted to a different subdomain or path.
DO NOT try passing the session identifier by POST or GET requests, this will make your application vulnerable to CSRF.
By the way, setting the allowed origin to * is also discouraged, because it can also be used for XSS/CSRF, in combination with other techniques. Please do limit requests to the third-party domain.
Last, but not least, you might also want to look into the subject of preflight requests via HTTP OPTIONS.
A server (in this case the PHP application) can have a session with many different clients at the same time. There is no way for the session to tell which session belongs to which client without having the PHPSESSID cookie. This cooke has to set in the request header for PHP to identify the client.
Related
Imagine a scenario where you own mysite.com, and it loads javascript from an exterior domain such as adsprovider.com/ads.js.
Can adsprovider.com's javascript then perform an ajax call to mysite.com and attempt to retrieve the user's session data? If so, how can you protect your users against it?
Can adsprovider.com's javascript then perform an ajax call to mysite.com
Yes. The origin is defined by the URL of the HTML document the script is loaded into, not by the URL the script itself is loaded from.
retrieve the user's session data
Only if the session data is exposed through HTTP. Stuff you never give to the client is safe (but there is probably going to be quite a lot of session data that should be kept private between your server and the user).
Note that the script can read document.cookies and steal the session token too (unless the session token is sent with the httponly flag on).
If so, how can you protect your users against it?
Sandbox the adverts in iframes hosted on different origins.
Fortunatly, an Ajax call for your session data can't be directly request from external server.
You can load javascript from a CDN external source of course, but the script run from your own domain, and access to your server is not possible from outside.
Otherwise there would be a lot of security problem.
Also keep in mind that javascript is client side.
You can check Same-origin policy and CORS for more info on this topic.
Say I'm on my home PC with my browser open, I visit a site that uses sessions, as long as I don't close my browser or remain idle for 24 minutes the session will be maintained as I make subsequent HTTP requests to the site.
However on my server running PHP, if I use fopen() or file_get_contents() to request a page from that same site, does that site create a session for the 'user' that is my server? Is there a way to keep the connection open so the session is maintained for subsequent fopen() requests?
Can I do this by passing the session id in the request headers on the subsequent request? ie I set the headers including the session id, pass the headers to a context_stream_create(), and then pass the context with the next fopen() request? Can this be done for POST requests (which is what I really need to do)?
Any app which can speak HTTP language have the capability to save the cookie and therefore save the state in HTTP.
The answer to your questions is YES. You can do anything you want with just using the right protocol ( GET/POST ) and sending proper headers ( Host, User-Agent, ... )
But you have an alternate solution, i think this class will help : PHP Browser Class
I know how the cookies work, just started to dig why Codeigniter does not store generated csrf token in SESSION, it just store in cookie. Concerned about security, I'v started to think about php setcookie() function params such as path and domain. And I have asked myself is it possible to set 'evil_cookie' with a path='/' and domain = 'www.goodsite.com' from another domain, from some 'www.evilsite.com'? And another question is, will 'evil_cookie' be sent to 'www.goodsite.com' when performing request to 'www.goodsite.com'?
So, I did a test. I'v created 'set_cookie.php' file and uploaded it to some 'www.evilsite.com':
setcookie('evil_cookie', 'gotcha', time() + 60 * 30, '/', 'www.goodsite.com');
I was using Firefox and Firebug + Cookie plugins for viewing sent and received cookies. So, I did receive 'evil_cookie' after the request to 'www.evilsite.com/set_cookie.php'. However, the cookie was not saved (at least there was no such cookie when viewing in firebug cookie plugin panel). Nor it was sent when requesting again to "www.evilsite.com/set_cookie.php". Just received but not saved.
From the Firefox browser point of view, it's logical and secure to save cookie for current domain only. IMHO those set cookie() params such as path and domain are primarily for managing cookies for current domain and its subdomains but not for external domains. I was a little bit upset I was unable to find related info on php.net, so I'm not sure is it a browser related behavior and specifics how it deals with "3rd party cookies" or it's more a standard? Does all browsers behave the same? If there's any solid and reliable source for such statements please share.
That is also relevant to another use of cookies - store session data (without using PHP native sessions, for example Codeigniter does so). So, if all browsers do not allow to safe cookie with other than current domain then It's OK. However, it does not protect from CSRF as 'www.evilsite.com' might contain evil javascript code that will create 'evil_cookie' directly on the client when a user will perform and get a request from 'www.evilsite.com'.
Cookies are subject to the same origin policy: a site can only write and read cookies for its own domain.
Cookies can only be set for the current domain or its subdomains as you have alredy found out (otherwise, anyone could replace anyone else's cookie; chaos would ensue). This is enforced by the browser: if you attempt to set cookies for another domain from the server side (using an HTTP header) the browser will ignore the cookie. If you attempt to do the same from the client side (using Javascript), the same origin policy will prevent you from doing so.
Therefore, www.evilsite.com can set a cookie for its own domain with Javascript, but that's not an issue: it could already do that using an HTTP header. There's no new attack vector here.
[…] is it possible to set 'evil_cookie' with a path='/' and domain = 'www.goodsite.com' from another domain, from some 'www.evilsite.com'?
No, user agents should ignore Set-Cookie directives with Domain attributes that do not domain-match the current requested domain:
The user agent will reject cookies unless the Domain attribute
specifies a scope for the cookie that would include the origin
server. For example, the user agent will accept a cookie with a
Domain attribute of "example.com" or of "foo.example.com" from
foo.example.com, but the user agent will not accept a cookie with a
Domain attribute of "bar.example.com" or of "baz.foo.example.com".
Such cookies would not even be accepted by user agents. Similar applies to the Path and Secure attributes.
See also How do browser cookie domains work? for examples of how the Domain attribute values are interpreted by user agents.
I have a class that has a function, lets say class.php:
class fun {
public function get_cookie() {
$old_cookie = $_COOKIE['mycookie'];
}
public function ssl() {
//redirect from http to https
}
In another php file, lets say index.php:
//include fun class
$fun = new fun;
$fun->ssl();
$fun->get_cookie();
My question is since the function get_cookie is after $fun->ssl() does the user send the cookie encrypted? or since the cookie code is coded before the $fun->ssl() is executed, the cookie gets sent unencrypted?
Never send anything via cookies which requires encryption.
Regardless of the answer to the actual question posed here, the contents of your cookies should be considered to be publically accessible and insecure.
Firstly, the entire set of cookies for the site is sent (in both directions) with every single web request. So even if you successfully encrypted them with SSL in this particular request, the user would only need to make a plain HTTP request for an image on your site, and he'd transmit them and get them sent back unencrypted.
Secondly, it is not unheard of for cookies to leak between sites. Many cross-site scripting hacks exist which can allow third-parties to get hold of your user's cookies. These would not be stored encrypted on the user's machine, even if they were sent via SSL.
So I'll repeat my initial statement again: never send anything via cookies which you need to keep secure.
The Wikipedia article has a very nice explanation of how cookies work. Basically, cookies are sent along with the request header. So unless the connection is being made via HTTPS then the cookie is being sent in the clear.
The cookie is sent before your code is running. PHP reads the header, fills the global variable $_COOKIE[] and then executes your code. So if somebody makes a request with HTTP, he will get the cookie unencrypted.
When you create the cookie, you can define, that the cookie is only sent to pages requested with HTTPS. You do this with the functions session_set_cookie_params() or setcookie() with the $secure parameter. Such cookies won't be sent, if a page is requested with HTTP.
If I had a user logged onto my site, having his id stored in $_SESSION, and from his browser he clicked a 'Save' button which would make an AJAX request to the server. Will his $_SESSION and cookies be retained in this request, and can I safely rely on the id being present in the $_SESSION?
The answer is yes:
Sessions are maintained server-side. As far as the server is concerned, there is no difference between an AJAX request and a regular page request. They are both HTTP requests, and they both contain cookie information in the header in the same way.
From the client side, the same cookies will always be sent to the server whether it's a regular request or an AJAX request. The Javascript code does not need to do anything special or even to be aware of this happening, it just works the same as it does with regular requests.
If the PHP file the AJAX requests has a session_start() the session info will be retained. (baring the requests are within the same domain)
What you're really getting at is: are cookies sent to with the AJAX request? Assuming the AJAX request is to the same domain (or within the domain constraints of the cookie), the answer is yes. So AJAX requests back to the same server do retain the same session info (assuming the called scripts issue a session_start() as per any other PHP script wanting access to session information).
Well, not always. Using cookies, you are good. But the "can I safely rely on the id being present" urged me to extend the discussion with an important point (mostly for reference, as the visitor count of this page seems quite high).
PHP can be configured to maintain sessions by URL-rewriting, instead of cookies. (How it's good or bad (<-- see e.g. the topmost comment there) is a separate question, let's now stick to the current one, with just one side-note: the most prominent issue with URL-based sessions -- the blatant visibility of the naked session ID -- is not an issue with internal Ajax calls; but then, if it's turned on for Ajax, it's turned on for the rest of the site, too, so there...)
In case of URL-rewriting (cookieless) sessions, Ajax calls must take care of it themselves that their request URLs are properly crafted. (Or you can roll your own custom solution. You can even resort to maintaining sessions on the client side, in less demanding cases.) The point is the explicit care needed for session continuity, if not using cookies:
If the Ajax calls just extract URLs verbatim from the HTML (as received from PHP), that should be OK, as they are already cooked (umm, cookified).
If they need to assemble request URIs themselves, the session ID needs to be added to the URL manually. (Check here, or the page sources generated by PHP (with URL-rewriting on) to see how to do it.)
From OWASP.org:
Effectively, the web application can use both mechanisms, cookies or
URL parameters, or even switch from one to the other (automatic URL
rewriting) if certain conditions are met (for example, the existence
of web clients without cookies support or when cookies are not
accepted due to user privacy concerns).
From a Ruby-forum post:
When using php with cookies, the session ID will automatically be sent in the request headers even for Ajax XMLHttpRequests. If you
use or allow URL-based php sessions, you'll have to add the session id
to every Ajax request url.
It is very important that AJAX requests retain session. The easiest example is when you try to do an AJAX request for the admin panel, let's say. Of course that you will protect the page that you make the request to, not to accessible by others who don't have the session you get after administrator login.
Makes sense?
One thing to watch out for though, particularly if you are using a framework, is to check if the application is regenerating session ids between requests - anything that depends explicitly on the session id will run into problems, although obviously the rest of the data in the session will unaffected.
If the application is regenerating session ids like this then you can end up with a situation where an ajax request in effect invalidates / replaces the session id in the requesting page.
That's what frameworks do, e.g. if you initialize session in Front Controller or boostrap script, you won't have to care about it's initalization either for page controllers or ajax controllers. PHP frameworks are not a panacea, but they do so many useful things like this!
put your session() auth in all server side pages accepting an ajax request:
if(require_once("auth.php")) {
//run json code
}
// do nothing otherwise
that's about the only way I've ever done it.