We are building a PHP multi-tenant application. Each company's account will run on their own subdomain abcorp.example.com. The application allows companies to write and publish content (faqs, etc) for their customers to read.
They will tell their customers to visit: abcorp.example.com/ to read the content. Or they will put a link to that URL in their secure web application.
However these companies may not want just anyone reading the content by going to abcorp.example.com/
So, the question I have is there any way to provide some basic authentication without getting into username and password authentication. I was thinking about some kind of hidden token added to the hyperlink or something like that
My goal:
If users type abcorp.example.com/ directly in the browser, they will not be able to see the web page because they didn't authenticate or pass the token in.
Avoid using username and passwords
Another option would be Referring URL Authentication
Of course, if someone makes the token public, it will open up access to whoever finds it.
I suppose each company could link to their page using a shared token, for example:
abccorp.example.com/?t=4rrfwr23rwads3
Each token could be stored in a file or a database.
When someone requests a page, it checks the value of $_GET['t'] with the one stored on the server. If it matches, it loads the rest of the page. Of course, this variable would have to be carried throughout the site, and included in every link.
Again, this will not be very secure. An exposed token could give access to the site to the entire world.
Your "hidden token" idea is essentialy the way sessions work. A session can be used to identify a user (ie. keep track of what a user does as they browse through the site), and is propagated either by passing the session ID along in links or by storing it in a cookie.
However, using a session without any other sort of authentication is inherently insecure! When you expose the way to authenticate and track users to the user itself, the user can modify or forge their authentication. For instance, the user could change the value passed along for the session ID or change the value stored in the cookie.
Please read the PHP manual section on sessions and security.
Client-side certification. Check out http://en.wikipedia.org/wiki/Mutual_authentication.
You could also use the clients IP address as a token, giving different IP addresses access to different (parts / instances) of the system. But gain, this is not very secure, as
you have no way of knowing who is behind the client PC and
IP addresses can be spoofed. Perhaps you could develop additional specs; giving IP addresses only access during office hours, or check the clients browser (user agent) and check it against the user agent officially being used at the client.
You can use basic hashing whereby a shared secret password or "key" is stored on your system and each company system (a different key for each company and not published publicly), and then you hash the secret password with the subdomain in the link and include the digest as a parameter. Then you validate it by running the same algorithm on your side and compare to the digest.
the link might look something like
abc.example.com/?d=b5939ca22f5dcf345b4000641995478c5910dbd1607b1bdadcbf4a8618a95211
where digest is:
$d = hash('sha256', $secret_password.$subdomain);
or including the referer:
$d = hash('sha256', ($secret_password.$subdomain.$_SERVER['HTTP_REFERER']));
The hurdle to get over is making sure each of the companies can support the correct generation of these links based on the company specific key/algorithm - and that it is different for each company so one company cannot produce links for another.
It is better than no authentication, or a public shared token that is not validated at all, but I'm sure it still has vulnerabilities.
Related
I am using a token based authentication for accessing a few crude web APIs I have designed for my site. At login, the username and password are posted to the login API, and it generates a token with a unique secret key, and the key is stored on the database. At each subsequent call, the token is sent with the request and verified on the server with the secret key.
I am using a web app to consume this API service and deliver a front end to the users. The web application is designed using HTML/Bootstrap/JQuery and the backend is written on php.
I have successfully tested my app and the token based authentication works. However, I have one concern. I find that the user id and token are displayed directly, only using url encoding on the address bar of the browser.
http://hasconpanel.ckoysolutions.com/hasconpanel.php?inputs={%20%22username%22%20:%20%22Debopam%20Parua%22%20,%20%22uid%22%20:%20%2220170520193421DP%22%20,%20%22token%22%20:%20%22Sa2pHyooWPoI79vfvJzLlw7UO%252B2p5hOpBttkEq7LQ%252BjAGm9XEmxfhLAcnJoLbqrsXCp75%252BG1M7nEUoCgsDVbIQ%253D%253D%22%20,%20%22list_of_devices%22%20:%20[{%22device_code%22:%22b8:27:eb:f1:b3:0f%22,%22device_name%22:%22First-Pi%22}]%20}
Now, if this address is copied, or suppose the browser is made to resume the previous session, no matter who tries to access it, they get an entry. Especially in case of public computer centers, if anyone accesses my web app with their credentials, and forgets to logout before killing the browser, the token system seems to fail miserably. Is there anyway to secure the token like encrypting it? Or someway for the app to not store the parameters in case of a browser/browser-tab close or at least not display it on the address bar? I have thought of making a fresh token at each request, but it slows the system drastically, so I want to avoid it.
Please suggest some way to solve this problem.
Thanks in advance.
Edit:
Explaining how the system works now:
The api system is hosted on the main domain of a shared server, and the app is hosted in a sub-domain. The main domain also hosts several webservices that are being called from a few raspberry pis installed at home.
This is how it works, the login is made from the primary website and on successful login, the web app is called with a get call with the user id, the token and a list of working devices for the user. Check is provided to prevent the app page being accessed without any of these three parameters. On fresh load, the user gets the choice to select a device from a drop-down menu. Now, each of these working devices can have three separate systems running. So, on selecting the device, a get call is again being made to the app, with the selected device added as a parameter along with the three previous parameters. This shows the token and the uid on the address-bar.
Token based authentication is very standard in the web world. The details vary, but what you are trying to do certainly isn't crazy. However, your security concerns are valid, and there are a number of potential solutions:
Use HTTPS exclusively. This will protect your token from being easily readable by everyone in between the user and you. Actually, do this regardless of anything else. HTTPS should be considered the default for security purposes these days: just pretend that HTTP is deprecated.
Move the token into the header of the request instead of the URL. As long as you are using HTTPS this doesn't actually change anything from a security standpoint, but it is fairly standard for the industry. It will also keep the token out of the browser history.
It is strange that you have a URL showing up in the browser address bar. I would expect a client-side application to make requests exclusively via ajax, which means there should't ever be anything in the address bar. You might need to clarify more on how exactly this application is working. I suspect you need to refactor so that none of your application URLs ever end up in the address bar, and instead operate via AJAX requests exclusively.
Still, HTTPS is the most important part. Your entire transaction after DNS lookup will be transmitted securely, so the token cannot be stolen by a man-in-the-middle. This is the most important step you have to take to secure it. If you don't use HTTPS, you might as well broadcast it to the world. Of course, if you are making non-ajax requests to a URL with the token in the query parameters then the token will be visible in the browser's address bar and history. Again, avoid that by using ajax requests only and put the cookie in the header.
Once you have HTTPS in place with ajax-only requests, the chances of having a token stolen are much smaller. Still, it can happen (in particular via an XSS attack), so become familiar with the principle of "defense in depth". Also, there are steps you can take to try to detect a stolen token an invalidate it. Things like:
Invalidate a token (for the user to log back in) if the IP Address changes (although this can impact mobile users, which probably isn't desirable)
Invalidate a token if the user agent changes (although that can be spoofed)
Enforce a server-side maximum session length
Make sure and require the user to re-authenticate if they want to change email/password.
Those are just a few suggestions off the top of my head. Again though, this is a pretty standard problem, so google will be your friend.
Note: I know there are LOTS of other StackOverflow questions dealing with this topic. I've read through many of them, as well as many other websites. I still have the following questions.
So, I'm building a REST API for a new product. At this time the API is entirely for private consumption by our websites and phone apps. However, I'm thinking it might be smart to design the API so that it can be made public in the future.
Authentication
While I've looked at OAuth, I think HTTP Basic Authentication over SSL is plenty secure enough for our API. From what I understand HTTP Basic Authentication over SSL is a completely viable way of authenticating a REST API. It's also quite simple, which is appealing for me since I'm new to API development.
Authorization
If a user logs in to the API using their username and password, they will only be given access to certain parts of the API. Meaning they'll have access to their own content, but not the content of other users. Further, they may be limited to what they can all do.
In addition to the user accounts, I plan to also have other other (non user) accounts for more global administrative tasks. These accounts could potentially have full access to the API.
Is this a good design? OR, is it bad to authenticate a user in this way? Should I only be authenticating my clients (ie. apps) this way?
Sessions
My big question is, when logging a user into our web app, how do I manage their sessions? REST stipulates sending the username and password with each request. Further, REST API's are stateless, so I cannot manage sessions there. However, I need to track that they've logged into the web app somehow. They clearly can't possibly login manually for each request.
One approach is, after a user logs in, we save their login credentials (email & password) to the PHP session. Then, each subsequent request to the API could use those credentials. However, saving usernames and passwords in a PHP session just feels wrong and very unsafe. But if not done this way, how are people managing sessions when interacting with a REST API?
The phone apps are easier, as you can save the user's login credentials into a keychain.
Can anyone help with my design questions?
I know this question is a bit old and maybe you already finished your work, but I'd like to give you some tips. Maybe these could help you or anybody in the future. :)
Authentication
HTTP Basic Auth over SSL is quite simple, that's true, but not so secure than you think. You only have to install 1 "fake" SSL cert on the client and with a man in the middle attack you can sniff the traffic.
How to install a fake certificate? It's not so hard in a browser lot of users just click on the ok when they see the huge red warning screen. On a mobile for example: http://cryptopath.wordpress.com/2010/01/29/iphone-certificate-flaws/
With this solution you only have to intercept the traffic once and you'll have the user's password!
My tip: Generate a temporary password at login and use this in every other requests. So the attacker have to intercept the login process for the password and if you store this pass locally on the phone for example it's much harder. (And of course you can add expiration to it etc...)
Authorization
I don't really understand what would you do. User access management is a good thing, but it depends on the given project.
Session
Not only the REST APIs, teh whole HTTP world is stateless. If you use a PHP session it stores a session id in a cookie on the client side and the browser sends this cookie value every time to the server.
The users don't have to login every time. They log in once and get a token/temporary password etc... and (or if you don't use these stuff) they send you a basic auth header at every requests.
This way you can easily track who sent you the request, because you already now who's that user and you can store and link some data to it on the server.
There are many ways to deal with users. Basic auth is one of them. And check this: OAuth's tokens and sessions in REST
"OAuth tokens are explicitly a session identifier, ..."
You don't have to store the user's password and email, you just have to check the headers/cookies/etc... from the client in every requests.
The phone apps are easier, as you can save the user's login
credentials into a keychain.
They can, but saving the user's real password on a phone is a very bad practice. Save a time limited token is a bit better. :)
In every other languages you can store values if you want. For example if you want to use a Python client for your API: It authenticates and stores a token or something what it needs in a variable and at every other requests it uses this stored data.
One more sidenote:
However, saving usernames and passwords in a PHP session just feels
wrong and very unsafe.
True that's unsafe, but the (real) PHP sessions are stored on the server side and as I said it stores only a single session id on the client side. Anybody who can get this session id, could impersonate the given user. (There are countermeasures for example IP check, etc...)
We have a website (foo.com) that does online training. A user logs in, then completes their training.
We've agreed to allow another company (bar.com) to send their clients through our training. One of the requirements is that their users should not need to create a separate login account on our site.
Here is my initial plan of attack:
When a user logs into bar.com (the other company's website), their backend will make a secure HTTPS request to foo.com (our website) requesting a one-time access token specifically for that user. For example, they may request the following URL:
https://foo.com/api/request_token.php?user=bob&pass=A1B2C3D4E5F6
This requests access to bob's account. The 'pass' component is a shared passkey known by foo.com and bar.com that is used to verify that the request is legitimate.
foo.com will respond with a one-time access token (for example, 0123456789ABCDEFG) which is stored into a database along with the user's id (bob).
bar.com will present a hyperlink to the user that links back to the online training at foo.com. Something like this:
https://foo.com/api/login.php?user=bob&token=0123456789ABCDEFG
When the user clicks on the link, foo.com checks the token in the database and (if it has not expired) removes it from the table of valid tokens and creates a session variable indicating that bob is now logged in, then redirects him to the training.
What I'd like to know is, where are the security holes and how can I mitigate them? I know that the URL will be encrypted, and I know that an entry will show up in my server log, but it's a one-time token, so I'm not worried about that. I can imagine someone brute-forcing different tokens, so I've included the user name in the second login url that the user clicks on so that the token will only work with that specific account.
But what I'm really worried about is what I don't know. And I don't understand much about the security issues here.
(Please note that this only covers an existing user who is attempting to log in. I will use another method to actually create the user account on foo.com)
I am coding in PHP.
What you're looking for is known as single-sign-on (SSO). There are a number of different industry-standard protocols for achieving this, but what you're basically doing is having another entity authenticate the user and provide that information to you in a way that you can verify that something you trust (their authentication system, whatever it is) has issued it.
The most standard way of doing this is SAML (Security Assertion Markup Language). This is a protocol where the third-party would authenticate to their system and it would generate a SAML Assertion that basically says who the user is (and other information, should that be required). The assertion is digitally signed so you can verify who issued it. This requires that you and the third-party exchange keys (in the form of certificates) and come to an understanding about what your assertions will contain, etc (typically expressed in SAML metadata that is exchanged between the Identity Provider and the Service Provider).
There are a number of SAML implementations/references out there for many platforms, including PHP. If you want to do this right, and securely, that is what you should investigate and pursue.
I'm trying to implement single sign-on for a web portal. I've written some code to send a POST request containing the user's login credentials to an external web app to log the user in. (Don't worry, this is all over SSL)
The HTTP response from the web app contains a cookie for the user's login. Is it possible for the web portal server to then pass that cookie to the user's browser? Or is that impossible since the web app is on a different subdomain? I understand there are some security measures built into cookies.
Short answer: NO.
The HTTP server can indeed log into the other service and pass the service's cookie back to the user, but the browser will set that cookie's domain to be the HTTP server's, not the remote service's. There's no way for 'server A' on 'domain A' to make a cookie appear to have originated from 'server B' on 'domain B'. If it were possible, it'd be trivial to steal everyone's authentication cookies for their bank, facebook, myspace, etc...
There are indeed things like cross-domain policies build into modern browsers.
However once upon a time, I created a single login techlology for my own website.
There is a trick you can do. First, on the main site where the users have their
login information, have them a secret generated key. With this unique secret
key to every user, pass them to the other site like
www.abc.com/secret_key
from this secret key, your other website should be able to pull the needed information
like username, profile picture & stuff like that and should create the session on that
domain. So you would have the session created for the opposite domain.
If you still need to pass something back, I would recommend you to go a way that
incorporates RPC over PHP and post something back to your major domain.
This should solve your problems. If you want I can attach some example code.
Note: The security here is in the secret key. Since it's a unique generated key
for example, a md5 hash, it's hard to replicate this. So there is no such thing
like someone could reprocude the secret_key and then login to your site as someone
else.
You should also note that, the secret_key api should only be able to get the
needed information so that not too much information is gathered on the other side.
Why reinvent the wheel? I think that you can find OpenID implementations for PHP. For the consumer and the provider.
You can restrict your OpenID-logins to your domains only if you don't want them to be used elsewhere.
I am building a small app as a service in django and now is the time to integrate it on some clients PHP web app.
Our client A from domain www.a.com handles his own authentication for his users and probably use cookies for sessions.
How could i make logged in users from his domain also logged in on my Django app dommain www.b.com/clientA/ ?
I see how i can make them reloggin on my domain and use an authbackend checking credential with domain A but that means the user will have to enter his login/pass twice: on www.a.com and www.b.com.
Accessing cookie from domain www.a.com is impossible for security reasons i think.
How would you handle this ?
You are correct in assuming cookies from another domain cannot be accessed. However, if it's on a subdomain, you should be able to access the cookies if they're set correctly.
If you absolutely must have them on completely separate domains, it's going to be a bit tricky. If you can't modify the existing PHP code, you can pretty much forget it.
One option would be using OpenID - that may be the simplest way to tackle this, as there are OpenID libraries available for PHP and Python. OpenID would allow you to have a single-sign on like authentiction, and since it's already used on various sites it is proven and works.
Another option is writing a custom single sign-on system.
The basic idea is that when a user arrives at your site, you direct them to a login site. This can be either in the PHP or Python end of things, or separate. Here, the user will sign in, and then the login generates a secret key - this can be a hash, random string, whatever as long as it's not predictable - and the user is redirected back to the main site with the key.
The main site then sees the user has a key, and sends a request to the login site behind the scenes to verify the user's key.
Now the user is logged in at one site. When the user visits the second site, it too redirects the user to the login site. Since the user had already logged in, the login site simply redirects the user back with a new secret key, and the second site verifies it from the login site and now the user is logged in without having to input their credentials another time.
Ok, this is how to authenticate a Django user from PHP, or how to "read" a Django password from PHP.
I think OpenID is the best solution but I had to authenticate Django users in a PHP app sharing the same database today and this is how I solved:
<?php
/* Generates crypted hash the same way as Django does */
function get_hexdigest($algorithm, $salt, $raw_password) {
if (!array_in($algorithm, array('md5', 'sha1'))) {
return false;
}
return $algorithm($salt.$raw_password);
}
/* Checks if password matches the same way Django does */
function check_password($raw_password, $django_password) {
list($algorithm, $salt, $hsh) = explode('$', $django_password);
return get_hexdigest($algoritm, $salt, $raw_password) === $hsh;
}
?>
The key is to understand the format in which Django saves the passwords, which is:
[algorithm]$[salt]$[hash]
So for example I had an "admin" user with password "admin" and the password field in the auth_user row was:
sha1$63a11$85a93f217a72212b23fb0d5b95f3856db9575c1a
The algorithm is "sha1", the salt, which was generated randomly is "63a11" and the crypted hash is "85a93f217a72212b23fb0d5b95f3856db9575c1a".
So who do you produce the crypted hash in PHP? You simple concatenate the salt and the raw password and hash it with the algorithm, in this case, sha1:
<?php
$salt = '63a11';
$pass = 'admin';
echo sha1($salt.$pass); // prints "85a93f217a72212b23fb0d5b95f3856db9575c1a"
?>
That wasn't difficult! I got it by reading the relevant code in the Django sources.
You can use HTTP redirects back and forth. When the user accesses www.b.com, and no cookie is set, redirect to www.a.com/crosslogin?return_to=URL&challenge=stuff. On a.com, check for the cookie, and if it is set, redirecto to URL?verified=otherstuff.
This would require challenge-response cryptography if you want users to prevent from faking authentication. a.com and b.com would need to setup a shared secret, and stuff is encrypted with that secret. otherstuff is also encrypted with that secret; when decrypted, it gives a tuple (stuff, user). b.com may need to keep a replay cache to make sure that otherstuff can be used only once.
I see the following options:
1) Use Open ID as Jani Hartkainen suggested. It could be the best solution.
2) Use one domain via http reverse proxy:
Use reverse http proxy to put both php application and your django application on the same domain. This would give you access to the sessions cookies of your php app.
Once you get the php session id in your django application run a request to the PHP application with the session cookie set to check who is logged in.
Unfortunately this may require html scraping or implementing a simple service in PHP application that would return a name of the logged in user.
Once you get the logged in user you can authorize it in your django app.
3) PHP session id passed via GET:
Modify the PHP app to add session id as a parameter to links to your django app.
For example ask clients to refer to your web site as follows:
<yourwebsite.com>/?client_session_id=<session_id>&client_name=<client_name>
Once you get the session id you can authenticate user as described in point 2.