mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-24 06:14:25 +02:00
Rememberme compatibility with php version 5.6+ (#4472)
This commit is contained in:
parent
88f2c50f0b
commit
645c0770a2
@ -45,13 +45,15 @@ class AuthenticationController extends Controller
|
|||||||
|
|
||||||
if (RememberMe::hasCookie() && $this->hasDb()) {
|
if (RememberMe::hasCookie() && $this->hasDb()) {
|
||||||
$authenticated = false;
|
$authenticated = false;
|
||||||
|
$iv = null;
|
||||||
try {
|
try {
|
||||||
$rememberMeOld = RememberMe::fromCookie();
|
$rememberMeOld = RememberMe::fromCookie();
|
||||||
|
$iv = $rememberMeOld->getAesCrypt()->getIv();
|
||||||
$authenticated = $rememberMeOld->authenticate();
|
$authenticated = $rememberMeOld->authenticate();
|
||||||
if ($authenticated) {
|
if ($authenticated) {
|
||||||
$rememberMe = $rememberMeOld->renew();
|
$rememberMe = $rememberMeOld->renew();
|
||||||
$this->getResponse()->setCookie($rememberMe->getCookie());
|
$this->getResponse()->setCookie($rememberMe->getCookie());
|
||||||
$rememberMe->persist($rememberMeOld->getAesCrypt()->getIv());
|
$rememberMe->persist($iv);
|
||||||
}
|
}
|
||||||
} catch (RuntimeException $e) {
|
} catch (RuntimeException $e) {
|
||||||
Logger::error("Can't authenticate user via remember me cookie: %s", $e->getMessage());
|
Logger::error("Can't authenticate user via remember me cookie: %s", $e->getMessage());
|
||||||
@ -60,6 +62,7 @@ class AuthenticationController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (! $authenticated) {
|
if (! $authenticated) {
|
||||||
|
(new RememberMe())->remove(bin2hex($iv));
|
||||||
$this->getResponse()->setCookie(RememberMe::forget());
|
$this->getResponse()->setCookie(RememberMe::forget());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +111,7 @@ class AuthenticationController extends Controller
|
|||||||
} else {
|
} else {
|
||||||
if (RememberMe::hasCookie() && $this->hasDb()) {
|
if (RememberMe::hasCookie() && $this->hasDb()) {
|
||||||
try {
|
try {
|
||||||
(new RememberMe())->remove(RememberMe::fromCookie()->getAesCrypt()->getIV());
|
(new RememberMe())->remove(bin2hex(RememberMe::fromCookie()->getAesCrypt()->getIV()));
|
||||||
} catch (RuntimeException $e) {
|
} catch (RuntimeException $e) {
|
||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class ManageUserDevicesController extends CompatController
|
|||||||
|
|
||||||
public function deleteAction()
|
public function deleteAction()
|
||||||
{
|
{
|
||||||
(new RememberMe())->removeSpecific($this->params->getRequired('fingerprint'));
|
(new RememberMe())->remove($this->params->getRequired('fingerprint'));
|
||||||
|
|
||||||
$this->redirectNow(
|
$this->redirectNow(
|
||||||
Url::fromPath('manage-user-devices/devices')
|
Url::fromPath('manage-user-devices/devices')
|
||||||
|
@ -67,7 +67,7 @@ class MyDevicesController extends CompatController
|
|||||||
|
|
||||||
public function deleteAction()
|
public function deleteAction()
|
||||||
{
|
{
|
||||||
(new RememberMe())->removeSpecific($this->params->getRequired('fingerprint'));
|
(new RememberMe())->remove($this->params->getRequired('fingerprint'));
|
||||||
|
|
||||||
$this->redirectNow('my-devices');
|
$this->redirectNow('my-devices');
|
||||||
}
|
}
|
||||||
|
2
etc/schema/mysql-upgrades/2.9.1.sql
Normal file
2
etc/schema/mysql-upgrades/2.9.1.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `icingaweb_rememberme`
|
||||||
|
MODIFY random_iv varchar(32) NOT NULL;
|
@ -45,7 +45,7 @@ CREATE TABLE `icingaweb_rememberme`(
|
|||||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
username varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
username varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
passphrase varchar(256) NOT NULL,
|
passphrase varchar(256) NOT NULL,
|
||||||
random_iv varchar(24) NOT NULL,
|
random_iv varchar(32) NOT NULL,
|
||||||
http_user_agent text NOT NULL,
|
http_user_agent text NOT NULL,
|
||||||
expires_at timestamp NULL DEFAULT NULL,
|
expires_at timestamp NULL DEFAULT NULL,
|
||||||
ctime timestamp NULL DEFAULT NULL,
|
ctime timestamp NULL DEFAULT NULL,
|
||||||
|
2
etc/schema/pgsql-upgrades/2.9.1.sql
Normal file
2
etc/schema/pgsql-upgrades/2.9.1.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE ONLY "icingaweb_rememberme"
|
||||||
|
ALTER COLUMN random_iv type character varying(32);
|
@ -105,7 +105,7 @@ CREATE TABLE "icingaweb_rememberme" (
|
|||||||
"id" serial,
|
"id" serial,
|
||||||
"username" character varying(254) NOT NULL,
|
"username" character varying(254) NOT NULL,
|
||||||
"passphrase" character varying(256) NOT NULL,
|
"passphrase" character varying(256) NOT NULL,
|
||||||
"random_iv" character varying(24) NOT NULL,
|
"random_iv" character varying(32) NOT NULL,
|
||||||
"http_user_agent" text NOT NULL,
|
"http_user_agent" text NOT NULL,
|
||||||
"expires_at" timestamp NULL DEFAULT NULL,
|
"expires_at" timestamp NULL DEFAULT NULL,
|
||||||
"ctime" timestamp NULL DEFAULT NULL,
|
"ctime" timestamp NULL DEFAULT NULL,
|
||||||
|
@ -51,15 +51,31 @@ class AesCrypt
|
|||||||
private $tag;
|
private $tag;
|
||||||
|
|
||||||
/** @var string The cipher method */
|
/** @var string The cipher method */
|
||||||
private $method = 'aes-128-gcm';
|
private $method = 'AES-128-GCM';
|
||||||
|
|
||||||
|
const GCM_SUPPORT_VERSION = '7.1';
|
||||||
|
|
||||||
public function __construct($random_bytes_len = 128)
|
public function __construct($random_bytes_len = 128)
|
||||||
{
|
{
|
||||||
$len = openssl_cipher_iv_length($this->method);
|
if (version_compare(PHP_VERSION, self::GCM_SUPPORT_VERSION, '<')) {
|
||||||
$this->iv = random_bytes($len);
|
$this->method = 'AES-128-CBC';
|
||||||
|
}
|
||||||
|
|
||||||
$this->key = random_bytes($random_bytes_len);
|
$this->key = random_bytes($random_bytes_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force set the method
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setMethod($method)
|
||||||
|
{
|
||||||
|
$this->method = $method;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the key
|
* Set the key
|
||||||
*
|
*
|
||||||
@ -110,7 +126,8 @@ class AesCrypt
|
|||||||
public function getIV()
|
public function getIV()
|
||||||
{
|
{
|
||||||
if (empty($this->iv)) {
|
if (empty($this->iv)) {
|
||||||
throw new RuntimeException('No iv set');
|
$len = openssl_cipher_iv_length($this->method);
|
||||||
|
$this->iv = random_bytes($len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->iv;
|
return $this->iv;
|
||||||
@ -162,8 +179,13 @@ class AesCrypt
|
|||||||
*/
|
*/
|
||||||
public function decrypt($data)
|
public function decrypt($data)
|
||||||
{
|
{
|
||||||
|
if ($this->method === 'AES-128-CBC') {
|
||||||
|
return $this->decryptCBC($data);
|
||||||
|
}
|
||||||
|
|
||||||
$decrypt = openssl_decrypt($data, $this->method, $this->getKey(), 0, $this->getIV(), $this->getTag());
|
$decrypt = openssl_decrypt($data, $this->method, $this->getKey(), 0, $this->getIV(), $this->getTag());
|
||||||
if (is_bool($decrypt) && $decrypt === false) {
|
|
||||||
|
if ($decrypt === false) {
|
||||||
throw new RuntimeException('Decryption failed');
|
throw new RuntimeException('Decryption failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,9 +217,13 @@ class AesCrypt
|
|||||||
*/
|
*/
|
||||||
public function encrypt($data)
|
public function encrypt($data)
|
||||||
{
|
{
|
||||||
|
if ($this->method === 'AES-128-CBC') {
|
||||||
|
return $this->encryptCBC($data);
|
||||||
|
}
|
||||||
|
|
||||||
$encrypt = openssl_encrypt($data, $this->method, $this->getkey(), 0, $this->getIV(), $this->tag);
|
$encrypt = openssl_encrypt($data, $this->method, $this->getkey(), 0, $this->getIV(), $this->tag);
|
||||||
|
|
||||||
if (is_bool($encrypt) && $encrypt === false) {
|
if ($encrypt === false) {
|
||||||
throw new RuntimeException('Encryption failed');
|
throw new RuntimeException('Encryption failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,4 +243,37 @@ class AesCrypt
|
|||||||
{
|
{
|
||||||
return base64_encode($this->encrypt($data));
|
return base64_encode($this->encrypt($data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function decryptCBC($data)
|
||||||
|
{
|
||||||
|
if (strlen($this->getIV()) !== 16) {
|
||||||
|
throw new RuntimeException('Decryption failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$c = base64_decode($data);
|
||||||
|
$hmac = substr($c, 0, 32);
|
||||||
|
$data = substr($c, 32);
|
||||||
|
|
||||||
|
$decrypt = openssl_decrypt($data, $this->method, $this->getKey(), 0, $this->getIV());
|
||||||
|
$calcHmac = hash_hmac('sha256', $data, $this->getKey(), true);
|
||||||
|
|
||||||
|
if ($decrypt === false || ! hash_equals($hmac, $calcHmac)) {
|
||||||
|
throw new RuntimeException('Decryption failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decrypt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function encryptCBC($data)
|
||||||
|
{
|
||||||
|
$encrypt = openssl_encrypt($data, $this->method, $this->getkey(), 0, $this->getIV());
|
||||||
|
|
||||||
|
if ($encrypt === false) {
|
||||||
|
throw new RuntimeException('Encryption failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$hmac = hash_hmac('sha256', $encrypt, $this->getkey(), true);
|
||||||
|
|
||||||
|
return base64_encode($hmac . $encrypt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,6 @@ class RememberMe
|
|||||||
{
|
{
|
||||||
$data = explode('|', $_COOKIE[static::COOKIE]);
|
$data = explode('|', $_COOKIE[static::COOKIE]);
|
||||||
$iv = base64_decode(array_pop($data));
|
$iv = base64_decode(array_pop($data));
|
||||||
$tag = base64_decode(array_pop($data));
|
|
||||||
|
|
||||||
$select = (new Select())
|
$select = (new Select())
|
||||||
->from(static::TABLE)
|
->from(static::TABLE)
|
||||||
@ -91,8 +90,23 @@ class RememberMe
|
|||||||
|
|
||||||
$rememberMe->aesCrypt = (new AesCrypt())
|
$rememberMe->aesCrypt = (new AesCrypt())
|
||||||
->setKey(hex2bin($rs->passphrase))
|
->setKey(hex2bin($rs->passphrase))
|
||||||
->setTag($tag)
|
|
||||||
->setIV($iv);
|
->setIV($iv);
|
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, AesCrypt::GCM_SUPPORT_VERSION, '>=')) {
|
||||||
|
$tag = array_pop($data);
|
||||||
|
|
||||||
|
if (empty($data)) {
|
||||||
|
$rememberMe->aesCrypt = (new AesCrypt())
|
||||||
|
->setMethod('AES-128-CBC')
|
||||||
|
->setKey(hex2bin($rs->passphrase))
|
||||||
|
->setIV($iv);
|
||||||
|
|
||||||
|
$data[0] = $tag; // encryptedPass
|
||||||
|
} else {
|
||||||
|
$rememberMe->aesCrypt->setTag(base64_decode($tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$rememberMe->username = $rs->username;
|
$rememberMe->username = $rs->username;
|
||||||
$rememberMe->encryptedPassword = $data[0];
|
$rememberMe->encryptedPassword = $data[0];
|
||||||
|
|
||||||
@ -140,14 +154,19 @@ class RememberMe
|
|||||||
*/
|
*/
|
||||||
public function getCookie()
|
public function getCookie()
|
||||||
{
|
{
|
||||||
|
$values = [
|
||||||
|
$this->encryptedPassword,
|
||||||
|
base64_encode($this->aesCrypt->getIV()),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, AesCrypt::GCM_SUPPORT_VERSION, '>=')) {
|
||||||
|
array_splice($values, 1, 0, base64_encode($this->aesCrypt->getTag()));
|
||||||
|
}
|
||||||
|
|
||||||
return (new Cookie(static::COOKIE))
|
return (new Cookie(static::COOKIE))
|
||||||
->setExpire($this->getExpiresAt())
|
->setExpire($this->getExpiresAt())
|
||||||
->setHttpOnly(true)
|
->setHttpOnly(true)
|
||||||
->setValue(implode('|', [
|
->setValue(implode('|', $values));
|
||||||
$this->encryptedPassword,
|
|
||||||
base64_encode($this->aesCrypt->getTag()),
|
|
||||||
base64_encode($this->aesCrypt->getIV()),
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,7 +237,7 @@ class RememberMe
|
|||||||
public function persist($iv = null)
|
public function persist($iv = null)
|
||||||
{
|
{
|
||||||
if ($iv) {
|
if ($iv) {
|
||||||
$this->remove($iv);
|
$this->remove(bin2hex($iv));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->getDb()->insert(static::TABLE, [
|
$this->getDb()->insert(static::TABLE, [
|
||||||
@ -244,7 +263,7 @@ class RememberMe
|
|||||||
public function remove($iv)
|
public function remove($iv)
|
||||||
{
|
{
|
||||||
$this->getDb()->delete(static::TABLE, [
|
$this->getDb()->delete(static::TABLE, [
|
||||||
'random_iv = ?' => bin2hex($iv)
|
'random_iv = ?' => $iv
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -263,24 +282,6 @@ class RememberMe
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove specific remember me information from the database
|
|
||||||
*
|
|
||||||
* @param string $username
|
|
||||||
*
|
|
||||||
* @param $iv
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function removeSpecific($iv)
|
|
||||||
{
|
|
||||||
$this->getDb()->delete(static::TABLE, [
|
|
||||||
'random_iv = ?' => $iv
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all users using rememberme cookie
|
* Get all users using rememberme cookie
|
||||||
*
|
*
|
||||||
|
@ -5,7 +5,7 @@ namespace Icinga\Web;
|
|||||||
|
|
||||||
use ipl\Html\BaseHtmlElement;
|
use ipl\Html\BaseHtmlElement;
|
||||||
use ipl\Html\Html;
|
use ipl\Html\Html;
|
||||||
use ipl\Web\Url;
|
use ipl\Web\Url as iplWebUrl; //alias is needed for php5.6
|
||||||
use ipl\Web\Widget\Icon;
|
use ipl\Web\Widget\Icon;
|
||||||
use ipl\Web\Widget\Link;
|
use ipl\Web\Widget\Link;
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ class RememberMeUserDevicesList extends BaseHtmlElement
|
|||||||
|
|
||||||
$link = (new Link(
|
$link = (new Link(
|
||||||
new Icon('trash'),
|
new Icon('trash'),
|
||||||
Url::fromPath($this->getUrl())
|
iplWebUrl::fromPath($this->getUrl())
|
||||||
->addParams(
|
->addParams(
|
||||||
[
|
[
|
||||||
'name' => $this->getUsername(),
|
'name' => $this->getUsername(),
|
||||||
|
@ -5,7 +5,7 @@ namespace Icinga\Web;
|
|||||||
|
|
||||||
use ipl\Html\BaseHtmlElement;
|
use ipl\Html\BaseHtmlElement;
|
||||||
use ipl\Html\Html;
|
use ipl\Html\Html;
|
||||||
use ipl\Web\Url;
|
use ipl\Web\Url as iplWebUrl; //alias is needed for php5.6
|
||||||
use ipl\Web\Widget\Link;
|
use ipl\Web\Widget\Link;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +91,7 @@ class RememberMeUserList extends BaseHtmlElement
|
|||||||
$element = Html::tag('tr');
|
$element = Html::tag('tr');
|
||||||
$link = new Link(
|
$link = new Link(
|
||||||
$user->username,
|
$user->username,
|
||||||
Url::fromPath($this->getUrl())->addParams(['name' => $user->username]),
|
iplWebUrl::fromPath($this->getUrl())->addParams(['name' => $user->username]),
|
||||||
['title' => sprintf(t('Device list of %s'), $user->username)]
|
['title' => sprintf(t('Device list of %s'), $user->username)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user