I have a "Client Center" on my website which is only accessible via HTTPS. I'm currently using a single session cookie to track PHP session variables such as User_Authenticated, Client_ID, etc once they log in.
I want to change the links on my navigation bar from "Login" to "Logout | Client Center" once they have logged in. This is easy enough, but most of my website has absolute links that point to HTTP instead of HTTPS. I could make all of my links relative and that way hopefully keep them on HTTPS once they've logged in, but my site is graphics-intensive and I can't guarantee that they'll stay on HTTPS (if, for instance, they type http://mysite.com on their browser after logging in).
My question is as follows:
1) Is there a risk in passing this cookie over HTTP? I hear that it can be intercepted and spoofed. That would be bad!
2) Can I use two cookies, one which is a secure_only cookie which contains their credentials, and another which just lets the browser know if they're logged in or not, for the links on my navigation bar? Is it possible to use multiple sessions simultaneously like this with different cookies?
I've also heard about using a database-based session management system, but for now that is way over my head. I'm new to PHP and MySQL (this is my first site).
Thanks in advance!
You can do what you want, using a second cookie. Then you can use the session cookie for HTTP and HTTPS pages (to get the login state), but make the authentication only with the second HTTPS-only cookie.
I wrote an article, describing how to separate this two concerns, maintaining the session and authentication. It's a good thing to do, even if you decide to use HTTPS for the whole site.
I would think about enforcing HTTPS for the whole site though, this would make your life easier. If your site has not very high traffic, it should be no problem for todays servers.
Authentication cookies being passed over http is a bad idea. That's the problem that firesheep brought to light, and the reason that Google, Facebook and Twitter, among others, now encourage (or require) https all the time.
If you're worried about sesion hijacking on your web site then you should be enforcing https for every page and asset that needs to be secured. Ideally enforce https for all assets to avoid annoying borwser warnings about mixed security on the page.
You can use this
http://systembash.com/content/force-https-ssl-access-url-apache/
Related
Normally When I design a site that allow users to login, I create session variables of the user info from the database and ensure that at least one of the session variable is available on each page of the site, else the user would be redirected to the login page like this:
if(!isset($_SESSION['username']))
{
header("Location:login.php");
}
But then I've been thinking lately, instead of using session variables to authenticate users, why not use the query string. My idea is to create a unique string which is based on some factors, like date(month, year, day) or access time(day, hour) or ip address, and maybe hash it using md5 so the url might look like this
://mysite.com/dashboard?auth=12jsdnnau819wiskj3jdnck23ksj12j3.
So now I can easily logout a user if he has not accessed the site for more than one hour or more than one day. But I do not know if this is a good idea, that is why i am here, to seek for advice on a better way to go. Thanks all.
Because URLs:
Leak easily
Get bookmarked
Don't carry their data over automatically when the user opens a new tab and navigates back to the site
and because nothing is stopping you from storing the same data in a session and using that to easily logout a user so it doesn't even add the one benefit you highlight.
Don't do this.
Do you remember the days when PHP session ID could be stored in the URL, and you would have URLs that look like: index.php?PHPSESSID=.... ?
We have moved away from this to more secure implementations, user friendly URLs, etc.
Just as a simple example of what can go wrong: A web crawler can crawl your website, and if reaching the admin panel with a properly authenticated URL (as you described), it could become publicly available to ... anyone.
So ... don't reinvent the wheel.
I can think of a few reasons not to do this:
The hashed request parameter is publicly visible. This means anyone using that url will be considered an authenticated user.
As I can see, the token is per user, so all requests using this token will be done on behalf the same user. If you share a url, anyone using that url will impersonate the same user, and have all the access rights granted to that user.
Other answers have mentioned that the urls can easly leak, or be bookmarked. In addition to their points, once the token expires, the url may become broken, if you trigger a login or other authentication redirect mechanism.
A pure technical drawback of your idea is also this: you need to persist the token when navigating across different pages in your app
The HTTP Session has been designed to serve this purpose and at the same time be safe enough. The application session length can be tuned so that it matches your requirement. I recommend you to get familiar on how HTTP sessions work and how to tune your session expiration policy, rather than compromise your application's security
Pretty basic question here. In PHP, if the user's browser has cookies disabled, you cannot make use of both server cookies ($_SESSION) AND client cookies ($_COOKIE, setcookie) or only the latter are disabled?
Basically you can't make the user log in or do anything that requires a session, right?
Also, in which case would someone want to have cookies disabled?
Thanks!
Yes, it's true. Both sessions and normal cookies are normal cookies. If a user does not accept cookies, he cannot use any of the functionality enabled by them. Which means pretty much the whole internet would break for that user, which is why in this day and age there's virtually nobody who has cookies disabled entirely.
PHP has a built-in mechanism called transparent session ids, which automagically rewrites all links to contain the session id in a query parameter. I would not suggest using it, since session ids in the URL open up a whole new can of worms.
For user friendliness, I'd recommend you test whether the user has cookies enabled or not (set a cookie, redirect to the next page with a flag in the URL that cookies should be set, see if you get any cookies back) and if not, kindly advise the user to enable them.
You can track the user by $_GET.
Imagine that on every-single-page the user visits you pass a ?user_id=XYZ123 then you would have implemented a very similar server-identification. It has obvious disadvantages:
if you copy/paste a URL you'll give away your session_id
because of 1 session high-jack is even less tech savy
Why do users disable cookies?
Users tend to throw first and third party cookies all in the mix but they come from different breeds.
First party cookies are generally ok. When you visit Facebook it's expected that Facebook keeps a cookie to store your interactions with the server.
What it's not expected is that the advertising company that has adds both on Facebook and on eBay gets your cookie back and checks, ah, so this guy was on eBay looking for xyz so now that he's on Facebook I'm gonna show him up abc to make him buy etc etc...
I think you should read the session reference manual http://www.php.net/manual/en/session.idpassing.php
In short, if your server can't find session_id, he can not restore session. But you can use alternate ways to store session values. Or you can generate session_od base on user's client environment parameters.
I have a site set up on www.domain.com, the site can authenticate users and persist their credentials in a cookie.
On occasions the users access handlers that are set up on different servers on a different sub domain. handlers.domain.com
I can't afford to use wildcard subdomain cookies (Cookies should not be available for other subdomains)
My solution for access control up until now was that every URL used for handlers.domain.com had a guid specific to the user. The handlers on the other site would assume the identity of the guid owner. This of course is not such a good security practice.
i was thinking about an alternative solution: All links to handlers.domain.com will actually be links to a redirector script on www.domain.com that will redirect to an encrypted time stamped url on handlers.domain.com which will then know for sure that it was accessed as a direct authenticated redirection from www.domain.com.
This solution will work fine on GET scenarios but will fail with handlers expecting POST data (up do big uploaded files)
Does anyone know or can think of a better solution or have any insight on my solution?
(In this case I am using ASP.NET but the solution will probably be platform agnostic, so I will tag this with various web platforms)
Thanks!
As you do not want to use cookies to establish a session (group of requests) you need to find other ways. As the information of the cookie is passed readable within the HTTP request, I do not see a problem if you for that one particular request pass that information as part of a POST request.
If you prefer a GET request I would additionally add a flag inside the users server-side session prior the redirect so to give the script that is the destination of the redirect the possibility to verify the validity of the request on the server-side.
You said you "can't afford to use wildcard subdomain cookies (Cookies should not be available for other subdomains)". Does that mean you can't afford it monetarily or you you don't want the user to have access to all subdomains? If it's the second, you could still use subdomain cookies by putting in an encrypted value with that user's ID and check it versus access permissions on your various subdomains. That keeps everything at the server where it's more secure versus at the URL level. The only way a potential hacker can get past it is to guess another user's ID and figure out your keys for properly encrypting it.
I thought I was being really slick by using the $_SERVER['HTTP_REFERER'] variable to guarantee my script was being called from the appropriate page.
Luckily, when I performed a header('Location: yourPathHere.php') redirect in my testing browser, it wouldn't set the $_SERVER['HTTP_REFERER'] variable. So I looked it up at http://php.net/manual/en/reserved.variables.server.php, only to find this...
'HTTP_REFERER'
The address of the page (if any) which
referred the user agent to the current
page. This is set by the user agent.
Not all user agents will set this, and
some provide the ability to modify
HTTP_REFERER as a feature. In short,
it cannot really be trusted.
So my question is: How can I guarantee my page is being navigated to from a trusted source?
EDIT: To clarify questions regarding the comments section. I'm trying to avoid XSRF (cross-site request forgery).
Relying on any user originated input for request verification is almost no better than no verification at all.
You should read this section on CSRF countermeasures from Wikipedia for a basic outline of available approaches to tackling the issue.
In short:
Web sites have various CSRF countermeasures available:
Requiring a secret, user-specific token in all form submissions and side-effect URLs prevents CSRF; the attacker's site cannot put the right token in its submissions
Requiring the client to provide authentication data in the same HTTP Request used to perform any operation with security implications (money transfer, etc)
Limiting the lifetime of session cookies
Ensuring that there is no crossdomain.xml file granting unintended access to Flash movies
Is the referring page also under your control?
If so, you could try setting some server-side session variables when a user visits page A, and check for them when he tries to access page B.
But like other respondants have said - this is not related to preventing XSS ..
I have a small problem.
How do I set a cookie for multiple domains?
I do understand the security problems, and I am sure it has been done before. The reason for this is SSO.
ie.
account.domain.com will need to set domain logged in for:
domain.com,
domain1.com,
domain2.com.
Is there any easy way, using PHP and cookies, or any alternatives?
There is absolutely no way for domain.com to set a cookie for domain1.com. What you are attempting to do can only be solved by getting the user's browser to submit requests to each domain which will then set its own cookie.
Then you need a way for each domain to verify the user's identity. There are two approaches to this:
Back channel - the sites contact each other directly to determine if a user is logged in.
Passing a token in the GET or POST - when the user's broweser is redirected to the other site a digitally signed parameter is passed containing the identity and session status.
It's really quite complicated. I suggest you don't roll your own. Take a look at SimpleSAMLPHP for a PHP implementation of what I'm describing.
What you're attempting can't be done. (It's a browser security issue, not a PHP one.)
Other than using some form of off-site authentication, the nearest you can achieve is making a cookie accessible across sub-domains, in which case you just use the optional 'domain' arg of PHP's set_cookie function.
This can be done via one domain acting like a master and others like a slave.
Say we've got a domain accounts.domain.com and it's our master.
Then we've got our slaves domain.com, something.com and another.com
When you'll log on on domain.com, it'll be actually site accounts.domain.com, then you'll get a cookie with unique ID for your browser and then you'll be redirected to domain.com's post-logon landing page (ie. domain.com/logon?check=true&unique-id=<browser unique id>&request-id=<unique request ID>). the landing page will contact the accounts.domain.com, querying it with the browser ID. If the transaction's okay, then you'll get logon cookie from domain.com.
Next, on every domain (domain.com, something.com and another.com) will be initial redirect to accounts.domain.com/roaming-check?return-url=<URL the redirect was initiated from>. Because we're returning home (we're logged already on accounts.domain.com), we'll be redirected again on our landing page (<domain name>.com/logon?check=true&unique-id=<browser unique id>&request-id=<unique request ID>) and from this point it's the same as the part with logging on. We're seamlessly roamed to another domain (without user knowing it as browsers doesn't usually show the redirected page until it passed the headers send(server)/receive(browser) section).
In case there's in fact no active logon, the site will save this "negative logon" to session and not try to check logon anymore (until we try to logon or load another domain).
I think this solution will suit your needs: "Simple Single Sign-On for PHP"