I run a largely session-dependent site in front of which I want to put Varnish.
The user might get a Set-Cookie header on the first request, it depends on which URL he entered.
PHP's session_start is used.
How can I make sure that the Cookie header from a client is in fact related to a valid PHP session if Varnish is in front?
If there is no Cookie header sent from the client I can easily send it to the backend for a Set-Cookie.
Right now I use an AJAX request which returns a Set-Cookie header for each page, that is supposed to (in)validate the Cookie for a user, and it seems for work for 95% of the visitors. The missing 5% is causing a lot of headache.
Is there a widely used method for solving this?
Related
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.
The application I'm working with accepts three different types of login. Automatically if the client connects from certain IP-adresses, or a POST request, either from a normal browser rendered form, or towards a JSON reading API-endpoint.
All three options boil down to calling the same functions for registering the user as logged in, and generating a session.
Despite the code paths being the same as far as I can determine, two of these consistently work, while one consistently fails to add a 'Set-Cookie' header to the response, even though the application logic generates a sessionid that gets sent in the reponse body.
Never having needed to dig into how session authentication works to this level of detail, I realise I don't understand. where the 'Set-Cookie' header, should come from. Should application logic always build the header manually? Will PHP do it automatically once session_start() is called? Does Apache do it based on other parts of the header?
I sort of ruled out 1 by failing to find anything with grep -ri "set.cookie" * in the codebase I'm working with.
session_start sends a session cache limiter and a session cookie (or sets a $_GET key with your PHPSESSID).
This function is where the Set-Cookie paramater is sent from. Apache will then pass it back to the browser when it sends the page back.
You need to remember however that storing the cookie is actually up to the browser. By and large they will be set without issue, but certain conditions will stop this from happening, such as the security settings in Internet Explorer or the user rejecting cookies entirely.
Further reading:
http://www.php.net/manual/en/function.session-start.php
http://www.php.net/manual/en/function.session-get-cookie-params.php
http://www.php.net/manual/en/function.session-status.php
I have the following situation:
User makes request to PAGE A that displays a form (server stores cache of this page)
User submits the form to CONTROLLER that is used for form submit
CONTROLLER finds an error in user submitted data, sets a cookie ERRORS detailing such, and redirects the user back to PAGE A
PAGE A shows the original content plus the ERRORS (server stores cache of this page)
PAGE A deletes ERRORS cookie
This works, but only if I don't use cache on my system for PAGE A.
Problem is that, after deleting the cookie, browser makes a request to my server without the cookie and gets a 304 Not Modified error and, as a result, the browser still shows the page with the error, instead of without (from the original request). Server stores cache properly (for page that has an error as well as an error-free page).
Basically the server has two cached pages now: PAGE A and PAGE A WITH ERRORS.
Browser, whose last known page was PAGE A WITH ERRORS asks for server a page with conditions of PAGE A, instead of PAGE A WITH ERRORS, since cookie does not exist anymore. But it thinks that the 304 response is about the PAGE A WITH ERRORS, instead of PAGE A. I even checked the data sent by the browser, it knows that it got PAGE A WITH ERRORS with the ERRORS cookie, yet accepts 304 not modified when making requests without that cookie. It does not validate its own cache against the conditions it was created under.
Doesn't browser validate its cache with the cookies it has set?
And is there a workaround for it without setting some GET variables for each request? Another alternative is to tell servers to never cache pages that have such an ERRORS state set, but that would be a hack.
Apparently the solution was to include this as a response header:
Vary: Cookie
This will take cookies into account in caching engines.
EDIT:
There is a problem though: Chrome, Internet Explorer and Firefox work as expected, but Safari and Opera both ignore 'Vary' header when storing and validating cache.
client-side sessions (a.k.a cookies) are probably not sufficient for this scenario...
would suggest server-side sessions instead. Even if Vary headers might work - with $_SESSION you'll be on the save side.
Cache-control: public is probably not what you want. Public essentially says that you're working with static, globally accessible data. It doesn't change per user. It's caching globally because you're claiming that the data is global.
As soon as you start changing per user, caches are having their assumptions violated. 'private' is more akin to what you want.
That will mean that you get less of the good middle-man caching, however. You might be able to hit some middle ground with re-validation, or with appropriate use of Vary headers.
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.