djoos / EscapeWSSEAuthenticationBundle

Symfony bundle to implement WSSE authentication
http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html
137 stars 59 forks source link

client implementation #9

Closed achecinski closed 12 years ago

achecinski commented 12 years ago

I have been struggling to implement a working client. I'm trying to implement a client where the user enters his username and password and it's used to create the digest for each wsse request. The problem is that the password is encoded and salted in the database (I user fosuserbundle to handle users). The one entered by the user is plain. When I compare the digests it never works. The one generated on the clien side uses plain password, the one build on the server uses the encoded and salted password. I havent't been able to figure out how to build the proper digest on the client's side. The client has no information on how the password is encoded and can not retrieve the password's salt. Can anyone help me?

djoos commented 12 years ago

Hi achecinski,

thanks for getting in touch!

Would you be able to explain a bit more in detail what you're trying to achieve? What is the exact role of your "client" and how exactly do you want this application to authenticate against another application via WSSE?

As far as I understand, you only have the username and plain text password on your client side of things, without knowing how to encode/salt the password. Just a couple of quick ideas:

Thanks in advance for your feedback!

Kind regards, David

achecinski commented 12 years ago

Thanks for your answer.

We (my company) have to build multiple clients that will consume rest services. We're building a web client and a desktop application. Some of the services need to be secured and we're thinking about using wsse to secure the transactions between the clients and the servers.

If I understand correctly, your first proposition is to send the username and password in plain text with every request (with maybe tls to cypher the info) and handling the digest generation on the server side. If I understand correctly, this solution is not working for me, as the interesting part in wsse is not sending plain information.

The second solution works better for me, this is something I already thought about. I was just wondering if there was something faster and easier to implement.

Thanks again, BR, Alex.

djoos commented 12 years ago

Hi Alex,

thanks for the extra information!

For the first idea I was actually still suggesting the digest to be generated on the client side though - definitely don't just send plain passwords over the wire! eg. $digest = $this->generate_digest($nonce,$created,$secret);//secret = plain text password $x_wsse = 'UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"'; ...and then handle the username/password combination vs. the database (FOSUser) on the server.

This is actually just how WSSE works, the only extra/custom bit you'll have to take care of is making sure this information that is sent over in the digest can be dealt with via a security provider. Eg. the in_memory one could deal with it as follows: providers: in_memory: users: djoos: { password: 'thisismyplainpassword', roles: [ 'ROLE_USER' ] } achecinski: { password: 'thisisyourplainpassword', roles: [ 'ROLE_USER' ] } In Escape\WSSEAuthenticationBundle\Security\Authentication\Provider\Provider.php $user->getPassword() will get the password for the user from the security provider and validate the digest.

Key to your solution will be to make sure you've got a provider that will check the FOSUser data. Unfortunately I haven't got much FOSUser experience, but I don't think it will take lots of time to put this together... As a matter of fact, if I'm not mistaking your security.yml will have the following: providers: fos_userbundle: id: fos_user.user_manager

So, $user->getPassword() shouldn't be a problem at all - it might be worth putting some debug into Escape\WSSEAuthenticationBundle\Security\Authentication\Provider\Provider.php and see what $user->getPassword() returns and find out why the digest doesn't match up.

Keep me updated as in the very near future we will be working on something very similar as well! :-)

Kind regards, David

achecinski commented 12 years ago

I know it will work with the in_memory provider as the passwords are stored in plain text. The thing is that with FosUser the password is salted and encoded before being stored in the database. Here is the default algorithm used to encode the password.

public function encodePassword($raw, $salt)
    {
        if (!in_array($this->algorithm, hash_algos(), true)) {
            throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
        }

        $salted = $this->mergePasswordAndSalt($raw, $salt);
        $digest = hash($this->algorithm, $salted, true);

        // "stretch" hash
        for ($i = 1; $i < $this->iterations; $i++) {
            $digest = hash($this->algorithm, $digest.$salted, true);
        }

        return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
    }

FosUser default params are sha512 as algorithm and about 5000 iterations. When you call $use->getPassword() with fosuser bundle, you get the encoded password from the database. The plain text password is never persisted. As you can see it's (almost) impossible to retrieve the plain text password with fosuserbundle. For security reason, I can not allow myself to keen plain text password in the database. (I don't want to have the same problem as sony did with their playstation live :-) ).

djoos commented 12 years ago

Hi Alex,

:-)

Definitely: storing plain text passwords is a big no no! I think we've figured out by bouncing ideas off eachoter that idea #1 is not going to work...

The only other solution I see (back to idea #2) is to store a authentication token when the user logs in and to make use of this token as secret for the digest for WSSE purposes.

It might be interesting to have a look into OAuth instead, allowing users to approve application(s) to act on their behalf without sharing their actual password. https://github.com/hwi/HWIOAuthBundle (previously https://github.com/KnpLabs/KnpOAuthBundle)

Hope this helps!

Kind regards, David

achecinski commented 12 years ago

I have thought about using oauth. I have found a great tutorial on how to implement it with FOSOAuthServerBundle : http://blog.logicexception.com/2012/04/securing-syfmony2-rest-service-wiith.html I'am still working on how to use it with clients that consumes my rest webservices.

djoos commented 12 years ago

I'll close this one for now - don't hesitate to contact me if you have any other questions!

Kind regards, David