mirror of
https://github.com/nuxsmin/sysPass.git
synced 2026-03-19 06:46:49 +01:00
* [MOD] Improved API auth security. There is no need to provide the user's password, it will ask for a token password when generating it.
This commit is contained in:
@@ -29,6 +29,7 @@ defined('APP_ROOT') || die();
|
||||
use Defuse\Crypto\Exception\CryptoException;
|
||||
use SP\Core\Crypt\Crypt;
|
||||
use SP\Core\Crypt\Hash;
|
||||
use SP\Core\Crypt\Vault;
|
||||
use SP\Core\Exceptions\InvalidArgumentException;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\Core\Session;
|
||||
@@ -182,9 +183,9 @@ abstract class ApiBase implements ApiInterface
|
||||
protected function getMPass()
|
||||
{
|
||||
try {
|
||||
$key = $this->getParam('pass') . $this->getParam('authToken');
|
||||
$securedKey = Crypt::unlockSecuredKey($this->ApiTokenData->getAuthtokenKey(), $key);
|
||||
return Crypt::decrypt($this->ApiTokenData->getAuthtokenPass(), $securedKey, $key);
|
||||
/** @var Vault $Vault */
|
||||
$Vault = unserialize($this->ApiTokenData->getAuthtokenVault());
|
||||
return $Vault->getData($this->getParam('pass') . $this->getParam('authToken'));
|
||||
} catch (CryptoException $e) {
|
||||
throw new SPException(SPException::SP_ERROR, __('Error interno', false), $e->getMessage());
|
||||
}
|
||||
|
||||
@@ -511,8 +511,6 @@ class ConfigActionController implements ItemControllerInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$hashMPass = Hash::hashKey($newMasterPass);
|
||||
|
||||
if (!$noAccountPassChange) {
|
||||
Util::lockApp();
|
||||
|
||||
@@ -554,7 +552,7 @@ class ConfigActionController implements ItemControllerInterface
|
||||
Util::unlockApp();
|
||||
}
|
||||
|
||||
ConfigDB::setCacheConfigValue('masterPwd', $hashMPass);
|
||||
ConfigDB::setCacheConfigValue('masterPwd', Hash::hashKey($newMasterPass));
|
||||
ConfigDB::setCacheConfigValue('lastupdatempass', time());
|
||||
|
||||
$this->LogMessage->setAction(__('Actualizar Clave Maestra', false));
|
||||
|
||||
@@ -28,7 +28,6 @@ defined('APP_ROOT') || die();
|
||||
|
||||
use SP\Account\AccountHistoryUtil;
|
||||
use SP\Account\AccountUtil;
|
||||
use SP\Api\ApiTokensUtil;
|
||||
use SP\Config\Config;
|
||||
use SP\Controller\Grids\Items;
|
||||
use SP\Core\ActionsInterface;
|
||||
@@ -36,7 +35,6 @@ use SP\Core\Exceptions\SPException;
|
||||
use SP\Core\Template;
|
||||
use SP\DataModel\ItemSearchData;
|
||||
use SP\Http\Request;
|
||||
use SP\Mgmt\ApiTokens\ApiToken;
|
||||
use SP\Mgmt\ApiTokens\ApiTokenSearch;
|
||||
use SP\Mgmt\Categories\CategorySearch;
|
||||
use SP\Mgmt\Customers\CustomerSearch;
|
||||
|
||||
@@ -28,7 +28,6 @@ defined('APP_ROOT') || die();
|
||||
|
||||
use SP\Account\AccountHistoryUtil;
|
||||
use SP\Account\AccountUtil;
|
||||
use SP\Api\ApiTokensUtil;
|
||||
use SP\Config\Config;
|
||||
use SP\Controller\Grids\Items;
|
||||
use SP\Core\ActionsInterface;
|
||||
|
||||
@@ -29,7 +29,7 @@ defined('APP_ROOT') || die();
|
||||
use SP\Account\Account;
|
||||
use SP\Account\AccountAcl;
|
||||
use SP\Account\AccountHistory;
|
||||
use SP\Api\ApiTokensUtil;
|
||||
use SP\Mgmt\ApiTokens\ApiTokensUtil;
|
||||
use SP\Core\ActionsInterface;
|
||||
use SP\Core\Crypt\Crypt;
|
||||
use SP\Core\Crypt\Session as CryptSession;
|
||||
@@ -48,7 +48,6 @@ use SP\DataModel\GroupData;
|
||||
use SP\DataModel\ProfileData;
|
||||
use SP\DataModel\TagData;
|
||||
use SP\DataModel\UserData;
|
||||
use SP\DataModel\UserPassData;
|
||||
use SP\Http\Request;
|
||||
use SP\Log\Email;
|
||||
use SP\Log\Log;
|
||||
@@ -72,7 +71,6 @@ use SP\Mgmt\Users\UserUtil;
|
||||
use SP\Util\Checks;
|
||||
use SP\Util\ImageUtil;
|
||||
use SP\Util\Json;
|
||||
use SP\Util\Util;
|
||||
|
||||
/**
|
||||
* Class AccItemMgmt
|
||||
|
||||
@@ -54,18 +54,20 @@ class Vault
|
||||
/**
|
||||
* Regenerar la clave de sesión
|
||||
*
|
||||
* @param string $key
|
||||
* @throws \Defuse\Crypto\Exception\BadFormatException
|
||||
* @throws \Defuse\Crypto\Exception\CryptoException
|
||||
* @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
|
||||
* @return Vault
|
||||
*/
|
||||
public function reKey()
|
||||
public function reKey($key = null)
|
||||
{
|
||||
$this->timeUpdated = time();
|
||||
$sessionMPass = $this->getData();
|
||||
$sessionMPass = $this->getData($key);
|
||||
|
||||
SessionUtil::regenerate();
|
||||
|
||||
$this->saveData($sessionMPass);
|
||||
$this->saveData($sessionMPass, $key);
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -73,16 +75,18 @@ class Vault
|
||||
/**
|
||||
* Devolver la clave maestra de la sesión
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
* @throws \Defuse\Crypto\Exception\CryptoException
|
||||
* @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
|
||||
* @throws \Defuse\Crypto\Exception\BadFormatException
|
||||
*/
|
||||
public function getData()
|
||||
public function getData($key = null)
|
||||
{
|
||||
$securedKey = Crypt::unlockSecuredKey($this->key, $this->getKey());
|
||||
$key = $key ?: $this->getKey();
|
||||
$securedKey = Crypt::unlockSecuredKey($this->key, $key);
|
||||
|
||||
return Crypt::decrypt($this->data, $securedKey, $this->getKey());
|
||||
return Crypt::decrypt($this->data, $securedKey, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,19 +103,21 @@ class Vault
|
||||
* Guardar la clave maestra en la sesión
|
||||
*
|
||||
* @param $data
|
||||
* @param string $key
|
||||
* @return $this
|
||||
* @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
|
||||
* @throws \Defuse\Crypto\Exception\BadFormatException
|
||||
* @throws \Defuse\Crypto\Exception\CryptoException
|
||||
*/
|
||||
public function saveData($data)
|
||||
public function saveData($data, $key = null)
|
||||
{
|
||||
if ($this->timeSet === 0) {
|
||||
$this->timeSet = time();
|
||||
}
|
||||
|
||||
$this->key = Crypt::makeSecuredKey($this->getKey());
|
||||
$this->data = Crypt::encrypt($data, $this->key, $this->getKey());
|
||||
$key = $key ?: $this->getKey();
|
||||
$this->key = Crypt::makeSecuredKey($key);
|
||||
$this->data = Crypt::encrypt($data, $this->key, $key);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
namespace SP\DataModel;
|
||||
|
||||
use SP\Core\Crypt\Vault;
|
||||
|
||||
/**
|
||||
* Class ApiTokenData
|
||||
*
|
||||
@@ -36,13 +38,9 @@ class ApiTokenData extends DataModelBase implements DataModelInterface
|
||||
*/
|
||||
public $authtoken_id;
|
||||
/**
|
||||
* @var string
|
||||
* @var Vault
|
||||
*/
|
||||
public $authtoken_key;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $authtoken_pass;
|
||||
public $authtoken_vault;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
@@ -85,19 +83,19 @@ class ApiTokenData extends DataModelBase implements DataModelInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @return Vault
|
||||
*/
|
||||
public function getAuthtokenKey()
|
||||
public function getAuthtokenVault()
|
||||
{
|
||||
return $this->authtoken_key;
|
||||
return $this->authtoken_vault;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $authtoken_key
|
||||
* @param Vault $authtoken_vault
|
||||
*/
|
||||
public function setAuthtokenKey($authtoken_key)
|
||||
public function setAuthtokenVault(Vault $authtoken_vault)
|
||||
{
|
||||
$this->authtoken_key = $authtoken_key;
|
||||
$this->authtoken_vault = $authtoken_vault;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,20 +209,4 @@ class ApiTokenData extends DataModelBase implements DataModelInterface
|
||||
{
|
||||
$this->authtoken_hash = $authtoken_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthtokenPass()
|
||||
{
|
||||
return $this->authtoken_pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authtoken_pass
|
||||
*/
|
||||
public function setAuthtokenPass($authtoken_pass)
|
||||
{
|
||||
$this->authtoken_pass = $authtoken_pass;
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ class ApiTokenForm extends FormBase implements FormInterface
|
||||
throw new ValidationException(__('Usuario no indicado', false));
|
||||
} elseif ($this->ApiTokenData->getAuthtokenActionId() === 0) {
|
||||
throw new ValidationException(__('Acción no indicada', false));
|
||||
} elseif ($this->ApiTokenData->getAuthtokenKey() === '') {
|
||||
} elseif ($this->ApiTokenData->getAuthtokenHash() === '') {
|
||||
throw new ValidationException(__('La clave no puede estar en blanco', false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
|
||||
namespace SP\Mgmt\ApiTokens;
|
||||
|
||||
use SP\Core\Crypt\Crypt;
|
||||
use SP\Core\Crypt\Hash;
|
||||
use SP\Core\Crypt\Session as CryptSession;
|
||||
use SP\Core\Crypt\Vault;
|
||||
use SP\Core\Exceptions\SPException;
|
||||
use SP\Core\Session;
|
||||
use SP\DataModel\ApiTokenData;
|
||||
@@ -67,8 +67,7 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
authtoken_actionId = ?,
|
||||
authtoken_createdBy = ?,
|
||||
authtoken_token = ?,
|
||||
authtoken_key = ?,
|
||||
authtoken_pass = ?,
|
||||
authtoken_vault = ?,
|
||||
authtoken_hash = ?,
|
||||
authtoken_startDate = UNIX_TIMESTAMP()';
|
||||
|
||||
@@ -78,8 +77,7 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
$Data->addParam($this->itemData->getAuthtokenActionId());
|
||||
$Data->addParam(Session::getUserData()->getUserId());
|
||||
$Data->addParam($token);
|
||||
$Data->addParam($this->itemData->getAuthtokenKey());
|
||||
$Data->addParam($this->itemData->getAuthtokenPass());
|
||||
$Data->addParam(serialize($this->itemData->getAuthtokenVault()));
|
||||
$Data->addParam(Hash::hashKey($this->itemData->getAuthtokenHash()));
|
||||
$Data->setOnErrorMessage(__('Error interno', false));
|
||||
|
||||
@@ -185,8 +183,7 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
authtoken_actionId = ?,
|
||||
authtoken_createdBy = ?,
|
||||
authtoken_token = ?,
|
||||
authtoken_key = ?,
|
||||
authtoken_pass = ?,
|
||||
authtoken_vault = ?,
|
||||
authtoken_hash = ?,
|
||||
authtoken_startDate = UNIX_TIMESTAMP()
|
||||
WHERE authtoken_id = ? LIMIT 1';
|
||||
@@ -197,8 +194,7 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
$Data->addParam($this->itemData->getAuthtokenActionId());
|
||||
$Data->addParam(Session::getUserData()->getUserId());
|
||||
$Data->addParam($token);
|
||||
$Data->addParam($this->itemData->getAuthtokenKey());
|
||||
$Data->addParam($this->itemData->getAuthtokenPass());
|
||||
$Data->addParam(serialize($this->itemData->getAuthtokenVault()));
|
||||
$Data->addParam(Hash::hashKey($this->itemData->getAuthtokenHash()));
|
||||
$Data->addParam($this->itemData->getAuthtokenId());
|
||||
$Data->setOnErrorMessage(__('Error interno', false));
|
||||
@@ -247,7 +243,7 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
'UPDATE authTokens
|
||||
SET authtoken_token = ?,
|
||||
authtoken_hash = ?,
|
||||
authtoken_key = ?,
|
||||
authtoken_vault = ?,
|
||||
authtoken_pass = ?,
|
||||
authtoken_startDate = UNIX_TIMESTAMP()
|
||||
WHERE authtoken_userId = ? LIMIT 1';
|
||||
@@ -256,8 +252,7 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
$Data->setQuery($query);
|
||||
$Data->addParam($this->generateToken());
|
||||
$Data->addParam(Hash::hashKey($this->itemData->getAuthtokenHash()));
|
||||
$Data->addParam($this->itemData->getAuthtokenKey());
|
||||
$Data->addParam($this->itemData->getAuthtokenPass());
|
||||
$Data->addParam(serialize($this->itemData->getAuthtokenVault()));
|
||||
$Data->addParam($this->itemData->getAuthtokenUserId());
|
||||
$Data->setOnErrorMessage(__('Error interno', false));
|
||||
|
||||
@@ -274,11 +269,10 @@ class ApiToken extends ApiTokenBase implements ItemInterface
|
||||
*/
|
||||
private function setSecureData($token)
|
||||
{
|
||||
$key = $this->itemData->getAuthtokenHash() . $token;
|
||||
$securedKey = Crypt::makeSecuredKey($key);
|
||||
$Vault = new Vault();
|
||||
$Vault->saveData(CryptSession::getSessionKey(), $this->itemData->getAuthtokenHash() . $token);
|
||||
|
||||
$this->itemData->setAuthtokenKey($securedKey);
|
||||
$this->itemData->setAuthtokenPass(Crypt::encrypt(CryptSession::getSessionKey(), $securedKey, $key));
|
||||
$this->itemData->setAuthtokenVault($Vault);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace SP\Api;
|
||||
namespace SP\Mgmt\ApiTokens;
|
||||
|
||||
use SP\Core\Acl;
|
||||
use SP\Core\ActionsInterface;
|
||||
@@ -8,7 +8,6 @@ ALTER TABLE `usrData`
|
||||
CHANGE COLUMN `user_mPass` `user_mPass` VARBINARY(1000) NULL DEFAULT NULL,
|
||||
CHANGE COLUMN `user_mIV` `user_mKey` VARBINARY(1000) NULL DEFAULT NULL;
|
||||
ALTER TABLE `authTokens`
|
||||
ADD COLUMN `authtoken_pass` VARBINARY(1000) NULL,
|
||||
ADD COLUMN `authtoken_key` VARBINARY(1000) NULL,
|
||||
ADD COLUMN `authtoken_vault` VARBINARY(2000) NULL,
|
||||
ADD COLUMN `authtoken_hash` VARBINARY(100) NULL;
|
||||
|
||||
|
||||
2
inc/themes/material-blue/css/styles.min.css
vendored
2
inc/themes/material-blue/css/styles.min.css
vendored
File diff suppressed because one or more lines are too long
2
inc/themes/material-blue/js/app-theme.min.js
vendored
2
inc/themes/material-blue/js/app-theme.min.js
vendored
@@ -1,5 +1,5 @@
|
||||
var $jscomp={scope:{},findInternal:function(a,g,e){a instanceof String&&(a=String(a));for(var h=a.length,k=0;k<h;k++){var l=a[k];if(g.call(e,l,k,a))return{i:k,v:l}}return{i:-1,v:void 0}}};$jscomp.defineProperty="function"==typeof Object.defineProperties?Object.defineProperty:function(a,g,e){if(e.get||e.set)throw new TypeError("ES3 does not support getters and setters.");a!=Array.prototype&&a!=Object.prototype&&(a[g]=e.value)};
|
||||
$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global?global:a};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(a,g,e,h){if(g){e=$jscomp.global;a=a.split(".");for(h=0;h<a.length-1;h++){var k=a[h];k in e||(e[k]={});e=e[k]}a=a[a.length-1];h=e[a];g=g(h);g!=h&&null!=g&&$jscomp.defineProperty(e,a,{configurable:!0,writable:!0,value:g})}};
|
||||
$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);$jscomp.polyfill=function(a,g,e,h){if(g){e=$jscomp.global;a=a.split(".");for(h=0;h<a.length-1;h++){var k=a[h];k in e||(e[k]={});e=e[k]}a=a[a.length-1];h=e[a];g=g(h);g!=h&&null!=g&&$jscomp.defineProperty(e,a,{configurable:!0,writable:!0,value:g})}};
|
||||
$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,e){return $jscomp.findInternal(this,a,e).v}},"es6-impl","es3");
|
||||
sysPass.Theme=function(a){var g=a.log,e={elems:{$wrap:$("#wrap-loading"),$loading:$("#loading")},show:function(a){void 0!==a&&!0===a&&e.elems.$wrap.addClass("overlay-full");e.elems.$wrap.show();e.elems.$loading.addClass("is-active")},hide:function(){e.elems.$wrap.removeClass("overlay-full").hide();e.elems.$loading.removeClass("is-active")},upgradeFull:function(){e.elems.$wrap.addClass("overlay-full")}},h=function(b){for(var d=0,f="",c;d<a.passwordData.complexity.numlength;)c=Math.floor(100*Math.random())%
|
||||
94+33,!a.passwordData.complexity.symbols&&(33<=c&&47>=c||58<=c&&64>=c||91<=c&&96>=c||123<=c&&126>=c)||!a.passwordData.complexity.numbers&&48<=c&&57>=c||!a.passwordData.complexity.uppercase&&65<=c&&90>=c||(d++,f+=String.fromCharCode(c));$("#viewPass").attr("title",f);var e=zxcvbn(f);a.passwordData.passLength=f.length;b?(d=b.parent(),c=$("#"+b.attr("id")+"R"),a.outputResult(e,b),b=new MaterialTextfield,d.find("input:password").val(f),d.addClass(b.CssClasses_.IS_DIRTY).removeClass(b.CssClasses_.IS_INVALID),
|
||||
|
||||
Reference in New Issue
Block a user