introduction to dAuth v0.3

By Dieter Plaetinck (Dieter_be)
This article introduces dAuth V0.3: the authentication (not authorisation) system with a focus on security, using techniques such as challenge-response, customizable multiple-stage password hashing, brute force (hammering) detection etc.

May i introduce... dAuth v0.3

dAuth is basically plug and play. get all the code that you'll find at the end of the article, execute the sql and you're all set. You can optionally tinker with the variables in the component to suit more specific needs.
You could actually stop reading here and just start playing with it. But if you're interested in the more in depth workings, read on.

Before you start

The dAuth system is based on the original "Challengeresponse authentication with fallback" article. This version will be referred to as dAuth v0.1 from now on.
http://bakery.cakephp.org/articles/view/128
I received quite some positive feedback about both v0.1 and v0.2 and it made me feel good. (thumbs up for the big chefs, they make this all happen!)

If you haven't read the previous article, i suggest you do it now, as i won't repeat all the necessary information here.

Keep in mind dAuth is only meant for authentication (is someone who he claims to be?) and not authorization (is someone allowed to do this or that?)

Also, don't use dAuth v0.1 or v0.2 code, it contains nothing that v0.3 doesn't have :) (apart from a few bugs ;-)

dAuth in depth

dAuth is a challenge-reponse authentication system.
Here is a diagramn of the transitions the password goes through.

Changes since previous versions



v0.3 has evolved quite a bit since v0.1.


Genericification (if that's a word)

Let's start with the most simple change. v0.3 is more generic then v0.1 because...
  1. i tried to organise the code in a way that makes most sense (put the auth logic in a component, let the users controller make use of the component, have the right functions in the right places (eg moved some logic to models memberfunctions)
  2. i replaced some view code with calls to cake's helper functions. This decreases performance a very little bit, and makes the code a tiny little bit more complicated, but the code is more generic, and that's what this is all about. (cheers Robert and Andy Dawson for the hints)

Detection and handling of brute-force attempts (hammering)

When someone tries to login, a new entry in the hosts table is created, with his ip-adress in it, or an already existing one is retrieved. The behaviour of this host is checked (using the $hammerRatio variable) and if needed, the host is denied access and put in a blocked status. It's possible to immediatly die() the execution if the host is blocked, or hammering is detected. (you choose this all with $diehammer and $dieblocked)
The blocking is effective for the timespan you define in $blockTime.
When discussing this with Crazylegs (othman ouahbi), he mentioned he has a similar system, but uses cookies to do additional checking: when bots/scripts would change their ip-adress, they would still have the cookie, and the auth system could detect this. Personally i'm not sure about the benefits of such an approach (as i doubt if scripts/bots just accept cookies, especially if they are smart enough to change ip's. i could also imagine several servers that try to enter many websites and switch their task to the other server when they get blocked, in this case it wouldn't work too) but it certainly is worth investigating.

Since i don't like to depend on crontabs, old (unneeded) loginAttempt records are deleted about every once in 500 http login requests with the dAuth _cleanUpAttempts() function.

Preventing users transmitting plaintext passes (disabling fallback)

set the $allowClearText variable in the component and you're set! how easy can it be :)
If $allowClearText = true; then the default form will be working, the user can fill in his details (username, password) and submit the form. Upon submit, if he has javascript, a hash will be created of the password, and the password itself will be replaced by a dummy string.
If he has no javascript, he will transmit the pass in cleartext, and the system will accept the login if the information is correct.

If $allowClearText = false, however, there will be a form with empty action and method fields, and with style set to invisible, accompanied by an error that the user should get javascript support if he wants to login. If he doesn't have javascript enabled, he will thus only see the error, and not the form.
However, if he has javascript enabled, the contents of the error will be emptied, the form method and action will be correctly filled in, and the form will be set to visible. (all happening immediatly at page-load) This results in exactly the same form as he would otherwise get with $allowClearText set to true. This way, the user can login normally, with the hashing and other fancy features enabled. :-)

Session hijacking detection & handling

This is an aspect where cake already handles quite a lot, by providing a decent session class with issues like fixations and impersonations already in mind.
I found a great article about security issues arising from using sessions: http://phpsec.org/projects/guide/4.html it mentions 4 aspects regarding session hijacking:
  1. Session id prediction: This means just guessing the identifier, and is a pretty unsuccessfull approach to session hijacking. Id's are randomly generated, and consist of many digits, so it's very unlikely to guess a valid session identifier. Even if found one, it's probably of not much use because you will need to do more to hijack the session. (see below) and the chances that your guessed (valid) id is actually the one belonging to your target, is also pretty small.
  2. Session id capturing: session id's are transferred via GET variables or cookies, data which is sent in cleartext over the network, and could be sniffed by bad guys, or retrieved via bugs or potentially other methods. I guess it could even be done serverside if you're on a shared host: a crewmember could retrieve your session files/entries in the database. I should look into this issue further, but keep in mind that getting the valid session id (with this method, or the id prediction method or the fixation method below) is not the only step towards a successful hijacking. read on...
  3. Session fixation: The key here is that a malicious user passes you a url with a (randomly chosen) session id already in it, if you keep that session alive by browsing the site and logging in, the malicious user knows a valid session id by which he could hijack your session. The article recommends generating a new random id when the user logs in. CakePHP is our buddy here because it systematically generates a new session id each http request, totally crushing fixation attempts! This also has the side-effect of making a successfully retrieved session id only valid for a very short timespan: the time between successive http requests or in worst case, the maximum session valid time in case the "last" session id is retrieved. This valid time is defined in the config file. see http://manual.cakephp.org/chapter/configuration (it is also used to delete the serverside tmp session files after expiring)
  4. Target impersonation: So, if the malicious user retrieved your session id one way or another, he should not be able to hijack your session. How can be checked if a request with a valid session id is coming from a good guy are a bad one? By checking not only the session id, but also additional parameters like the user agent. Again, cake spoils the fun and takes away any need to program this in the application code ( ;-) ) It has hijack-protection built-in by checking the user agent. However, personally i think this could be improved, because it's isn't very hard for a malicious user to try some known user agents, especially since some are very popular (most targets probably use MSIE6 for example, and all the frequently used user agents are not only known (they are everywhere on the internet), they are also a select few.) A system that would block users that had more then one user agent in a short timespan would be of use here i think. (or let them auth again). Even more, i would just use the ip-adress as additional criteria to check your user against. (this is not recommended by the article, but personally i've never seen one user having more then one ip-adress in successive http requests. But even if this would be the case, letting the user login again is just a small sacrifice. On the other hand, the chance that the good guy and the bad guy share the same ip is very small, and faking an other ip adress is an extra step that makes it even harder to take-over a session (maybe something to think about for later cake versions, activating ip-check when security is set to high?)

Small changes

  1. I've changed the name of the encrypt() function to hash(), because strictly speaking, that function isn't really encryption because by definition, in that case decryption should be possible. It's rather hashing, which is irreversible (which doesn't mean that one can't find passwords for a given hash, see previous article. But this can take very long, especially if you make this function more complicated then using plain md5's or sha's)
  2. You'll see that hash() is no longer just a sha1 hash. To protect against password-retrieval efforts i added a salt. And not just a static salt, since the salt is the first letter of the password itself, the salt is customized for each password which makes using password-cracking tools harder :-)
  3. I improved the error reporting mechanism a bit. Different errors while processing the user input in the business layer will result in warning messages that are directly linked to the specific problem.
  4. and more... (see for yourself :)

These were all changes from v0.1 to v0.2
There are also quite a few specific additions specific to v0.3, you'll see those at the specific pages mentioned below.


Installation

Option 1

Here are the pages with the code:
  1. views, usersController and javascripts http://bakery.cakephp.org/articles/view/152
  2. component http://bakery.cakephp.org/articles/view/153
  3. helper http://bakery.cakephp.org/articles/view/154
  4. models http://bakery.cakephp.org/articles/view/155

Option 2

you could also just grab the code from these urls:
  1. views/users/login.thtml: http://bakery.cakephp.org/articles/download_code/152/block/1
  2. views/users/register.thtml: http://bakery.cakephp.org/articles/download_code/152/block/2
  3. views/elements/userinfo.thtml: http://bakery.cakephp.org/articles/download_code/152/block/3
  4. views/users/change_password.thtml: http://bakery.cakephp.org/articles/download_code/152/block/4
  5. controllers/users_controller.php: http://bakery.cakephp.org/articles/download_code/152/block/5
  6. webroot/js/d_auth.js: http://bakery.cakephp.org/articles/download_code/152/block/6
  7. webroot/js/sha1.js: http://bakery.cakephp.org/articles/download_code/152/block/7
  8. controllers/components/d_auth.php: http://bakery.cakephp.org/articles/download_code/153/block/1
  9. views/helpers/d_auth.php: http://bakery.cakephp.org/articles/download_code/154/block/1
  10. models/user.php: http://bakery.cakephp.org/articles/download_code/155/block/1
  11. models/host.php: http://bakery.cakephp.org/articles/download_code/155/block/2
  12. models/login_attempt.php: http://bakery.cakephp.org/articles/download_code/155/block/3
  13. SQL code: http://bakery.cakephp.org/articles/download_code/155/block/4

Don't forget you need to follow cake's conventions!http://manual.cakephp.org/appendix/conventions

Configuration

Take a look at the variables in the component, change to your likings and you're all set!

Final words

You'll see that i've added a little bonus, a basic (no captcha or other fancy stuff yet) /users/register and /users/changePassword form/action, and a userinfo element which strictly speaking can't be part of an auth-system, but since it they integrate so nicely with the auth system and share some logic i just have to do it ;-). Even if you don't want any visitor to register on your site, you can limit this action to yourself to easily create new users. (the right component's stage1Hash() function is called for you so the perfect hash is put in the database) Ironicly enough, when filling in the register or change password form, the passwords can not be hashed using stage2Hash, since this hashing manner changes all the time and the server would not be able to know the original stage1 hash that is the "source" of the stage 2 hash. And the database needs to store the stage1 hash so... If anyone would sniff the network, and if they are smart enough, they could enter the sniffed hash in the form, modify the javascript code a bit to start from the stage1 hash (which would be created anyway) instead of the original password, in order to generate valid responses for the challenge/response system. Https would certainly be in its place here, or otherwise a customized encryption system so that the server could decrypt the password.




Also, keep in mind that i'm just human. Humans make errors, especially humans like me! Take a look at the code, try some stuff out, and give us some feedback, thanks! (especially the (timing stuff in) the host behaviour checking could use some attention)

To-do

  1. Look into session id capturing, how it can be done, if cake does something about it, or if i should do it
  2. Consider using cookies as add-on for hammering detection (with possibly denying users if they disable cookies, or using a fallback for these users, with the same security issue remaining)
  3. Find out how i can request the SSL layer if it's available
  4. Find out how can encrypt and decrypt efficiently for using the register and password-change forms.

 

Comments 147

CakePHP Team Comments Author Comments
 

Bug

1 MySQL version limitation

I've found many MySQL deployments to be quite ancient. For example, a major hosting company like 1and1.com still uses
version 4.0.27. Unfortunately, "collate utf8_bin" is only supported under 4.1 and later.
Posted Nov 21, 2006 by Ed
 

Comment

2 fixed

I've found many MySQL deployments to be quite ancient. For example, a major hosting company like 1and1.com still uses
version 4.0.27. Unfortunately, "collate utf8_bin" is only supported under 4.1 and later.

good point, i removed those pieces. they shouldn't be necessary anyway.
Posted Nov 21, 2006 by Dieter Plaetinck
 

Question

3 Error after install

Could anyone please help. I keep getting this error on the page after installing this:

Fatal error: Class 'L' not found in /media/docs/krowe/html/cTrack/cake/libs/model/model_php5.php on line 481

Contents of model_php5.php
function __constructLinkedModel($assoc, $className)
{
$colKey = Inflector::underscore($className);
if(ClassRegistry::isKeySet($colKey))
{
$this->{$className} = ClassRegistry::getObject($colKey);
}
else
{
$this->{$className} = new $className(); // <-- this is the line that throws the error
}

$this->alias[$assoc] = $this->{$className}->table;
$this->tableToModel[$this->{$className}->table] = $className;
$this->modelToTable[$className] = $this->{$className}->table;
}

I am very new at using this but I have a few pages working on my site and believe I have my routes properly setup, but just in case here they are:
$Route->connect('/users/newpass/', array('controller'=>'users', 'action'=>'changePassword'), 'change_password');
$Route->connect('/users/register/', array('controller'=>'users', 'action'=>'register'), 'register');
/* $Route->connect('/users/logout/', array('controller'=>'users', 'action'=>'logout'), 'login'); */
$Route->connect('/users/*', array('controller'=>'users', 'action'=>'login'), 'login');

Any help would be appreciated. Thanks.
Posted Nov 25, 2006 by Kevin Rowe
 

Question

4 User check

Neat work. Got it up very quickly. Now do you have implemented a helper function to do like this?

loggedIn()): ?> Stuff for authed users

It would be really sweet, and it'd make it easy to actually use this stuff. Also, it would be great to have functions to retrieve the actually logged username and stuff. Or just tell me how to get it done, and I'd do it myself.

Thanks a lot.
Posted Nov 25, 2006 by Chris Hoeppner
 

Comment

5 reply to Chris and Kevin

Chris, I've written a small fix for you (i will probably add this in future releases)
in the dAuthComponent add this at the end of the startup function:
[code php] $User = $this->Session->read('User');
$this->controller->set('User',$User);
This sets the userdata from the session available in your presentation layer. (Like this you don't even need helper functions, you could for example put something like this in any of your views, layouts or elements:

View Template:


<?php
if($User)    echo ('Logged in as '.$User['username']);
else        echo 
'not logged in';
?>
Posted Nov 26, 2006 by Dieter Plaetinck
 

Comment

6 reply to Chris and Kevin part 2

Kevin, it seems like cake is searching for a class named L. I have no idea what might cause that issue. (as i never called a class L or similar). at which pages/controller-actions does it occur? only actions involving dAUth logic? only pages using the UsersController? or...? we need more info. Doublechecking that you saved all the right files to right directories (and the filenames are correct) is also a good idea.
Posted Nov 26, 2006 by Dieter Plaetinck
 

Bug

7 undescore missing

There's a bug in d_auth.php line 269:

It reads:
$hosts = $this->controller->Host->findAllByIpAdress($ip);

Should be:
$hosts = $this->controller->Host->findAllByIp_Adress($ip);

since your field an has an underscore in it.

Great component!!
Posted Nov 28, 2006 by Othman ouahbi
 

Comment

8 bug fixed

you're right. This is a php4 issue that is mentioned in the groups, but not (yet) in the manual.

more info:
http://groups.google.com/group/cake-php/browse_thread/thread/859d121726ece828/b062a3db8c435c32 https://trac.cakephp.org/ticket/1567
i'll add it as a comment in the component and include the fix in future releases.
Posted Dec 3, 2006 by Dieter Plaetinck
 

Comment

9 Not a bug

I don't believe that's a bug. Adding the underscore cause this error:

SQL Error in model Host: 1054: Unknown column 'Host.ip__adress' in 'where clause'

Works for me as is... thank you Dieter.

There's a bug in d_auth.php line 269:

It reads:
$hosts = $this->controller->Host->findAllByIpAdress($ip);

Should be:
$hosts = $this->controller->Host->findAllByIp_Adress($ip);

since your field an has an underscore in it.

Great component!!
Posted Dec 10, 2006 by Veronica Gonzalez
 

Comment

10 camelcase vs underscores

as is: works on php5, not on php4
proposal by othman: works on php4 (i think, can't check), not on php5

fix that i posted: works on php4 and php5 ( http://bakery.cakephp.org/articles/view/153 )
Posted Dec 11, 2006 by Dieter Plaetinck
 

Comment

11 Great news

hey guys.
I fixed all found bugs and added some features. I also updated the article a bit.

Have fun with v0.3 !

Dieter
Posted Dec 16, 2006 by Dieter Plaetinck
 

Bug

12 Some minor bugs

Downloaded v0.3 and upgraded from v0.2

Some extra bugs to report:

Under d_auth.jsp doStage2 function:
current: var hash = stage2Hash(stage1Hash(password) + salt);
Should be: var hash = stage2Hash(stage1Hash(password),salt);

Took me few hours to find this problem. Do change it asap.

Under users_controller.php:
For those who does not have it, add the RequestHandler Component with:

var $components = array('dAuth','RequestHandler');

And the current syntax of the RequestHandler method calls are in lower case.

current: $this->requestHandler->getClientIP()
change to: $this->RequestHandler->getClientIP()

That is all. Hope these changes are useful.
Posted Dec 19, 2006 by redhex
 

Comment

13 oops

that's odd, those are all bugs that i found (and fixed) while developing. I was sure i put the latest code on the bakery but i guess i overlooked something.
I'll fix it right now!
Posted Dec 19, 2006 by Dieter Plaetinck
 

Comment

14 Javascript not enabled warning in IE

Dieter,
Pardon it is me again. In IE, the warning:

"Login impossible. For security reasons, you should enable javascript."

appears even when IE has javascript running. Firefox works nicely. I am not too sure is it me and my whole office, we does that the javascript no enabled message.

A work around was to change the message from a

to a .

Under dAuth helper formheader function:

Current:
$output = "

$action $this->noClearTextErrorMessage

";

Change to:
$output = "$action $this->noClearTextErrorMessage";

It works and I guess there should not be much side effect to the display. Hope this is useful again.
Posted Dec 19, 2006 by redhex
 

Bug

15 Minor bug

Notice: Undefined property: dAuthComponent::$allowCleartext in C:\Arquivos de programas\Apache Group\Apache2\htdocs\ismael\esite\app\controllers\users_controller.php on line 81

Simple put the letter 't' to uppercase
here:
$this->set('allowcleartext', $this->DAuth->allowCleartext);
to
$this->set('allowcleartext', $this->DAuth->allowClearText);
^
Posted Dec 20, 2006 by Ismael S. Kafeltz
 

Bug

16 bugfixes

Thanks for the find Ismael :) Hope you like the system!

Redhex, i tried ie 5,5.5 and 6, and with all the versions i get the error message, but that might have something to do with the fact that i have problems enabling JS in my IE browsers (on linux) ;-) I think it's very strange that the showing or not of an error message is defined by the use of a html tag... But i'll keep an eye open ... ;-)

So ftr,
bugfix #1 for v0.3: users_controller.php line 80/81: change allowCleartext to allowClearText

Dieter
Posted Dec 20, 2006 by Dieter Plaetinck
 

Comment

17 Re

The download links not working is because somebody messed with bakery's code: https://trac.cakephp.org/ticket/1810
As for the "headers already sent" error.. check line 441 of cake/libs/controller/controller.php, something must be causing some output
I have no idea what version of cake you use so i can't check for you

Posted Dec 28, 2006 by Dieter Plaetinck
 

Question

18 Headers already sent error

As for the "headers already sent" error.. check line 441 of cake/libs/controller/controller.php, something must be causing some output
I have no idea what version of cake you use so i can't check for you

Thanks for the speedy response.

Sorry, I forgot to say which version I was using. I am using the latest released version of Cake - v1.1.12.4205 under xampp and Windows XP.

The line 441 in controller.php is the line in the redirect function which does "header('Location: ' . $url);"

The data already sent to the page comes from cake\libs\view\view.php:308 which is in function render which does a "print $out;".

This, I believe, is called by the controller->flash when the registration is successful on line 195 of the component d_auth.php

The other major problem I have is that the hashed password stored in my database is the same regardless of what password I enter for registration (and any subsequent login fails).
Posted Dec 28, 2006 by Paul Venkatesh
 

Bug

19 SHA1 .js problem

The other major problem I have is that the hashed password stored in my database is the same regardless of what password I enter for registration (and any subsequent login fails).
Ok. I seem to have resolved the hash problem. It appears that the implementation of the sha1 algorithm in sha1.js at http://bakery.cakephp.org/articles/view/152 is now not the same as that by Chris Veness at http://www.movable-type.co.uk/scripts/SHA-1.html. If I replace the algorithm in ths d_auth sha1.js with this implementation, the hash does change and the login does work.
Posted Dec 28, 2006 by Paul Venkatesh
 

Comment

20 Re

Ok. I seem to have resolved the hash problem. It appears that the implementation of the sha1 algorithm in sha1.js at http://bakery.cakephp.org/articles/view/152 is now not the same as that by Chris Veness at http://www.movable-type.co.uk/scripts/SHA-1.html. If I replace the algorithm in ths d_auth sha1.js with this implementation, the hash does change and the login does work. Well i guess we can blame 2 bakery bugs for this :)
the first bug is the one that doesn't allow you to download the "real" code https://trac.cakephp.org/ticket/1810 the 2nd bug is the one that considers some characters as tokens to start a change in layout, hence not only *** the layout of the code, but also leaving out a few chars from the code. https://trac.cakephp.org/ticket/1663 (i've put a warning on that bakery page that you shouldn't copy paste the code like that but i guess i should have been more clear)
Posted Dec 29, 2006 by Dieter Plaetinck
 

Comment

21 Shouldnt Authentication

has a CAPTCHA imagem to help?
like this one: http://www.ejeliot.com/pages/2
I think this would help, wouldn't?
Posted Jan 1, 2007 by Ismael S. Kafeltz
 

Comment

22 Captchas

Captcha's have their uses.. (especially at the stage of registering accounts). For the logging in on itself they aren't of much use as a decent anti-hammering system can do wonders. (and checking for hammering behaviour is much more usefull then using a captcha on a login form, imo, not to mention that captcha's on login forms would imply an unnecessary strain on real users)
I will maybe implement them someday, but captcha's are -to me- a feature with low priority, as there are aspects that are much more important (and often neglected) when focusing on a decent, secure authentication system. Not to mention that actually, they just don't have much use for authencation...
Posted Jan 1, 2007 by Dieter Plaetinck
 

Bug

23 form data is never empty

Testing the user function has turned up what I think is an problem with the way the login form's hidden elements are being loaded prior to submission and the use of the "if (!empty($this->data))" test.
If you set debug to dump the objects it is clear that the hidden elements hold data

[form] => Array
(
[data] => Array
(
[User] => Array
(
[username] =>
[password] =>
[hashed_pw] => 8930d9ac7e3b2cdfe67b22e71b9022a4036e9a67
[special_sauce] => 261053480
)
)
)
Looks like ($this->data) will always contain hashed_pw and special_sauce.... or am I missing something?

Posted Jan 4, 2007 by Michael Clarke
 

Comment

24 data checking

I assume that with "user function" you mean the /users/login action...

The if (!empty($this->data)) test, is to check whether the action is called for the first time (the user hasn't filled in any form and wants to login) or if he is sent there from a form.

If the user is coming from the form, special_sauce will always be filled, hashed_pw will be filled if javascript is enabled clientside.

If the user just goes to users/login for the first time (without filling the login form), $this->data will be empty.


Deeper checking of the contents of $this->data happen deeper inside the component (eg checking username, password,...)

Dieter
Posted Jan 4, 2007 by Dieter Plaetinck
 

Question

25 Email as username

Hi
Great work. Really like what your doing :)

I often uses an email address instead of an username.
It would be nice if you implemented a feature in the component so you could switch between email and username as login credentials.

Now I have to change this every time you release a new version.
Posted Jan 5, 2007 by Henrik Persson
 

Comment

26 user choosable identifiers

Good idea.. i'll think about this for the next version :)
Dieter
Posted Jan 6, 2007 by Dieter Plaetinck
 

Bug

27 No login details slight modification

If the user hits immediately while getting to the form(stupid but needs to be detected), the user gets a PHP error that postUser is not set. The solution is outlined below:

###############
AT THE END OF

function attemptLogin($postUser = null,$ip = null)
{
$success = false;
$cleartext = true;
...
if($cleanHost)
{
// Changed from 0.2
if(is_array($postUser) && !empty($postUser) && isset($postUser['User']) &&
isset($postUser['User']['username']) && isset($postUser['User']['password']))

Change this to:
if(!empty($postUser) && isset($postUser['User']) &&
isset($postUser['User']['username']) && isset($postUser['User']['password'])) // note the is_array was removed

At the end of this if block, you must include the } else { statement.

.. which reads:

} else {
$this->error = $this->messages['empty_login'];
} // close post validation
} // close if CleanHost
return $success;
} // close function

Also, add this to the $messages variable:
var $messages = array( ...,
'empty_login' => 'Empty Credentials' );

######################
Changes I have also made in my version of your work (0.2):

1) Group rights, is the user allowed to use this admin/user function?

