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()) {
|
||||
$authenticated = false;
|
||||
$iv = null;
|
||||
try {
|
||||
$rememberMeOld = RememberMe::fromCookie();
|
||||
$iv = $rememberMeOld->getAesCrypt()->getIv();
|
||||
$authenticated = $rememberMeOld->authenticate();
|
||||
if ($authenticated) {
|
||||
$rememberMe = $rememberMeOld->renew();
|
||||
$this->getResponse()->setCookie($rememberMe->getCookie());
|
||||
$rememberMe->persist($rememberMeOld->getAesCrypt()->getIv());
|
||||
$rememberMe->persist($iv);
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
Logger::error("Can't authenticate user via remember me cookie: %s", $e->getMessage());
|
||||
|
@ -60,6 +62,7 @@ class AuthenticationController extends Controller
|
|||
}
|
||||
|
||||
if (! $authenticated) {
|
||||
(new RememberMe())->remove(bin2hex($iv));
|
||||
$this->getResponse()->setCookie(RememberMe::forget());
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +111,7 @@ class AuthenticationController extends Controller
|
|||
} else {
|
||||
if (RememberMe::hasCookie() && $this->hasDb()) {
|
||||
try {
|
||||
(new RememberMe())->remove(RememberMe::fromCookie()->getAesCrypt()->getIV());
|
||||
(new RememberMe())->remove(bin2hex(RememberMe::fromCookie()->getAesCrypt()->getIV()));
|
||||
} catch (RuntimeException $e) {
|
||||
// pass
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ class ManageUserDevicesController extends CompatController
|
|||
|
||||
public function deleteAction()
|
||||
{
|
||||
(new RememberMe())->removeSpecific($this->params->getRequired('fingerprint'));
|
||||
(new RememberMe())->remove($this->params->getRequired('fingerprint'));
|
||||
|
||||
$this->redirectNow(
|
||||
Url::fromPath('manage-user-devices/devices')
|
||||
|
|
|
@ -67,7 +67,7 @@ class MyDevicesController extends CompatController
|
|||
|
||||
public function deleteAction()
|
||||
{
|
||||
(new RememberMe())->removeSpecific($this->params->getRequired('fingerprint'));
|
||||
(new RememberMe())->remove($this->params->getRequired('fingerprint'));
|
||||
|
||||
$this->redirectNow('my-devices');
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
username varchar(254) COLLATE utf8mb4_unicode_ci 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,
|
||||
expires_at timestamp NULL DEFAULT NULL,
|
||||
ctime timestamp NULL DEFAULT NULL,
|
||||
|
|
|
@ -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,
|
||||
"username" character varying(254) 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,
|
||||
"expires_at" timestamp NULL DEFAULT NULL,
|
||||
"ctime" timestamp NULL DEFAULT NULL,
|
||||
|
|
|
@ -51,15 +51,31 @@ class AesCrypt
|
|||
private $tag;
|
||||
|
||||
/** @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)
|
||||
{
|
||||
$len = openssl_cipher_iv_length($this->method);
|
||||
$this->iv = random_bytes($len);
|
||||
if (version_compare(PHP_VERSION, self::GCM_SUPPORT_VERSION, '<')) {
|
||||
$this->method = 'AES-128-CBC';
|
||||
}
|
||||
|
||||
$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
|
||||
*
|
||||
|
@ -110,7 +126,8 @@ class AesCrypt
|
|||
public function getIV()
|
||||
{
|
||||
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;
|
||||
|
@ -162,8 +179,13 @@ class AesCrypt
|
|||
*/
|
||||
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());
|
||||
if (is_bool($decrypt) && $decrypt === false) {
|
||||
|
||||
if ($decrypt === false) {
|
||||
throw new RuntimeException('Decryption failed');
|
||||
}
|
||||
|
||||
|
@ -195,9 +217,13 @@ class AesCrypt
|
|||
*/
|
||||
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);
|
||||
|
||||
if (is_bool($encrypt) && $encrypt === false) {
|
||||
if ($encrypt === false) {
|
||||
throw new RuntimeException('Encryption failed');
|
||||
}
|
||||
|
||||
|
@ -217,4 +243,37 @@ class AesCrypt
|
|||
{
|
||||
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]);
|
||||
$iv = base64_decode(array_pop($data));
|
||||
$tag = base64_decode(array_pop($data));
|
||||
|
||||
$select = (new Select())
|
||||
->from(static::TABLE)
|
||||
|
@ -91,8 +90,23 @@ class RememberMe
|
|||
|
||||
$rememberMe->aesCrypt = (new AesCrypt())
|
||||
->setKey(hex2bin($rs->passphrase))
|
||||
->setTag($tag)
|
||||
->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->encryptedPassword = $data[0];
|
||||
|
||||
|
@ -140,14 +154,19 @@ class RememberMe
|
|||
*/
|
||||
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))
|
||||
->setExpire($this->getExpiresAt())
|
||||
->setHttpOnly(true)
|
||||
->setValue(implode('|', [
|
||||
$this->encryptedPassword,
|
||||
base64_encode($this->aesCrypt->getTag()),
|
||||
base64_encode($this->aesCrypt->getIV()),
|
||||
]));
|
||||
->setValue(implode('|', $values));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,7 +237,7 @@ class RememberMe
|
|||
public function persist($iv = null)
|
||||
{
|
||||
if ($iv) {
|
||||
$this->remove($iv);
|
||||
$this->remove(bin2hex($iv));
|
||||
}
|
||||
|
||||
$this->getDb()->insert(static::TABLE, [
|
||||
|
@ -244,7 +263,7 @@ class RememberMe
|
|||
public function remove($iv)
|
||||
{
|
||||
$this->getDb()->delete(static::TABLE, [
|
||||
'random_iv = ?' => bin2hex($iv)
|
||||
'random_iv = ?' => $iv
|
||||
]);
|
||||
|
||||
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
|
||||
*
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Icinga\Web;
|
|||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
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\Link;
|
||||
|
||||
|
@ -124,7 +124,7 @@ class RememberMeUserDevicesList extends BaseHtmlElement
|
|||
|
||||
$link = (new Link(
|
||||
new Icon('trash'),
|
||||
Url::fromPath($this->getUrl())
|
||||
iplWebUrl::fromPath($this->getUrl())
|
||||
->addParams(
|
||||
[
|
||||
'name' => $this->getUsername(),
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Icinga\Web;
|
|||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -91,7 +91,7 @@ class RememberMeUserList extends BaseHtmlElement
|
|||
$element = Html::tag('tr');
|
||||
$link = new Link(
|
||||
$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)]
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue