diff --git a/UserModule.php b/UserModule.php index cc624a9..e6ed8ea 100644 --- a/UserModule.php +++ b/UserModule.php @@ -160,14 +160,14 @@ public static function t($str='',$params=array(),$dic='user') { /** * @return hash string. */ - public static function encrypting($string="") { + public static function encrypting($string="", $salt="") { $hash = Yii::app()->getModule('user')->hash; if ($hash=="md5") - return md5($string); + return md5($string.$salt); if ($hash=="sha1") - return sha1($string); + return sha1($string.$salt); else - return hash($hash,$string); + return hash($hash,$string.$salt); } /** diff --git a/components/UserIdentity.php b/components/UserIdentity.php index 6f8986c..d5c59d4 100644 --- a/components/UserIdentity.php +++ b/components/UserIdentity.php @@ -26,13 +26,16 @@ public function authenticate() } else { $user=User::model()->notsafe()->findByAttributes(array('username'=>$this->username)); } - if($user===null) + if($user===null) { if (strpos($this->username,"@")) { $this->errorCode=self::ERROR_EMAIL_INVALID; } else { $this->errorCode=self::ERROR_USERNAME_INVALID; } - else if(Yii::app()->getModule('user')->encrypting($this->password)!==$user->password) + return false; + } + + if(Yii::app()->getModule('user')->encrypting($this->password, $user->salt)!==$user->password) $this->errorCode=self::ERROR_PASSWORD_INVALID; else if($user->status==0&&Yii::app()->getModule('user')->loginNotActiv==false) $this->errorCode=self::ERROR_STATUS_NOTACTIV; @@ -42,6 +45,13 @@ public function authenticate() $this->_id=$user->id; $this->username=$user->username; $this->errorCode=self::ERROR_NONE; + + //when user has no salt, let's be generous and give him some. + if(empty($user->salt)) { + $user->salt = User::getNewSalt(); + $user->password = Yii::app()->getModule('user')->encrypting($this->password, $user->salt); + $user->save(); + } } return !$this->errorCode; } diff --git a/controllers/AdminController.php b/controllers/AdminController.php index ab28421..cad496f 100644 --- a/controllers/AdminController.php +++ b/controllers/AdminController.php @@ -64,6 +64,7 @@ public function actionAdmin() public function actionView() { $model = $this->loadModel(); + $model->password = ''; $this->render('view',array( 'model'=>$model, )); @@ -85,7 +86,8 @@ public function actionCreate() $profile->attributes=$_POST['Profile']; $profile->user_id=0; if($model->validate()&&$profile->validate()) { - $model->password=Yii::app()->controller->module->encrypting($model->password); + $model->salt = User::getNewSalt(); + $model->password=Yii::app()->controller->module->encrypting($model->password,$model->salt); if($model->save()) { $profile->user_id=$model->id; $profile->save(); @@ -116,16 +118,21 @@ public function actionUpdate() if($model->validate()&&$profile->validate()) { $old_password = User::model()->notsafe()->findByPk($model->id); - if ($old_password->password!=$model->password) { - $model->password=Yii::app()->controller->module->encrypting($model->password); + $old_salt = $old_password->salt; + $old_password = $old_password->password; + if (isset($_POST['User']['password']) && $old_password != Yii::app()->controller->module->encrypting($model->password,$old_salt)) { + $model->salt = User::getNewSalt(); + $model->password=Yii::app()->controller->module->encrypting($model->password,$model->salt); $model->activkey=Yii::app()->controller->module->encrypting(microtime().$model->password); + } else { + unset($model->password); } $model->save(); $profile->save(); $this->redirect(array('view','id'=>$model->id)); } else $profile->validate(); } - + $model->password = ''; $this->render('update',array( 'model'=>$model, 'profile'=>$profile, diff --git a/controllers/LoginController.php b/controllers/LoginController.php index db03de5..3a25acd 100644 --- a/controllers/LoginController.php +++ b/controllers/LoginController.php @@ -17,8 +17,8 @@ public function actionLogin() $model->attributes=$_POST['UserLogin']; // validate user input and redirect to previous page if valid if($model->validate()) { - $this->lastViset(); - if (Yii::app()->getBaseUrl()."/index.php" === Yii::app()->user->returnUrl) + $this->lastVisit(); + if (Yii::app()->user->returnUrl=='/index.php') $this->redirect(Yii::app()->controller->module->returnUrl); else $this->redirect(Yii::app()->user->returnUrl); @@ -30,7 +30,7 @@ public function actionLogin() $this->redirect(Yii::app()->controller->module->returnUrl); } - private function lastViset() { + private function lastVisit() { $lastVisit = User::model()->notsafe()->findByPk(Yii::app()->user->id); $lastVisit->lastvisit_at = date('Y-m-d H:i:s'); $lastVisit->save(); diff --git a/controllers/ProfileController.php b/controllers/ProfileController.php index 2eb3070..944016b 100644 --- a/controllers/ProfileController.php +++ b/controllers/ProfileController.php @@ -75,8 +75,9 @@ public function actionChangepassword() { $model->attributes=$_POST['UserChangePassword']; if($model->validate()) { $new_password = User::model()->notsafe()->findbyPk(Yii::app()->user->id); - $new_password->password = UserModule::encrypting($model->password); - $new_password->activkey=UserModule::encrypting(microtime().$model->password); + $new_password->salt = User::getNewSalt(); + $new_password->password = UserModule::encrypting($model->password,$new_password->salt); + $new_password->activkey = UserModule::encrypting(microtime().$model->password); $new_password->save(); Yii::app()->user->setFlash('profileMessage',UserModule::t("New password is saved.")); $this->redirect(array("profile")); diff --git a/controllers/RegistrationController.php b/controllers/RegistrationController.php index 7890b65..a5315a7 100644 --- a/controllers/RegistrationController.php +++ b/controllers/RegistrationController.php @@ -39,42 +39,46 @@ public function actionRegistration() { $profile->attributes=((isset($_POST['Profile'])?$_POST['Profile']:array())); if($model->validate()&&$profile->validate()) { - $soucePassword = $model->password; - $model->activkey=UserModule::encrypting(microtime().$model->password); - $model->password=UserModule::encrypting($model->password); - $model->verifyPassword=UserModule::encrypting($model->verifyPassword); - $model->superuser=0; - $model->status=((Yii::app()->controller->module->activeAfterRegister)?User::STATUS_ACTIVE:User::STATUS_NOACTIVE); - - if ($model->save()) { - $profile->user_id=$model->id; - $profile->save(); - if (Yii::app()->controller->module->sendActivationMail) { - $activation_url = $this->createAbsoluteUrl('/user/activation/activation',array("activkey" => $model->activkey, "email" => $model->email)); - UserModule::sendMail($model->email,UserModule::t("You registered from {site_name}",array('{site_name}'=>Yii::app()->name)),UserModule::t("Please activate you account go to {activation_url}",array('{activation_url}'=>$activation_url))); - } - - if ((Yii::app()->controller->module->loginNotActiv||(Yii::app()->controller->module->activeAfterRegister&&Yii::app()->controller->module->sendActivationMail==false))&&Yii::app()->controller->module->autoLogin) { - $identity=new UserIdentity($model->username,$soucePassword); - $identity->authenticate(); - Yii::app()->user->login($identity,0); - $this->redirect(Yii::app()->controller->module->returnUrl); - } else { - if (!Yii::app()->controller->module->activeAfterRegister&&!Yii::app()->controller->module->sendActivationMail) { - Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Contact Admin to activate your account.")); - } elseif(Yii::app()->controller->module->activeAfterRegister&&Yii::app()->controller->module->sendActivationMail==false) { - Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Please {{login}}.",array('{{login}}'=>CHtml::link(UserModule::t('Login'),Yii::app()->controller->module->loginUrl)))); - } elseif(Yii::app()->controller->module->loginNotActiv) { - Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Please check your email or login.")); - } else { - Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Please check your email.")); - } - $this->refresh(); - } - } - } else $profile->validate(); - } - $this->render('/user/registration',array('model'=>$model,'profile'=>$profile)); - } + $model->salt = User::getNewSalt(); + + $sourcePassword = $model->password; + $model->activkey=UserModule::encrypting(microtime().$model->password); + $model->password=UserModule::encrypting($model->password, $model->salt); + $model->verifyPassword=UserModule::encrypting($model->verifyPassword, $model->salt); + $model->superuser=0; + $model->status=((Yii::app()->controller->module->activeAfterRegister)?User::STATUS_ACTIVE:User::STATUS_NOACTIVE); + + if ($model->save()) { + $profile->user_id=$model->id; + $profile->save(); + if (Yii::app()->controller->module->sendActivationMail) { + $activation_url = $this->createAbsoluteUrl('/user/activation/activation',array("activkey" => $model->activkey, "email" => $model->email)); + UserModule::sendMail($model->email,UserModule::t("You registered from {site_name}",array('{site_name}'=>Yii::app()->name)),UserModule::t("Please activate you account go to {activation_url}",array('{activation_url}'=>$activation_url))); + } + + if ((Yii::app()->controller->module->loginNotActiv||(Yii::app()->controller->module->activeAfterRegister&&Yii::app()->controller->module->sendActivationMail==false))&&Yii::app()->controller->module->autoLogin) { + $identity=new UserIdentity($model->username,$sourcePassword); + $identity->authenticate(); + Yii::app()->user->login($identity,0); + $this->redirect(Yii::app()->controller->module->returnUrl); + } else { + if (!Yii::app()->controller->module->activeAfterRegister&&!Yii::app()->controller->module->sendActivationMail) { + Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Contact Admin to activate your account.")); + } elseif(Yii::app()->controller->module->activeAfterRegister&&Yii::app()->controller->module->sendActivationMail==false) { + Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Please {{login}}.",array('{{login}}'=>CHtml::link(UserModule::t('Login'),Yii::app()->controller->module->loginUrl)))); + } elseif(Yii::app()->controller->module->loginNotActiv) { + Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Please check your email or login.")); + } else { + Yii::app()->user->setFlash('registration',UserModule::t("Thank you for your registration. Please check your email.")); + } + $this->refresh(); + } + } + } else $profile->validate(); + } + $model->password = ''; + $model->verifyPassword = ''; + $this->render('/user/registration',array('model'=>$model,'profile'=>$profile)); + } } } \ No newline at end of file diff --git a/data/schema.mysql.sql b/data/schema.mysql.sql index 607451d..9f85502 100644 --- a/data/schema.mysql.sql +++ b/data/schema.mysql.sql @@ -2,6 +2,7 @@ CREATE TABLE `tbl_users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) NOT NULL, `password` varchar(128) NOT NULL, + `salt` varchar(40) NULL DEFAULT NULL, `email` varchar(128) NOT NULL, `activkey` varchar(128) NOT NULL DEFAULT '', `create_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, diff --git a/data/schema.sqlite.sql b/data/schema.sqlite.sql index 0762f39..9c0db0c 100644 --- a/data/schema.sqlite.sql +++ b/data/schema.sqlite.sql @@ -7,7 +7,8 @@ CREATE TABLE tbl_users ( create_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, lastvisit_at TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', superuser int(1) NOT NULL DEFAULT '0', - status int(1) NOT NULL DEFAULT '0' + status int(1) NOT NULL DEFAULT '0', + salt varchar(40) DEFAULT NULL ); CREATE TABLE tbl_profiles ( diff --git a/data/user.sqlite b/data/user.sqlite index 9607732..902a5f5 100644 Binary files a/data/user.sqlite and b/data/user.sqlite differ diff --git a/migrations/m121001_001625_userSalt.php b/migrations/m121001_001625_userSalt.php new file mode 100644 index 0000000..99df5a4 --- /dev/null +++ b/migrations/m121001_001625_userSalt.php @@ -0,0 +1,64 @@ +getModule('user')) { + echo "\n\nAdd to console.php :\n" + ."'modules'=>array(\n" + ."...\n" + ." 'user'=>array(\n" + ." ... # copy settings from main config\n" + ." ),\n" + ."...\n" + ."),\n" + ."\n"; + return false; + } + + switch ($this->dbType()) { + case "mysql": + $this->addColumn(Yii::app()->getModule('user')->tableUsers,'salt',"VARCHAR(40) NULL DEFAULT NULL AFTER `password`"); + break; + case "sqlite": + default: + $this->addColumn(Yii::app()->getModule('user')->tableUsers,'salt',"VARCHAR(40) DEFAULT NULL"); + break; + } + } + + public function safeDown() + { + switch ($this->dbType()) { + case "mysql": + $this->dropColumn(Yii::app()->getModule('user')->tableUsers,'salt'); + break; + case "sqlite": + + $this->execute('ALTER TABLE `'.Yii::app()->getModule('user')->tableUsers.'` RENAME TO `'.__CLASS__.'_'.Yii::app()->getModule('user')->tableUsers.'`'); + $this->createTable(Yii::app()->getModule('user')->tableUsers, array( + "id" => "pk", + "username" => "varchar(20) NOT NULL", + "password" => "varchar(128) NOT NULL", + "email" => "varchar(128) NOT NULL", + "activkey" => "varchar(128) NOT NULL", + "superuser" => "int(1) NOT NULL", + "status" => "int(1) NOT NULL", + "create_at" => "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP", + "lastvisit_at" => "TIMESTAMP", + )); + $this->execute('INSERT INTO `'.Yii::app()->getModule('user')->tableUsers.'` SELECT "id","username","password","email","activkey","superuser","status","create_at","lastvisit_at" FROM `'.__CLASS__.'_'.Yii::app()->getModule('user')->tableUsers.'`'); + $this->dropTable(__CLASS__.'_'.Yii::app()->getModule('user')->tableUsers); + + break; + } + } + + public function dbType() + { + list($type) = explode(':',Yii::app()->db->connectionString); + echo "type db: ".$type."\n"; + return $type; + } +} \ No newline at end of file diff --git a/models/User.php b/models/User.php index a245586..e4ba519 100644 --- a/models/User.php +++ b/models/User.php @@ -14,6 +14,7 @@ class User extends CActiveRecord * @var integer $id * @var string $username * @var string $password + * @var string $salt * @var string $email * @var string $activkey * @var integer $createtime @@ -121,7 +122,7 @@ public function scopes() 'condition'=>'superuser=1', ), 'notsafe'=>array( - 'select' => 'id, username, password, email, activkey, create_at, lastvisit_at, superuser, status', + 'select' => 'id, username, password, salt, email, activkey, create_at, lastvisit_at, superuser, status', ), ); } @@ -196,8 +197,19 @@ public function getLastvisit() { public function setLastvisit($value) { $this->lastvisit_at=date('Y-m-d H:i:s',$value); } + + /** + * simply returns the md5 digested value of PHP's uniqid() + * That should be sufficient to make most rainbowtables useless. + * + * Modify this function to change the way a salt is generated if you disagree. + */ + public static function getNewSalt() + { + return md5(uniqid(mt_rand(), true)); + } - public function afterSave() { + public function afterSave() { if (get_class(Yii::app())=='CWebApplication'&&Profile::$regMode==false) { Yii::app()->user->updateSession(); } diff --git a/models/UserChangePassword.php b/models/UserChangePassword.php index e1256f5..0cad07d 100644 --- a/models/UserChangePassword.php +++ b/models/UserChangePassword.php @@ -39,7 +39,10 @@ public function attributeLabels() */ public function verifyOldPassword($attribute, $params) { - if (User::model()->notsafe()->findByPk(Yii::app()->user->id)->password != Yii::app()->getModule('user')->encrypting($this->$attribute)) + $old_password = User::model()->notsafe()->findByPk(Yii::app()->user->id); + $old_salt = $old_password->salt; + $old_password = $old_password->password; + if ($old_password != Yii::app()->getModule('user')->encrypting($this->$attribute, $old_salt)) $this->addError($attribute, UserModule::t("Old Password is incorrect.")); } } \ No newline at end of file diff --git a/views/admin/view.php b/views/admin/view.php index 4f2dafe..56b48b8 100644 --- a/views/admin/view.php +++ b/views/admin/view.php @@ -36,9 +36,7 @@ } array_push($attributes, - 'password', 'email', - 'activkey', 'create_at', 'lastvisit_at', array(