2) Email as the login information

3) Redirect to login page if you need to be logged in to view a certain page. After successful login, the login page redirects back to the same page.

I will post these 'add-ons' when I update to 0.3 and fix a couple bugs.
Posted Jan 6, 2007 by Chef Boyardee
 

Comment

28 no bugs but some good ideas

First of all, you should really upgrade to v0.3
I think everything you say has been fixed.

First of all: in v0.3 the userscontroller checks is $this->data is empty or not. if it's not empty it sends it to the component, after that the component does additional checks to see if $this->data is an array, if it contains elements , if $this->data['User'] exists etc etc. If $this->data is empty, it just shows a new login form. (or shows it again). Imho this is normal behaviour

Then your extra ideas:
1) dAuth is an authentication system, not an authorisation system. I recommend using cake's ACL-system to do access control. (along with one of the components that has been published on the bakery for example). The upcoming dAuth v0.4 will be perfectly compatible (and very easy to integrate) with adsix' acl component.

2) good idea, i'll implement it (see few posts higher)

3) Also a good idea, i'll implement that too.
Posted Jan 7, 2007 by Dieter Plaetinck
 

Bug

29 Problems with other languages than english

Hi Dieter,
May be you could modify the helper to separate the label and the id of the field
* In echo $dAuth->formPassword('Password','User/password'); Label and ID are the same, if I call the field "Mot de passe" (password in french), it cannot be encrypted.

Thank you again
Yann
Posted Jan 8, 2007 by Jean Carfantan
 

Bug

30 id of password in helper must be kept default

Good point.
I consider this a bug.
The api of the dAuth helper isn't very well constructed btw. I mean it works, and it is as secure as i wanted it to be (unless we discover more bugs ofcourse ;-)), but it isn't very flexible when it comes to choosing your own styles, error messages, or labels. You can easily fix/hack it yourself, but i will make it better in the next version :)

So ftr,
bugfix #1 for v0.3: users_controller.php line 80/81: change allowCleartext to allowClearText
bugfix #2 for v0.3: don't change the id of the password in the views, or make a little change to the helper to make it work.
Posted Jan 8, 2007 by Dieter Plaetinck
 

Bug

31 May be a little confusion

Thank you Dieter for your answer, yes you app works well and we are now just resolving some details, I will make the little change in the helper.
In the users_controller
you have var $helpers = array('Javascript','DAuth');
var $components = array('DAuth','RequestHandler');

I changed it into
var $helpers = array('Javascript','dAuth');
var $components = array('DAuth','RequestHandler');
to avoid any confusion between component and helper that generates an error.

Cheers

Yann
Posted Jan 9, 2007 by Jean Carfantan
 

Comment

32 Redirect to entry page after login

Reply to comment 27 and 28. For those who cannot wait, here are the small changes you can do to enable the user to be redirected back the page where they entered.

In the function checkSession which is in the appController

function checkSession()
{
// If the session info hasn't been set...
if (!$this->Session->check('User'))
{
$this->Session->write('login_referrer',$this->params['url']['url']);
// Force the user to login
$this->redirect('/users/login');
exit();
}
}

And in the login() function in the user controller we place the follow code:

if ($success){
// some codes you might have goes here.

if ($this->Session->check('login_referrer'))
{
$loginReferrer = $this->Session->read('login_referrer');
$this->Session->delete('login_referrer'); $this->redirect($loginReferrer);
return true;
}
// the line before is the original redirect code
$this->dAuth->redirect('login_success');
return true;
}

That should be all. I am looking forward to dAuth v0.4. It had been a really good component for me so far.
Posted Jan 9, 2007 by redhex
 

Comment

33 Reply to Dieter

Hi Dieter Plaetinck,

The above code works with the DAuth user views, but if I try to use the PHP code in the views of another model/ controller, it fails. Can you please guide me how to achieve that. I used the default blog tutorial and tried to add DAuth to it.
There is no associates of the blog posts with users as of now. Will adding that help or can I do it some other way so that any other views of different models can also access the same.

Cheers,
JKG
Posted Jan 9, 2007 by Jayant Kumar Gandhi
 

Comment

34 blog tutorial

I don't really understand your problem, can you give more information? Maybe do a post on the google group mailinglist and explain the problem there. I'm glad it works "in normal circumstances" but i don't understand why you would apply the code in other views or controllers. (the blog tutorial only has the Posts controller and views, auth logic doesn't belong there). You can link users to posts with the belongsTo and hasMany relations (see manual)
The elements however, are usable in any view or layout.
Posted Jan 9, 2007 by Dieter Plaetinck
 

Comment

35 Redirect to entry page after login (with variable passing)

in the app_controler.php ->>>>>>
function checkSession($vars = NULL) {
// If the session info hasn't been set...
if (!$this->Session->check('Customer'))
{
$redirect = array('link' => $_SERVER['REQUEST_URI'], 'vars' => $vars);
// Force the user to login
$this->Session->write('redirect',$redirect);
$this->redirect('/customers/login');
}
}


now add anywhere you want to check the session or pass the variables->>>
//Authenticate
$this->checkSession($_POST);


now to read the information when it comes back after login -->>>>>>
$redirect = $this->Session->read('redirect');
$this->set('redirect', $redirect);

if($redirect['vars'] != '' && $redirect['vars']['exampe_number'] != 0){
} else {
// user never sent data through the form (hack)
}
Posted Jan 9, 2007 by Chef Boyardee
 

Question

36 Header Issue

Not sure if a bug or not... but I haven't been able to find an answer, I am also getting the error about headers already being sent:

Warning: Cannot modify header information - headers already sent by (output started at /home/xxxxx/public_html/dev/app/models/user.php:39) in /home/xxxxx/public_html/dev/cake/libs/controller/controller.php on line 441
I can tell that where the error is actually being generated is when the redirect function is being called in d_auth.php. Looking at my source code I don't see and rendered HTML other than the error, so I am perplexed... any ideas??
Posted Jan 10, 2007 by Andy
 

Comment

37 Headers Followup

I forgot to mention... the error above is occurring after I login...
Posted Jan 10, 2007 by Andy
 

Comment

38 Header Issue

Andy,
In general, this problem rises up when you have a space before the first When you cut/paste some code, you often put an extra space.
Yann
Posted Jan 11, 2007 by Jean Carfantan
 

Question

39 May I have some salt

Hi Dieter,
Thank you for your availability as a 'chef pâtissier'. I understand well your clever distinction between login et transport but I scanned all your code and don't understand why I am not able to login on the login page, my credentials are always mismatched with the password on the database, I have a problem of salt - an important item to make a good cake... May be I did not pick up the good code or something... I give up ;=)
Yann
Posted Jan 11, 2007 by Jean Carfantan
 

Comment

40 headers already sent

Andy,
In general, this problem rises up when you have a space before the first When you cut/paste some code, you often put an extra space.
Yann
or any kind of whitespace (spaces,newlines,tabs,..) for that matter.
so Andy, check your user.php
Posted Jan 11, 2007 by Dieter Plaetinck
 

Comment

41 Salt

Jean, how do you mean "always" ? didn't you say a few comments back "yes you app works well and we are now just resolving some details"?

I also read you've been changing some code. Maybe try to remember if you made any changes to the code, and what they were (version control rules!).
If you can't figure it out i suggest you install it again from scratch, but fixing the 2 bugs as explained above. If then it still doesn't work post here again and try to be more detailed (which variables aren't set for example?) because without further information i can't magically guess what might cause your problem (especially since you seem to be the only one having this problem)
Also, are you sure there is the right hash in the database? did you change the stage1Hash or stage2Hash functions? if you change one of them in the component, you have to change it in the helper too, and vice versa.
Also, your hash in the database will become invalid if you change a hashing function.
Posted Jan 11, 2007 by Dieter Plaetinck
 

Comment

42 salt or pepper

Hello Dieter,
Sorry for my bad communication. I did not changed anything from your code, I just implemented it inside my app and all works well except that I can't log in.
My critical point is in the component :
$real_hash = $this->stage2Hash($dbUser['User']['password'],$salt);
$submitted_hash = $postUser['User']['hashed_pw'];
if($real_hash == $submitted_hash)
{
$success = true;
}
I get $success false.

I suppose that when I log in for the first time, I would not have to go through this part of code, this part is aimed for those who are logged in yet and have a session.
Is that correct ?
Thank you
Yann
Posted Jan 11, 2007 by Jean Carfantan
 

Comment

43 THANKS

Thanks Dieter, I had whitespace at the end of a couple controllers. I removed it and it's all better!

Great component... you are saving me WEEKS of agonizing development!
Posted Jan 11, 2007 by Andy
 

Comment

44 open source salt aka NaCl

Andy you're welcome, i love the principle of open source and i like giving back to the community.

Jean Carfantan : post a topic on the google group, i'll help you there, and be more detailed (maybe read a tutorial about debugging)
for instance, what are the values of $real_hash, $dbUser['User']['password'] and $submitted_hash? if we can't find the problem there, we'll dig deeper in the php/javascript code until we find it.
Posted Jan 11, 2007 by Dieter Plaetinck
 

Question

45 User variable

Dieter,

I'm using your most recent code. When I try and look at the $User variable in any of my views, it returns an undefined variable, even if I have logged in. I have checked and the two lines:

$User = $this->Session->read('User');
$this->controller->set('User',$User);

Are at the end of my startup function. Any ideas?

Thanks,
Andy
Posted Jan 11, 2007 by Andy
 

Comment

46 Session

Also, I checked my session variables and I do show that I logged in successfully, as my username is showing up in my session vars.
Posted Jan 11, 2007 by Andy
 

Comment

47 The whole process

Hi Dieter,

Thank you for your proposition. I thought about giving you the different values but I have here a problem of process or a problem of comprehension.

You have two functions :
* the first one uses the first letter of the password in clear as salt, then put it into the database
* the second one uses the crc32(time()) to generate a new salt to transport the password along the pages

When you log in you need the first algorithm to compare with the hash inside the database and you need the second one to put a new and more secure hash inside the session.

My problem here is that it is the second hash that is compared with the hash inside the database.

Tell me, Dieter, if my argument is correct.

Jean

Posted Jan 12, 2007 by Jean Carfantan
 

Comment

48 userdata in view and an explanation about the hashing

Andy if you want userdata available in your views throughout your entire site, i think you need to put something like this in your app_controller's beforefilter (i don't have time right now to spit through my code). there might be a better way to implement this (eg by calling dAuth's functions) but for now this will work like a charm.
$sessionUser = $this->Session->read('User');
if(!$sessionUser || !is_array($sessionUser) || empty($sessionUser))
{
$sessionUser = false;
}
$this->set('User',$sessionUser);


Jean we don't "transport a password along pages". We transfer it from client to server.
And "the second function" aka stage2Hash doesn't call crc32(). The component does this on its own, then it's put in the session and later on it's retrieved from the session to call stage2Hash()

And no, there are several cases:
1) no javascript enabled @ client+ fallback not allowed -> user doesn't get a valid form, gets error message and can't login.
2) no javascript enabled @ client, but fallback is allowed: the password is sent in cleartext over the network, the server receives this, and does a stage1Hash() on it to compare it with the stage1 hash stored inside the database
3) javascript enabled @ client: javascript computes the stage2 hash in 2 stages (first stage1, then stage2, based on the salt that it gets from the server), then sends this stage2 hash to the server. the server grabs the salt from the session and does a stage2Hash() on the stage1 that it retrieves from the databases, and then compares the stage2 hashes.

There is no such thing as "making the password more secure to put it in the session", it works just like described above, the session doesn't have much to do with it (it does contain the userdata that is retrieved from the database, which includes the stage1 hash.)

