mirror of https://github.com/MISP/MISP
new: [internal] Allow to log authkey usage in Redis
parent
6ce13b8168
commit
e5e855b3c2
|
@ -230,82 +230,7 @@ class AppController extends Controller
|
|||
if (array_key_exists('Security', $this->components)) {
|
||||
$this->Security->csrfCheck = false;
|
||||
}
|
||||
// If enabled, allow passing the API key via a named parameter (for crappy legacy systems only)
|
||||
$namedParamAuthkey = false;
|
||||
if (Configure::read('Security.allow_unsafe_apikey_named_param') && !empty($this->params['named']['apikey'])) {
|
||||
$namedParamAuthkey = $this->params['named']['apikey'];
|
||||
}
|
||||
// Authenticate user with authkey in Authorization HTTP header
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) || !empty($namedParamAuthkey)) {
|
||||
$foundMispAuthKey = false;
|
||||
$authentication = explode(',', $_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($namedParamAuthkey)) {
|
||||
$authentication[] = $namedParamAuthkey;
|
||||
}
|
||||
$user = false;
|
||||
foreach ($authentication as $authKey) {
|
||||
$authKey = trim($authKey);
|
||||
if (preg_match('/^[a-zA-Z0-9]{40}$/', $authKey)) {
|
||||
$foundMispAuthKey = true;
|
||||
$temp = $this->checkAuthUser($authKey);
|
||||
if ($temp) {
|
||||
$user = $temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($foundMispAuthKey) {
|
||||
$authKeyToStore = substr($authKey, 0, 4)
|
||||
. str_repeat('*', 32)
|
||||
. substr($authKey, -4);
|
||||
if ($user) {
|
||||
unset($user['gpgkey']);
|
||||
unset($user['certif_public']);
|
||||
// User found in the db, add the user info to the session
|
||||
if (Configure::read('MISP.log_auth')) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $user['id'],
|
||||
'email' => $user['email'],
|
||||
'action' => 'auth',
|
||||
'title' => "Successful authentication using API key ($authKeyToStore)",
|
||||
'change' => 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user);
|
||||
$this->isApiAuthed = true;
|
||||
} else {
|
||||
// User not authenticated correctly
|
||||
// reset the session information
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
// Do not log every fail, but just once per hour
|
||||
if ($redis && !$redis->exists('misp:auth_fail_throttling:' . $authKeyToStore)) {
|
||||
$redis->setex('misp:auth_fail_throttling:' . $authKeyToStore, 3600, 1);
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'User',
|
||||
'model_id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'auth_fail',
|
||||
'title' => "Failed authentication using API key ($authKeyToStore)",
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->destroy();
|
||||
throw new ForbiddenException('Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header.');
|
||||
}
|
||||
unset($user);
|
||||
}
|
||||
}
|
||||
if ($this->Auth->user() == null) {
|
||||
if ($this->__logByAuthKey() === false ||$this->Auth->user() === null) {
|
||||
throw new ForbiddenException('Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header.');
|
||||
}
|
||||
} elseif (!$this->Session->read(AuthComponent::$sessionKey)) {
|
||||
|
@ -489,6 +414,100 @@ class AppController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|bool True if authkey was correct, False if incorrect and Null if not provided
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __logByAuthKey()
|
||||
{
|
||||
// If enabled, allow passing the API key via a named parameter (for crappy legacy systems only)
|
||||
$namedParamAuthkey = false;
|
||||
if (Configure::read('Security.allow_unsafe_apikey_named_param') && !empty($this->params['named']['apikey'])) {
|
||||
$namedParamAuthkey = $this->params['named']['apikey'];
|
||||
}
|
||||
// Authenticate user with authkey in Authorization HTTP header
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) || !empty($namedParamAuthkey)) {
|
||||
$foundMispAuthKey = false;
|
||||
$authentication = explode(',', $_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($namedParamAuthkey)) {
|
||||
$authentication[] = $namedParamAuthkey;
|
||||
}
|
||||
$user = false;
|
||||
foreach ($authentication as $authKey) {
|
||||
$authKey = trim($authKey);
|
||||
if (preg_match('/^[a-zA-Z0-9]{40}$/', $authKey)) {
|
||||
$foundMispAuthKey = true;
|
||||
$temp = $this->checkAuthUser($authKey);
|
||||
if ($temp) {
|
||||
$user = $temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($foundMispAuthKey) {
|
||||
$authKeyToStore = substr($authKey, 0, 4)
|
||||
. str_repeat('*', 32)
|
||||
. substr($authKey, -4);
|
||||
if ($user) {
|
||||
unset($user['gpgkey']);
|
||||
unset($user['certif_public']);
|
||||
// User found in the db, add the user info to the session
|
||||
if (Configure::read('MISP.log_auth')) {
|
||||
$this->loadModel('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $user['id'],
|
||||
'email' => $user['email'],
|
||||
'action' => 'auth',
|
||||
'title' => "Successful authentication using API key ($authKeyToStore)",
|
||||
'change' => 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
// Allow to log key usage
|
||||
if (isset($user['authkey_id']) && Configure::read('MISP.log_auth_redis')) {
|
||||
/** @var Redis $redis */
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
if ($redis) {
|
||||
$key = "misp:authkey:{$user['authkey_id']}";
|
||||
$hashKey = date("Y-m-d") . ":{$_SERVER['REMOTE_ADDR']}";
|
||||
$redis->hIncrBy($key, $hashKey, 1);
|
||||
}
|
||||
}
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user);
|
||||
$this->isApiAuthed = true;
|
||||
return true;
|
||||
} else {
|
||||
// User not authenticated correctly
|
||||
// reset the session information
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
// Do not log every fail, but just once per hour
|
||||
if ($redis && !$redis->exists('misp:auth_fail_throttling:' . $authKeyToStore)) {
|
||||
$redis->setex('misp:auth_fail_throttling:' . $authKeyToStore, 3600, 1);
|
||||
$this->loadModel('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'User',
|
||||
'model_id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'auth_fail',
|
||||
'title' => "Failed authentication using API key ($authKeyToStore)",
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->destroy();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if:
|
||||
* - user exists in database
|
||||
|
|
|
@ -57,7 +57,7 @@ class AuthKey extends AppModel
|
|||
$end = substr($authkey, -4);
|
||||
$existing_authkeys = $this->find('all', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['authkey', 'user_id'],
|
||||
'fields' => ['id', 'authkey', 'user_id'],
|
||||
'conditions' => [
|
||||
'OR' => [
|
||||
'expiration >' => time(),
|
||||
|
@ -70,7 +70,11 @@ class AuthKey extends AppModel
|
|||
$passwordHasher = $this->getHasher();
|
||||
foreach ($existing_authkeys as $existing_authkey) {
|
||||
if ($passwordHasher->check($authkey, $existing_authkey['AuthKey']['authkey'])) {
|
||||
return $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
|
||||
$user = $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
|
||||
if ($user) {
|
||||
$user['authkey_id'] = $existing_authkey['AuthKey']['id'];
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue