/trunk/doc/developer_notes/server_secret.md |
---|
4,76 → 4,48 |
In the base configuration, you will see something like this: |
OIDplus::baseConfig()->setValue("SERVER_SECRET", "................................"); |
OIDplus::baseConfig()->setValue('SERVER_SECRET', '................................'); |
This value is chosen randomly by the configuration file generator (setup). |
Derivation of secrets and auth keys |
----------------------------------- |
Where is SERVER_SECRET being used? |
---------------------------------- |
Important: The usage of `OIDplus::baseConfig()->getValue("SERVER_SECRET")` |
is deprecated due to security considerations. |
Instead, please always use `OIDplus::authUtils()->makeSecret()` |
with a unique `$data` argument (prefer a GUID) |
if you need a secret that is derived from the server secret. |
If you want to generate an authentication key (e.g. to send via an email), |
use `OIDplus::authUtils()->makeAuthKey()` |
with a unique `$data` argument (prefer a GUID) |
in combination with `OIDplus::authUtils()->validateAuthKey()`. |
This auth key is NOT temporary by default so you need to make sure |
that you encode a timestamp in it. |
Implementation |
-------------- |
`makeAuthKey(data) = makeSecret(data) = sha3_512_hmac(data, "OIDplus:" + SERVER_SECRET)` |
Currently, the implementation of `makeAuthKey` and `makeSecret` |
is the same, but you should only use `makeAuthKey` |
if you combine it with `validateAuthKey`, otherwise, you |
should use `makeSecret`. This makes the code easier to understand. |
Where are makeAuthKey and makeSecret being used? |
------------------------------------------------ |
System / Core: |
System: |
- Auth content Store (OIDplusAuthContentStoreJWT.class.php): |
Key to sign JWT tokens (used for Automated AJAX requests, REST API and logins with "Remember me") using PBKDF2+HMAC |
* If a private/public key pair exists: Sign the JWT using that private key. |
* Otherwise sign it with: |
`JWT = HS512(hash_pbkdf2("sha512", OIDplus::authUtils()->makeSecret("0be35e52-f4ef-11ed-b67e-3c4a92df8582"), "", 10000, 64/*256bit*/, false))` |
Key to sign JWT tokens (used for Automated AJAX requests and logins with "Remember me") using PBKDF2+HMAC |
(ONLY if the server does not have a Public/Private key pair!) |
`JWT = HS512(hash_pbkdf2('sha512', SERVER_SECRET+"/OIDplusAuthContentStoreJWT", '', 10000, 64/*256bit*/, false))` |
- Session Handler (OIDplusSessionHandler.class.php): |
Encryption of session contents (regular logins) |
* if OpenSSL is installed: sha512-pbkdf2 + AES-256-CBC + sha3-512-hmac |
* if OpenSSL is not installed: sha3-512-hmac |
* In both cases, the key is `OIDplus::authUtils()->makeSecret("b118abc8-f4ec-11ed-86ca-3c4a92df8582")`. |
if OpenSSL is installed: sha512-pbkdf2 + AES-256-CBC + sha3-512-hmac |
if OpenSSL is not installed: sha3-512-hmac |
- Auth utils: Generation of auth keys |
`makeAuthKey(data) = sha3_512_hmac(data, "authkey:"+SERVER_SECRET);` |
used at plugin forgot RA password (public/091): |
`makeAuthKey("reset_password;" + email + ";" + timestamp) |
= sha3_512_hmac("reset_password;" + email + ";" + timestamp, "authkey:"+SERVER_SECRET);` |
used at plugin ViaThinkSoft FreeOID activation (public/200): |
`makeAuthKey("com.viathinksoft.freeoid.activate_freeoid;" + email + ";" + timestamp) |
= sha3_512_hmac("com.viathinksoft.freeoid.activate_freeoid;" + email + ";" + timestamp, "authkey:"+SERVER_SECRET);` |
used at plugin invite RA (ra/092): |
`makeAuthKey("activate_ra;" + email + ";" + timestamp) |
= sha3_512_hmac("activate_ra;" + email + ";" + timestamp, "authkey:"+SERVER_SECRET);` |
used at plugin change RA email (ra/102): |
`makeAuthKey("activate_new_ra_email;" + old_email + ";" + new_email + ";" + timestamp) |
= sha3_512_hmac("activate_new_ra_email;" + old_email + ";" + new_email + ";" + timestamp, "authkey:"+SERVER_SECRET);` |
Temporary auth keys (sent via email etc.): |
* used at plugin forgot RA password (public/091): |
`makeAuthKey("93a16dbe-f4fb-11ed-b67e-3c4a92df8582:" + email + "/" + timestamp)` |
* used at plugin ViaThinkSoft FreeOID activation (public/200): |
`makeAuthKey("40c87e20-f4fb-11ed-86ca-3c4a92df8582:" + email + "/" + timestamp)` |
* used at plugin invite RA (ra/092): |
`makeAuthKey("ed840c3e-f4fa-11ed-b67e-3c4a92df8582:" + email + "/" + timestamp)` |
* used at plugin change RA email (ra/102): |
`makeAuthKey("5ef24124-f4fb-11ed-b67e-3c4a92df8582:" + old_email + "/" + new_email + "/" + timestamp)` |
Plugin WHOIS (public/100): |
- Authentication token for hidden OIDs = `smallhash(SERVER_SECRET + "/WHOIS/" + id);` |
Plugin OID-IP (public/100): |
- Authentication token for hidden OIDs = `smallhash(OIDplus::authUtils()->makeSecret("d8f44c7c-f4e9-11ed-86ca-3c4a92df8582:" + id))` |
Plugin VNag version check (admin/901): |
- Webreader password = `OIDplus::authUtils()->makeSecret("65d9f488-f4eb-11ed-b67e-3c4a92df8582")` |
- Webreader password = `sha3_512(SERVER_SECRET + "/VNAG")` |
Plugin RDAP (frdl): |
- `OIDplus::authUtils()->makeSecret("cee75760-f4f8-11ed-b67e-3c4a92df8582")` is used to generate a cache filename |
--- |
Plugin VTS Client Challenge Captcha: |
- Challenge integrity : `OIDplus::authUtils()->makeAuthKey("797bfc34-f4fa-11ed-86ca-3c4a92df8582:" + challenge)` |
- Cache filename : `"vts_client_challenge_" + OIDplus::authUtils()->makeSecret("461f4a9e-f4fa-11ed-86ca-3c4a92df8582:" + ipTarget + "/" + random) + ".tmp"` |
Important: Please never use SERVER_SECRET alone for any hashing/HMAC without adding any context to it. |
GUID Registry |
------------- |
- Example: Bad `hmac(message, SERVER_SECRET)` |
- Example: Good `hmac(message, 'xyz:'.SERVER_SECRET)` |
The "real GUIDs" are documented at the [ViaThinkSoft OIDplus Registration Authority](https://oidplus.viathinksoft.com/oidplus/?goto=guid%3Aoidplus%2FauthRealms). |
Reason: Since the SERVER_SECRET is used at many different places, we must make sure that the calculated values do not reveal information about the SERVER_SECRET in any kind. |
/trunk/includes/classes/OIDplusAuthContentStoreJWT.class.php |
---|
414,7 → 414,7 |
$k = new \Firebase\JWT\Key($pubKey, 'RS256'); // RSA+SHA256 ist hardcoded in getPkiStatus() generation |
$this->content = (array) \Firebase\JWT\JWT::decode($jwt, $k); |
} else { |
$key = OIDplus::authUtils()->makeSecret('0be35e52-f4ef-11ed-b67e-3c4a92df8582'); |
$key = OIDplus::baseConfig()->getValue('SERVER_SECRET', '').'/OIDplusAuthContentStoreJWT'; |
$key = hash_pbkdf2('sha512', $key, '', 10000, 32/*256bit*/, false); |
$k = new \Firebase\JWT\Key($key, 'HS512'); // HMAC+SHA512 is hardcoded here |
$this->content = (array) \Firebase\JWT\JWT::decode($jwt, $k); |
436,7 → 436,7 |
$privKey = OIDplus::getSystemPrivateKey(); |
return \Firebase\JWT\JWT::encode($payload, $privKey, 'RS256'); // RSA+SHA256 ist hardcoded in getPkiStatus() generation |
} else { |
$key = OIDplus::authUtils()->makeSecret('0be35e52-f4ef-11ed-b67e-3c4a92df8582'); |
$key = OIDplus::baseConfig()->getValue('SERVER_SECRET', '').'/OIDplusAuthContentStoreJWT'; |
$key = hash_pbkdf2('sha512', $key, '', 10000, 32/*256bit*/, false); |
return \Firebase\JWT\JWT::encode($payload, $key, 'HS512'); // HMAC+SHA512 is hardcoded here |
} |
/trunk/includes/classes/OIDplusAuthUtils.class.php |
---|
380,7 → 380,7 |
OIDplus::logger()->log("V2:[OK]A", "Admin logged out (%1)", $loginfo); |
} |
// Authentication keys for generating secrets or validating arguments (e.g. sent by mail) |
// Authentication keys for validating arguments (e.g. sent by mail) |
/** |
* @param string $data |
387,17 → 387,8 |
* @return string |
* @throws OIDplusException |
*/ |
public function makeSecret(string $data): string { |
return sha3_512_hmac($data, 'OIDplus:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'), false); |
} |
/** |
* @param string $data |
* @return string |
* @throws OIDplusException |
*/ |
public function makeAuthKey(string $data): string { |
return $this->makeSecret($data); |
return sha3_512_hmac($data, 'authkey:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'), false); |
} |
/** |
/trunk/includes/classes/OIDplusBaseConfig.class.php |
---|
47,12 → 47,6 |
* @return mixed|null |
*/ |
public function getValue(string $name, $default=null) { |
if ($name == 'SERVER_SECRET') { |
$caller_class = debug_backtrace()[1]['class']; |
if (!str_starts_with($caller_class, 'ViaThinkSoft\\OIDplus\\')) { |
throw new OIDplusException(_L('Outdated plugin: Calling %1 from a plugin is deprecated. Please use %2 instead', 'SERVER_SECRET', 'OIDplus::authUtils()->makeSecret()')); |
} |
} |
return $this->exists($name) ? $this->data[$name] : $default; |
} |
/trunk/includes/classes/OIDplusSessionHandler.class.php |
---|
40,7 → 40,7 |
*/ |
public function __construct() { |
$this->sessionLifetime = OIDplus::baseConfig()->getValue('SESSION_LIFETIME', 30*60); |
$this->secret = OIDplus::authUtils()->makeSecret('b118abc8-f4ec-11ed-86ca-3c4a92df8582'); |
$this->secret = OIDplus::baseConfig()->getValue('SERVER_SECRET'); |
// **PREVENTING SESSION HIJACKING** |
// Prevents javascript XSS attacks aimed to steal the session ID |
/trunk/plugins/frdl/publicPages/1276945_rdap/OIDplusRDAP.class.php |
---|
81,7 → 81,7 |
$cacheFile = $this->rdapCacheDir. 'rdap_' |
.sha1(\get_current_user() |
. $this->rdapBaseUri.__FILE__.$query |
.OIDplus::authUtils()->makeSecret('cee75760-f4f8-11ed-b67e-3c4a92df8582') |
.OIDplus::baseConfig()->getValue('SERVER_SECRET', sha1(__FILE__.\get_current_user()) ) |
) |
.'.' |
.strlen( $this->rdapBaseUri.$query ) |
/trunk/plugins/viathinksoft/adminPages/901_vnag_version_check/OIDplusPageAdminVNagVersionCheck.class.php |
---|
142,7 → 142,7 |
* @throws OIDplusException |
*/ |
public static function vnag_password(): string { |
return OIDplus::authUtils()->makeSecret('65d9f488-f4eb-11ed-b67e-3c4a92df8582'); |
return sha3_512(OIDplus::baseConfig()->getValue('SERVER_SECRET').'/VNAG'); |
} |
} |
/trunk/plugins/viathinksoft/captcha/vts_challenge/OIDplusCaptchaPluginVtsClientChallenge.class.php |
---|
56,6 → 56,8 |
*/ |
public function action(string $actionID, array $params): array { |
if ($actionID == 'get_challenge') { |
$server_secret='VtsClientChallenge:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'); |
$offset = 0; // doesn't matter |
$min = $offset; |
$max = $offset + OIDplus::baseConfig()->getValue('VTS_CAPTCHA_COMPLEXITY', 50000); |
64,8 → 66,8 |
$starttime = time(); |
$random = mt_rand($min,$max); |
$ip_target = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; |
$challenge = sha3_512($starttime.'/'.$ip_target.'/'.$random); // $random is secret! |
$challenge_integrity = OIDplus::authUtils()->makeAuthKey('797bfc34-f4fa-11ed-86ca-3c4a92df8582:'.$challenge); |
$challenge = sha3_512($starttime.'/'.$ip_target.'/'.$random); |
$challenge_integrity = sha3_512_hmac($challenge,$server_secret); |
$send_to_client = array($starttime, $ip_target, $challenge, $min, $max, $challenge_integrity); |
$open_trans_file = self::getOpenTransFileName($ip_target, $random); |
92,6 → 94,7 |
*/ |
private static function getOpenTransFileName(string $ip_target, $random): string { |
$dir = OIDplus::localpath().'/userdata/cache'; |
$server_secret='VtsClientChallenge:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'); |
// First, delete challenges which were never completed |
$files = glob($dir.'/vts_client_challenge_*.tmp'); |
102,7 → 105,7 |
@unlink($file); |
} |
return $dir.'/vts_client_challenge_'.OIDplus::authUtils()->makeSecret('461f4a9e-f4fa-11ed-86ca-3c4a92df8582:'.$ip_target.'/'.$random).'.tmp'; |
return $dir.'/vts_client_challenge_'.sha3_512_hmac($ip_target.'/'.$random, $server_secret).'.tmp'; |
} |
/** |
131,6 → 134,8 |
if (is_null($fieldname)) $fieldname = 'vts_validation_result'; |
$server_secret='VtsClientChallenge:'.OIDplus::baseConfig()->getValue('SERVER_SECRET'); |
if (!isset($params[$fieldname])) throw new OIDplusException(_L('No challenge response found').' (A)'); |
$client_response = @json_decode($params[$fieldname], true); |
151,7 → 156,7 |
throw new OIDplusException(_L('IP address has changed. Please try again. (current IP %1, expected %2)', $current_ip, $ip_target)); |
} else if (time()-$starttime > OIDplus::baseConfig()->getValue('VTS_CAPTCHA_MAXTIME', 10*60/*10 minutes*/)) { |
throw new OIDplusException(_L('Challenge expired. Please try again.')); |
} else if (!OIDplus::authUtils()->validateAuthKey('797bfc34-f4fa-11ed-86ca-3c4a92df8582:'.$challenge, $challenge_integrity)) { |
} else if ($challenge_integrity != sha3_512_hmac($challenge,$server_secret)) { |
throw new OIDplusException(_L('Challenge integrity failed')); |
} else if ($challenge !== sha3_512($starttime.'/'.$ip_target.'/'.$answer)) { |
throw new OIDplusException(_L('Wrong answer')); |
/trunk/plugins/viathinksoft/language/dede/messages.xml |
---|
5046,14 → 5046,6 |
</message> |
<message> |
<source><![CDATA[ |
Outdated plugin: Calling %1 from a plugin is deprecated. Please use %2 instead |
]]></source> |
<target><![CDATA[ |
Veraltetes Plugin: Das Aufrufen von %1 ist ab sofort verboten. Es muss zukünftig %2 verwendet werden |
]]></target> |
</message> |
<message> |
<source><![CDATA[ |
Output format |
]]></source> |
<target><![CDATA[ |
/trunk/plugins/viathinksoft/publicPages/091_forgot_password/OIDplusPagePublicForgotPassword.class.php |
---|
46,7 → 46,7 |
OIDplus::logger()->log("V2:[WARN]RA(%1)", "A new password for '%1' was requested (forgot password)", $email); |
$timestamp = time(); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:reset_password$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('93a16dbe-f4fb-11ed-b67e-3c4a92df8582:'.$email.'/'.$timestamp)); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:reset_password$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('reset_password;'.$email.';'.$timestamp)); |
$message = $this->getForgotPasswordText($params['email']); |
$message = str_replace('{{ACTIVATE_URL}}', $activate_url, $message); |
69,7 → 69,7 |
$auth = $params['auth']; |
$timestamp = $params['timestamp']; |
if (!OIDplus::authUtils()->validateAuthKey('93a16dbe-f4fb-11ed-b67e-3c4a92df8582:'.$email.'/'.$timestamp, $auth)) { |
if (!OIDplus::authUtils()->validateAuthKey('reset_password;'.$email.';'.$timestamp, $auth)) { |
throw new OIDplusException(_L('Invalid auth key')); |
} |
/trunk/plugins/viathinksoft/publicPages/100_whois/OIDplusPagePublicWhois.class.php |
---|
233,6 → 233,6 |
* @throws OIDplusException |
*/ |
public static function genWhoisAuthToken(string $id): int { |
return smallhash(OIDplus::authUtils()->makeSecret('d8f44c7c-f4e9-11ed-86ca-3c4a92df8582:'.$id)); |
return smallhash(OIDplus::baseConfig()->getValue('SERVER_SECRET').'/WHOIS/'.$id); |
} |
} |
/trunk/plugins/viathinksoft/publicPages/200_viathinksoft_freeoid/OIDplusPagePublicFreeOID.class.php |
---|
82,7 → 82,7 |
OIDplus::logger()->log("V2:[INFO]OID(oid:%1)+RA(%2)", "Requested a free OID for email '%2' to be placed into root '%1'", $root_oid, $email); |
$timestamp = time(); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:com.viathinksoft.freeoid.activate_freeoid$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('40c87e20-f4fb-11ed-86ca-3c4a92df8582:'.$email.'/'.$timestamp)); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:com.viathinksoft.freeoid.activate_freeoid$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('com.viathinksoft.freeoid.activate_freeoid;'.$email.';'.$timestamp)); |
$message = file_get_contents(__DIR__ . '/request_msg.tpl'); |
$message = str_replace('{{SYSTEM_URL}}', OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL), $message); |
103,7 → 103,7 |
$auth = $params['auth']; |
$timestamp = $params['timestamp']; |
if (!OIDplus::authUtils()->validateAuthKey('40c87e20-f4fb-11ed-86ca-3c4a92df8582:'.$email.'/'.$timestamp, $auth)) { |
if (!OIDplus::authUtils()->validateAuthKey('com.viathinksoft.freeoid.activate_freeoid;'.$email.';'.$timestamp, $auth)) { |
throw new OIDplusException(_L('Invalid auth key')); |
} |
/trunk/plugins/viathinksoft/raPages/092_invite/OIDplusPageRaInvite.class.php |
---|
47,7 → 47,7 |
OIDplus::logger()->log("V2:[INFO]RA(%1)", "RA '%1' has been invited", $email); |
$timestamp = time(); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:activate_ra$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('ed840c3e-f4fa-11ed-b67e-3c4a92df8582:'.$email.'/'.$timestamp)); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:activate_ra$'.$email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('activate_ra;'.$email.';'.$timestamp)); |
$message = $this->getInvitationText($email); |
$message = str_replace('{{ACTIVATE_URL}}', $activate_url, $message); |
70,7 → 70,7 |
$auth = $params['auth']; |
$timestamp = $params['timestamp']; |
if (!OIDplus::authUtils()->validateAuthKey('ed840c3e-f4fa-11ed-b67e-3c4a92df8582:'.$email.'/'.$timestamp, $auth)) { |
if (!OIDplus::authUtils()->validateAuthKey('activate_ra;'.$email.';'.$timestamp, $auth)) { |
throw new OIDplusException(_L('Invalid auth key')); |
} |
/trunk/plugins/viathinksoft/raPages/102_change_email/OIDplusPageRaChangeEMail.class.php |
---|
95,7 → 95,7 |
OIDplus::logger()->log("V2:[INFO]RA(%1)+RA(%2)", "Requested email address change from '%1' to '%2'", $old_email, $new_email); |
$timestamp = time(); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:activate_new_ra_email$'.$old_email.'$'.$new_email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('5ef24124-f4fb-11ed-b67e-3c4a92df8582:'.$old_email.'/'.$new_email.'/'.$timestamp)); |
$activate_url = OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL) . '?goto='.urlencode('oidplus:activate_new_ra_email$'.$old_email.'$'.$new_email.'$'.$timestamp.'$'.OIDplus::authUtils()->makeAuthKey('activate_new_ra_email;'.$old_email.';'.$new_email.';'.$timestamp)); |
$message = file_get_contents(__DIR__ . '/change_request_email.tpl'); |
$message = str_replace('{{SYSTEM_URL}}', OIDplus::webpath(null,OIDplus::PATH_ABSOLUTE_CANONICAL), $message); |
135,7 → 135,7 |
throw new OIDplusException(_L('E-Mail-Address cannot be changed because this user does not have a password')); |
} |
if (!OIDplus::authUtils()->validateAuthKey('5ef24124-f4fb-11ed-b67e-3c4a92df8582:'.$old_email.'/'.$new_email.'/'.$timestamp, $auth)) { |
if (!OIDplus::authUtils()->validateAuthKey('activate_new_ra_email;'.$old_email.';'.$new_email.';'.$timestamp, $auth)) { |
throw new OIDplusException(_L('Invalid auth key')); |
} |