mirror of https://github.com/MISP/MISP
fix: Upgraded hashing algorithm used and added requirement to confirm password for user profile changes
- Added method to upgrade all passwords to blowfish transparently - All profile edit pages (/users/edit, /admin/users/edit, /users/change_pw) now require the user's password to be confirmed - Thanks to cert.govt.nz for the security report.pull/2339/head
parent
d377bc195b
commit
3317f56ca1
|
@ -73,6 +73,14 @@ class AppController extends Controller {
|
|||
'authError' => 'Unauthorised access.',
|
||||
'loginRedirect' => array('controller' => 'users', 'action' => 'routeafterlogin'),
|
||||
'logoutRedirect' => array('controller' => 'users', 'action' => 'login', 'admin' => false),
|
||||
'authenticate' => array(
|
||||
'Form' => array(
|
||||
'passwordHasher' => 'Blowfish',
|
||||
'fields' => array(
|
||||
'username' => 'email'
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
'Security',
|
||||
'ACL',
|
||||
|
@ -125,12 +133,7 @@ class AppController extends Controller {
|
|||
)
|
||||
);
|
||||
} else {
|
||||
$this->Auth->authenticate = array(
|
||||
'Form' => array(
|
||||
'fields' => array('username' => 'email'),
|
||||
'userFields' => $auth_user_fields
|
||||
)
|
||||
);
|
||||
$this->Auth->authenticate['Form']['userFields'] = $auth_user_fields;
|
||||
}
|
||||
$versionArray = $this->{$this->modelClass}->checkMISPVersion();
|
||||
$this->mispVersion = implode('.', array_values($versionArray));
|
||||
|
@ -151,7 +154,6 @@ class AppController extends Controller {
|
|||
if (isset($_SERVER['HTTP_USER_AGENT'])) {
|
||||
if (preg_match('/(?i)msie [2-8]/',$_SERVER['HTTP_USER_AGENT']) && !strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')) throw new MethodNotAllowedException('You are using an unsecure and outdated version of IE, please download Google Chrome, Mozilla Firefox or update to a newer version of IE. If you are running IE9 or newer and still receive this error message, please make sure that you are not running your browser in compatibility mode. If you still have issues accessing the site, get in touch with your administration team at ' . Configure::read('MISP.contact'));
|
||||
}
|
||||
|
||||
$userLoggedIn = false;
|
||||
if (Configure::read('Plugin.CustomAuth_enable')) $userLoggedIn = $this->__customAuthentication($_SERVER);
|
||||
if (!$userLoggedIn) {
|
||||
|
|
|
@ -84,6 +84,20 @@ class UsersController extends AppController {
|
|||
throw new NotFoundException('Something went wrong. Your user account could not be accessed.');
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!$this->_isRest()) {
|
||||
if (!empty($this->request->data['User']['current_password'])) {
|
||||
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
|
||||
if (!$hashed) {
|
||||
$abortPost = true;
|
||||
$this->Session->setFlash('Invalid password. Please enter your current password to continue.');
|
||||
}
|
||||
unset($this->request->data['User']['current_password']);
|
||||
} else {
|
||||
$abortPost = true;
|
||||
$this->Session->setFlash('Please enter your current password to continue.');
|
||||
}
|
||||
}
|
||||
if (!$abortPost) {
|
||||
// What fields should be saved (allowed to be saved)
|
||||
$fieldList = array('email', 'autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled');
|
||||
if ("" != $this->request->data['User']['password'])
|
||||
|
@ -96,6 +110,7 @@ class UsersController extends AppController {
|
|||
} else {
|
||||
$this->Session->setFlash(__('The profile could not be updated. Please, try again.'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->User->set('password', '');
|
||||
$this->request->data = $this->User->data;
|
||||
|
@ -110,21 +125,40 @@ class UsersController extends AppController {
|
|||
|
||||
public function change_pw() {
|
||||
$id = $this->Auth->user('id');
|
||||
$this->User->id = $id;
|
||||
$user = $this->User->find('first', array(
|
||||
'conditions' => array('User.id' => $id),
|
||||
'recursive' => -1
|
||||
));
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$abortPost = false;
|
||||
if (!empty($this->request->data['User']['current_password'])) {
|
||||
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
|
||||
if (!$hashed) {
|
||||
$abortPost = true;
|
||||
$this->Session->setFlash('Invalid password. Please enter your current password to continue.');
|
||||
}
|
||||
unset($this->request->data['User']['current_password']);
|
||||
} else {
|
||||
$abortPost = true;
|
||||
$this->Session->setFlash('Please enter your current password to continue.');
|
||||
}
|
||||
if (!$abortPost) {
|
||||
// What fields should be saved (allowed to be saved)
|
||||
$fieldList[] = 'password';
|
||||
$user['User']['change_pw'] = 0;
|
||||
$user['User']['password'] = $this->request->data['User']['password'];
|
||||
$user['User']['confirm_password'] = $this->request->data['User']['confirm_password'];
|
||||
$temp = $user['User']['password'];
|
||||
// Save the data
|
||||
if ($this->User->save($this->request->data, true ,$fieldList)) {
|
||||
if ($this->User->save($user)) {
|
||||
$this->Session->setFlash(__('Password Changed.'));
|
||||
$this->User->saveField('email', $this->Auth->user('email'));
|
||||
$this->User->saveField('change_pw', 0);
|
||||
$this->_refreshAuth();
|
||||
$this->__extralog("change_pw");
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
} else {
|
||||
$this->Session->setFlash(__('The password could not be updated. Please, try again.'));
|
||||
$this->Session->setFlash(__('The password could not be updated. Make sure you meet the minimum password length / complexity requirements.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->loadModel('Server');
|
||||
$this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']);
|
||||
$this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']);
|
||||
|
@ -132,9 +166,6 @@ class UsersController extends AppController {
|
|||
$this->User->read(null, $id);
|
||||
$this->User->set('password', '');
|
||||
$this->request->data = $this->User->data;
|
||||
}
|
||||
// XXX ACL roles
|
||||
$this->__extralog("change_pw");
|
||||
$roles = $this->User->Role->find('list');
|
||||
$this->set(compact('roles'));
|
||||
}
|
||||
|
@ -512,6 +543,21 @@ class UsersController extends AppController {
|
|||
if (!isset($this->request->data['User'])) {
|
||||
$this->request->data['User'] = $this->request->data;
|
||||
}
|
||||
$abortPost = false;
|
||||
if (!$this->_isRest()) {
|
||||
if (!empty($this->request->data['User']['current_password'])) {
|
||||
$hashed = $this->User->verifyPassword($this->Auth->user('id'), $this->request->data['User']['current_password']);
|
||||
if (!$hashed) {
|
||||
$abortPost = true;
|
||||
$this->Session->setFlash('Invalid password. Please enter your current password to continue.');
|
||||
}
|
||||
unset($this->request->data['User']['current_password']);
|
||||
} else {
|
||||
$abortPost = true;
|
||||
$this->Session->setFlash('Please enter your current password to continue.');
|
||||
}
|
||||
}
|
||||
if (!$abortPost) {
|
||||
$this->request->data['User']['id'] = $id;
|
||||
if (!isset($this->request->data['User']['email'])) {
|
||||
$this->request->data['User']['email'] = $userToEdit['User']['email'];
|
||||
|
@ -613,6 +659,7 @@ class UsersController extends AppController {
|
|||
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->describe('Users', 'admin_edit', $id, $this->response->type());
|
||||
|
@ -702,12 +749,30 @@ class UsersController extends AppController {
|
|||
throw new ForbiddenException('You have reached the maximum number of login attempts. Please wait ' . Configure::read('SecureAuth.expire') . ' seconds and try again.');
|
||||
}
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
// Check the length of the user's authkey
|
||||
$userPass = $this->User->find('first', array(
|
||||
'conditions' => array('User.email' => $this->request->data['User']['email']),
|
||||
'fields' => array('User.password'),
|
||||
'recursive' => -1
|
||||
));
|
||||
if (strlen($userPass['User']['password']) == 40) {
|
||||
$this->AdminSetting = ClassRegistry::init('AdminSetting');
|
||||
$db_version = $this->AdminSetting->find('all', array('conditions' => array('setting' => 'db_version')));
|
||||
$versionRequirementMet = $this->User->checkVersionRequirements($db_version[0]['AdminSetting']['value'], '2.4.77');
|
||||
if ($versionRequirementMet) {
|
||||
$passwordToSave = $this->request->data['User']['password'];
|
||||
}
|
||||
unset($this->Auth->authenticate['Form']['passwordHasher']);
|
||||
}
|
||||
}
|
||||
if ($this->Auth->login()) {
|
||||
$this->__extralog("login"); // TODO Audit, __extralog, check: customLog i.s.o. __extralog, no auth user?: $this->User->customLog('login', $this->Auth->user('id'), array('title' => '','user_id' => $this->Auth->user('id'),'email' => $this->Auth->user('email'),'org' => 'IN2'));
|
||||
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->saveField('last_login', $this->Auth->user('current_login'));
|
||||
$this->User->saveField('current_login', time());
|
||||
if (empty($this->Auth->authenticate['Form']['passwordHasher']) && !empty($passwordToSave)) $this->User->saveField('password', $passwordToSave);
|
||||
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
|
||||
// TODO removed the auto redirect for now, due to security concerns - will look more into this
|
||||
// $this->redirect($this->Auth->redirectUrl());
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
App::uses('Model', 'Model');
|
||||
App::uses('LogableBehavior', 'Assets.models/behaviors');
|
||||
App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');
|
||||
class AppModel extends Model {
|
||||
|
||||
public $name;
|
||||
|
||||
public $loadedPubSubTool = false;
|
||||
|
@ -47,7 +47,7 @@ class AppModel extends Model {
|
|||
58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
|
||||
63 => false, 64 => false, 65 => false, 66 => false, 67 => true,
|
||||
68 => false, 69 => false, 71 => false, 72 => false, 73 => false,
|
||||
75 => false
|
||||
75 => false, 77 => false
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -699,6 +699,9 @@ class AppModel extends Model {
|
|||
$this->__addIndex('attributes', 'value1', 255);
|
||||
$this->__addIndex('attributes', 'value2', 255);
|
||||
break;
|
||||
case '2.4.77':
|
||||
$sqlArray[] = 'ALTER TABLE `users` CHANGE `password` `password` VARCHAR(255) COLLATE utf8_bin NOT NULL;';
|
||||
break;
|
||||
case 'fixNonEmptySharingGroupID':
|
||||
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
|
@ -1051,4 +1054,10 @@ class AppModel extends Model {
|
|||
$this->loadedPubSubTool = $pubSubTool;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function checkVersionRequirements($versionString, $minVersion) {
|
||||
$version = explode('.', $versionString);
|
||||
$minVersion = explode('.', $minVersion);
|
||||
return ($version[0] >= $minVersion[0] && $version[1] >= $minVersion[1] && $version[2] >= $minVersion[2]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -251,7 +251,8 @@ class User extends AppModel {
|
|||
public function beforeSave($options = array()) {
|
||||
$this->data[$this->alias]['date_modified'] = time();
|
||||
if (isset($this->data[$this->alias]['password'])) {
|
||||
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
|
||||
$passwordHasher = new BlowfishPasswordHasher();
|
||||
$this->data[$this->alias]['password'] = $passwordHasher->hash($this->data[$this->alias]['password']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1026,4 +1027,21 @@ class User extends AppModel {
|
|||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function verifyPassword($user_id, $password) {
|
||||
$currentUser = $this->find('first', array(
|
||||
'conditions' => array('User.id' => $user_id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('User.password')
|
||||
));
|
||||
if (empty($currentUser)) return false;
|
||||
if (strlen($currentUser['User']['password']) == 40) {
|
||||
App::uses('SimplePasswordHasher', 'Controller/Component/Auth');
|
||||
$passwordHasher = new SimplePasswordHasher();
|
||||
} else {
|
||||
$passwordHasher = new BlowfishPasswordHasher();
|
||||
}
|
||||
$hashed = $passwordHasher->check($password, $currentUser['User']['password']);
|
||||
return $hashed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,12 @@
|
|||
|
||||
?>
|
||||
</fieldset>
|
||||
<div style="border-bottom: 1px solid #e5e5e5;width:100%;"> </div>
|
||||
<div class="clear" style="margin-top:10px;">
|
||||
<?php
|
||||
echo $this->Form->input('current_password', array('type' => 'password', 'div' => false, 'class' => 'input password required', 'label' => 'Confirm with your current password'));
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
|
||||
echo $this->Form->end();?>
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required')));
|
||||
?>
|
||||
</fieldset>
|
||||
<div style="border-bottom: 1px solid #e5e5e5;width:100%;"> </div>
|
||||
<div class="clear" style="margin-top:10px;">
|
||||
<?php
|
||||
echo $this->Form->input('current_password', array('type' => 'password', 'div' => false, 'class' => 'input password required', 'label' => 'Confirm with your current password'));
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
|
||||
echo $this->Form->end();
|
||||
|
|
|
@ -29,8 +29,16 @@
|
|||
echo $this->Form->input('contactalert', array('label' => 'Receive alerts from "contact reporter" requests', 'type' => 'checkbox'));
|
||||
?>
|
||||
</fieldset>
|
||||
<?php echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
|
||||
echo $this->Form->end();?>
|
||||
<div style="border-bottom: 1px solid #e5e5e5;width:100%;"> </div>
|
||||
<div class="clear" style="margin-top:10px;">
|
||||
<?php
|
||||
echo $this->Form->input('current_password', array('type' => 'password', 'div' => false, 'class' => 'input password required', 'label' => 'Confirm with your current password'));
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
$user['User']['id'] = $id;
|
||||
|
|
Loading…
Reference in New Issue