PHP: Persisting LDAP connection across requests - php

I have a (I think not so uncommon) scenario:
my PHP app queries an LDAP server to handle authentication. The LDAP server only accepts authenticated access, so the first ldap_bind() call is actually used as the authentication mechanism and to retrieve the logged in user's basic information.
So far so good. But when I want to use the LDAP service to perform other queries (e.g. the logged in user's or other users' extra info) I have to go through LDAP authentication again.
I do not want to store the user's password in session because it might leak, but I can't have the user authenticate at each request either in order to provide the credentials for the LDAP server.
So I thought about the following solutions:
Store the LDAP connection handle in session, so I don't have to provide credentials every time. That won't work because the handle is a resource and can't be serialized for session storage.
Store the password in session, but encrypted. The encryption seed could be derived from some volatile var, e.g. a hash of the session ID, so that it won't be stored in a var and can never leak.
<Your idea here>
Any thoughts?
Thanks
gm

My two cents... First use case I think could be avoided pulling down everything for the user the first time you connect. Store all of it minus the password in session. Second case: Do you need to authenticate as user X to access that data? Can you not create a user with read access and use those credentials (App user) to query these types of things. Alternately maybe that user info should not live in AD. Use it for authentication but keep user data in the DB (master), or synced in DB from AD (slave).

As for storing the password, this is working (using Yii framework, but it's easy to adapt to vanilla PHP):
public function getUserPW(){
if(empty(Yii::app()->user->sneak)) return false;
$seed=sha1(Yii::app()->session->sessionID);
$iv=substr(md5(Yii::app()->session->sessionID),0,16);
return openssl_decrypt(Yii::app()->user->sneak,self::CRYPT_ALGO,$seed,false,$iv);
}
public function setUserPW($pw){
$seed=sha1(Yii::app()->session->sessionID);
$iv=substr(md5(Yii::app()->session->sessionID),0,16);
Yii::app()->user->setState('sneak',
openssl_encrypt($pw,self::CRYPT_ALGO,$seed,false,$iv)
);
}

Related

What is the best way to go about generating a "token" for user authentication

Let's say I have a MySQL database with thousands of user accounts in it. These accounts contain lots of data, but for verification purposes, they each contain a username and a (hashed and salted) password. Now, when a user requests signing in, I will take a username and password from them, transfer it via WSS to a Node.js server then transfer it via HTTPS to a PHP file on another server. On that server I will look up the username in the MySQL database, and if I find an account, I will hash the password and see if it matches that username's password. If they both match, then I want the PHP file to create a "verification token" of sorts, save it (and associate it with the account verified) and send the token back to the Node.js server. I then want the Node.js server to send that token back to the client and for the client to save that token. Now the next time the user connects to the Node.js server via WSS, I want the client to check for an existing token, and if it exists I want it to send that token via WSS to the Node.js server, the Node.js server to send that via HTTPS to a PHP file, and that PHP file to see what account that token belongs to, and complete the sign in...
So I guess my first question would be: Is this a good model? Would this be a good idea, and would this be secure, or should I do this differently?
My second question is: What would be the best way to go about generating this token? Should it be completely random? Should it be a combination of letters+numbers? Should it be a hash of some random values? How should I go about the generation of this "token"?
To clarify, I'm not asking how to build a server or make requests or transfer data or anything of that sort, I'm merely asking what is the most secure way to generate a "token" that can be used as authentication to the same degree that a username+password can be used.
Thanks in advance! I'm sorry if this is a really stupid question.
I think you are describing a JWT. There are several packages implementing this in PHP.

Symfony 3 LDAP authentication populate database with all users

I'm trying to implement an authentication system and I'm unsure of the correct way to components to use in the Symfony security component.
In essence, what I want to do is authenticate users against an LDAP server, but also have access to User objects for the purposes of database queries (e.g. listing all users in a dropdown).
Now in the LDAP tutorial it states:
Checking a user's password against an LDAP server while fetching user information from another source (database using FOSUserBundle, for example).
However, it doesn't really go into any explicit detail about how this is done. How can I create User objects from the LDAP server? I just want to store usernames, emails, and roles - I don't need passwords since I want to do actual authentication with the LDAP server itself.
Or am I better off to not use User entities in a database, and instead grab all the user information from the server whenever someone logs in (so that it's up to date)? If this is the case, how do I fetch this information when someone logs in, and use it in a similar way as I would a set of User entity objects?

How to know the objectGUID of a user that successfully authenticated with LDAP bind

I have:
a PHP application on a linux server
a (windows server 2012) domain controller
I want to make it possible for AD users to log in the PHP application. The PHP application will associate data to the user, so i need to create the user in my database.
When a new user is created in the PHP application, the administrator chooses from a list of the userPrincipalName of the AD users. When a userPrincipalName is chosen, the PHP gets the objectGUID of this user and stores it in the database. Indeed, as the UPN could change, i can not rely on that to uniquely identify the user. Also, i want that if the UPN changes, the user can sill log in seamlessly to the PHP application, with his new UPN.
When a user wants to log in my application, the PHP receives a username and a password, and pass them to ldap_bind to check if the credentials are valid. The problem at that point is that ldap bind gives no clue of what user he actually identified (ldap_bind has a complicated logic of validating a login/password (https://msdn.microsoft.com/en-us/library/cc223499.aspx)).
The most atomic (and hence, reliable) way of doing it would be if ldap bind was returning the objectGUID... But it doesn't, and i won't code the logic of ldap bind in PHP to find which user he has actually identified (it would be buggy anyway because of race condition if the directory change between the ldap bind and my "manual" search)
So what should i do to authenticate AND identify the LDAP user from PHP, if possible, in a single request to the domain controller (to get atomicity) ?
At some point in the process you need to have logic that searches for the UPN being used for the username in the login, obtain its objectGuid, and search for that objectGuid in the database. There is no other way to get that information.
If you're worried about a potential race condition (which seems like quite the edge case, given the timing between the bind and the search would be extremely tight), you could use a separate AD service account that searches for the account by UPN prior to the bind. Then in the ldap_bind for checking the user's credentials you could even use the objectGuid from the account you searched for to do the login, as that's a valid "username" for an AD bind (the GUID with curly braces around it that is).

Laravel LDAP and Database connection

I have several different types of users who use my system. One set of users is unique as all their credentials are accessed via LDAP, the other users are all relatively similar but do have different roles. I am using the built in User class for the regular users (that is, not the ones who login using LDAP) and am using Laravel 4. I query the LDAP and it returns either success or failure but once I've done that I don't have a user logged in per se. I set up a "fake" user in the Users database, which I could then manually authorise by using it's id
Auth::loginUsingId($user_id);
In this case, when I have verified their details via LDAP I manually authorise them using the dummy account. In this sense, every user who logs in with LDAP credentials is authorised as this single dummy user. While this works it means making a redundant User with false information and it is just generally quite messy. Furthermore, I lose the rich information the LDAP provides.
Effectively, I'd like to be able to authorise a user when they are not actually a User according to the Users table. I believe the solution may lie in altering the UserProviderInterface.php or the EloquentUserProvider.php.

Django single sign on and Php site: cross domain login?

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.

Categories