I reviewed my login code btw and i don't see where it would compare a stage2 hash against the stage1 from the database. Please tell me at which point this happens.
Posted Jan 12, 2007 by Dieter Plaetinck
 

Comment

49 My data

Thank you Dieter for your clear explanation.
Here is my data
username = ravel
password = pavane

I get this
Real hash : 903a6e1948193c9da492c4a06aa571816b0f274a
Submitted hash : b174737ff0ba3f2ce29f20ec792ac25209fb76b9

Jean
Posted Jan 12, 2007 by Jean Carfantan
 

Comment

50 google group

Dude start a thread on the google group, we will help you there
Posted Jan 13, 2007 by Dieter Plaetinck
 

Question

51 A couple of questions.

I moved the login form to an element.

I also added the following block to my pages controller.
$error = '';

if (!empty($this->data))
{
$success = $this->DAuth->attemptLogin($this->params['data'],$this->RequestHandler->getClientIP());
if($success)
{
$this->DAuth->redirect('login_success');
return true;
}
else
{
$error = $this->DAuth->getErrorMessage();
if(!$error)
{
$error = $this->noReason;
}
}
}
$this->DAuth->newSalt();
$this->set('error', $error);
$this->set('allowcleartext', $this->DAuth->allowClearText);

I also check for whether or not I am currently within the users controller in my default layout. If I am *not* and the user is *not* logged in I include the login form. If they are in the users controller or are logged in they see their user info. I did this because having the javascript included in the element was interfering with the javascript in the register form. ( I assume I was regenerating a hash that shouldn't have been regenerated )

This works fine in that it registers users and logs in and out just fine. The one *bug* or issue I found and would like help with is that once registration is complete I get a message ( with debug > 0 ) that I am missing a ../layouts//.thtml file - Yes, 2 slashes and a blank filename. It also appears the redirect stopped working for some reason ( or it is not succeeding? )

The user accounts *are* created and work fine.
Any ideas or instructions on how to include a login form on all pages if the user is logged out and their info when they are logged in without breaking the hashes or registration form?

Thanks
Abba Bryant
Posted Dec 31, 1969 by Abba Bryant
 

Comment

52 userinfo element and using form throughout entire site

If you want a login form on all pages, you need to put the same logic that is performed in the userscontrollers' login action in your appcontrollers beforeFilter (or beforeRender).
Not necessarily the whole action (which also checks if post data coming from a login-form is included) but at least the last 3 lines:
$this->DAuth->newSalt();
$this->set('error', $error);
$this->set('allowcleartext',$this->DAuth->allowClearText);

This will do the necessary preparations to make a login form work.
(obviously the DAuth component must be available in the AppController for this)

If you want to show userdata I recommend using (and modifying until it does what you want) the userinfo element you can find here:
http://bakery.cakephp.org/articles/view/152
As for your layout problem, this is about code that you wrote, so if you want help with i recommend you create a thread on the google group and post at least your code.
Posted Dec 31, 1969 by Dieter Plaetinck
 

Bug

53 login form is not xhtml 1.0

* Tag form requires attribute "action" to be defined
Posted Dec 31, 1969 by anshul
 

Comment

54 ToDo 3 request SSL if it is available

Not sure if you are wanting to check from inside a page if SSL is available (e.g. via "AJAX"), or if you just want to know if you are being called under SSL (potentially slackening client-side javascript encryption?), but if it is the latter something along these lines should do:

echo $_SERVER['SERVER_PORT'];

HTTP = Port 80
HTTPS (SSL) = Port 443
Posted Apr 11, 2007 by Chris Eaton
 

Comment

55 Thanks

I have tried othAuth,obAuth,YACCA and dAuth,dAuth is the only one that can work after I setuped step by step,thanks!
Posted Apr 11, 2007 by wjz
 

Comment

56 reply to wjz and Chris

@wjz: no problem. Although, DAuth is meant to be as secure as possible, and user-friendlyness is only a second goal. Funny that you found DAuth to be the most easy one.

@Chris: thanks for the idea, but more then that needs to be done (to "check" if it's avaible), I was thinking about trying a redirect to a https://* url, but I haven't yet been busy with this idea, it will come though... :-)
Posted Apr 13, 2007 by Dieter Plaetinck
 

Comment

57 repeat password

Hi, I've been trying to add a repeat password field when registering and changing password so the user has to type it again to avoid wrong password entry. Where do you think is better to put the validation for that?

Since the password is changed into the hash when it gets to the server then i guess it has to be on client side with javascript, or should I hash both fields and validate serverside?

I would be great if this could be on the next version ;)
Posted May 2, 2007 by Carlos Zuniga
 

Comment

58 repeat password

Hello Carlos.
First of all you should hash both fields. It wouldn't be very secure otherwise ;-)

I'm not much experienced with custom validations, but I think writing a function (or overriding an internal one) for this in the model is a good idea: just to check if the hashes (or cleartext passes, when submitted in cleartext with fallback enabled) match and invalidate the model when they don't. then you'd just show the form again with an error message.
Posted May 3, 2007 by Dieter Plaetinck
 

Comment

59 Error in host.php model file

Hello,
I think, in file host.php is mistake at this (cca 81) line:

$attempts = $this->LoginAttempt->findCount(array('host_id' => ' = '.$data['Host']['id'],'LoginAttempt.created' => '>= '.gmdate("Y-m-d H:i:s", $limit)));

generated this SQL (in mssql) wrong question:
SELECT COUNT(*) AS count FROM [login_attempts] AS [LoginAttempt] LEFT JOIN [hosts] AS [Host] ON ([LoginAttempt].[host_id] = [Host].[id]) WHERE [host_id] = ' = 31' AND [LoginAttempt].[created] >= '2007-05-03 21:22:06'

There is problem here : "WHERE [host_id] = ' = 31' "

I think that the line sould be:
$attempts = $this->LoginAttempt->findCount(array('host_id' => $data['Host']['id'],'LoginAttempt.created' => '>= '.gmdate("Y-m-d H:i:s", $limit)));

Thanks for this auth component Dieter.

Mareg

Posted May 3, 2007 by Marek Grossman
 

Comment

60 bugfixes

Hmm seems reasonable..
What version of cake are you using btw? Maybe this behavior has been changed in the unstable 1.2 tree

This has been reported for postgresql too btw. check comment # 2 on this page: http://bakery.cakephp.org/articles/view/dauth-v0-3-models
the funny thing is that his fix is just removing the space before the equal sign, while your fix is removing the equal sign all together. I'll look further into this to find the approach that should work on all databases, although i assume that both fixes work equally well. (i think cake will check if there is an equals sign (without space before it) and if it isn't there add one automatically)

So ftr:

bugfix #1 for v0.3: users_controller.php line 80/81: change allowCleartext to allowClearText
bugfix #2 for v0.3: don't change the id of the password in the views, or make a little change to the helper to make it
bugfix #3 for v0.3: remove default '0000-00-00 00:00:00' from sql of models.
bugfix #4 for v0.3: change 'host_id' => ' = '.$data['Host']['id'] to 'host_id' => '= '.$data['Host']['id'] (remove the space before the equals sign)
Posted May 4, 2007 by Dieter Plaetinck
 

Bug

61 Blank password is not detected in register function

hi,

On the register view, if I enter all but Password field (leave empty), the registration is successful when it should not be. Looking at the database, there is a hashed password being stored.

1) Is the JS code is generating a hashed password even when the password field is blank? Aka, using blank as a valid value? Because in the database, I always see the same hashed value.

2) In your dAuth component, attemptRegister function, you are not catching an empty password field. For example:
$this->controller->User->create();
$user['User]['email'] = $postUser[user] $user[user][password] = $hash

In this last statement, you should first check to see whether $postUser[email][password] was set and has contents. It's only if then, the PostUser[user][hased_pw] is a hashed equivalent of something other than a blank password.

Please let me know if my analysis is on the right track.
Cheers.

Posted Jun 4, 2007 by Vaz
 

Comment

62 Strange IMG tag bug

Today I struggled with dAuth to make it work as an element.

DAuth sets a random number in $this->Session->write('salt', $salt); and when I read that key with $this->Session->read('salt'); after I posted the form it was a different number then before the post. But this was only when I called it from the controller /newsarticles/index/, when I used the form in the controller /users/login/ the number salt was right. So I figured some code in the /newsarticles/index/ controller messes with the session vars. After checking all the code the thing messing up the session var was......

An -tag referring to an non-existent image. :|

I don't know why it messes with the session, but it does. Maybe some .htaccess thing?

But to use this component as an element do this:

Model Class:

<?php 
class AppController extends Controller
{
    var 
$helpers = array('Html''Javascript''DAuth');
    var 
$components = array('DAuth','Session');

    function 
beforeRender()
    {
        
// Begin DAuth component code
        
$this->DAuth->newSalt();
        
        
$errorLogin $this->DAuth->getErrorMessage();
        if (!
$errorLogin) {
            
$errorLogin 'Unknown reason.';
        }
        
$this->set('errorLogin'$errorLogin);
        
$this->set('allowcleartext',$this->DAuth->allowClearText);
        
// End DAuth component code
        
    
}
}
?>

Default.thtml:

View Template:


echo $this->renderElement('loginbox', array('errorLogin' => $errorLogin, 'allowcleartext' => $allowcleartext, 'special_sauce' => $special_sauce));

That's it :) Great component btw!
Posted Jun 23, 2007 by Jordy
 

Comment

63 General announcement and reply to Vaz and Jordy

Good point Vaz.
I don't have the time right now to check this, but I sure believe it behaves like that because I didn't explicitly program anything to encounter it. So you could hack the javascript to send an empty hash or something for an empty password so the serverside code can detect this and act accordingly (form validation)

I would like to announce that it seems like in the forseeable future, I will no longer have the time to maintain dAuth. At the end of this comment you'll see the current list with bugs, most of them are not critical but it's good to take a look at it anyway and patch dAuth yourself.

Jordy: I think it's a known - but very rare bug - somebody once blogged about it (the title was "cakephp: always make sure you set your image paths correctly" or something like that)


bugfix #1 for v0.3: users_controller.php line 80/81: change allowCleartext to allowClearText
bugfix #2 for v0.3: don't change the id of the password in the views, or make a little change to the helper to make it
bugfix #3 for v0.3: remove default '0000-00-00 00:00:00' from sql of models.
bugfix #4 for v0.3: change 'host_id' => ' = '.$data['Host']['id'] to 'host_id' => '= '.$data['Host']['id'] (remove the space before the equals sign)
bugfix #5 for v0.3: don't allow registration with empty password. (patch the d_auth javascript)
Posted Jul 10, 2007 by Dieter Plaetinck
 

Comment

64 design issue

A friend at school pointed out something pretty important:
For the users convenience, we create a dummy, random string of the same length as the password, so that the user doesn't notice his password has changed as he tries to submit the form.

This is bad design because this is a hint for sniffers: if they sniff your traffic they can know the length of the password. They still don't know the password itself so it's not a security leak but still this is not good practice... yet another reason why we should use https.

Read more about why i recommend https and about me not longer having time for cake/dauth on my blog:
http://dieter.plaetinck.be/bye_cakephp_bye_dauth_hello_drupal
Posted Jul 21, 2007 by Dieter Plaetinck
 

Comment

65 dAuth with Ajax and side order of fries

Hi Dieter,

I already had a simple registration system in place, but when I re-read your features-list on dAuth, I switched! Thank you. Not only did you provide encrypted login that doesn't require SSL, you've added IDS too. And with well-written code. Huzzah!

I'm integrating your core code with Ajax-powered forms, because I want a login box embedded in the default layout that changes to a 'home' and 'logout' links box when you're logged in.

When it's ready, I'll be happy to share the code with you.

Thanks!
Paul Wolborsky
Posted Jul 27, 2007 by Paul Wolborsky
 

Comment

66 Header errors seem to have really mundane cause

I've gotten a rash of header errors too, all of them without exception caused by spaces before the initial tag. Most of the code you copy-and-paste or save from this page has 'em. That's views, controllers, helpers, components, models.

Make sure those tags are airtight, and you'll get rid of the errors.

Paul
Posted Jul 27, 2007 by Paul Wolborsky