diff options
| author | Andrew Dolgov <fox@fakecake.org> | 2025-04-07 20:23:19 +0300 |
|---|---|---|
| committer | Andrew Dolgov <fox@fakecake.org> | 2025-04-07 20:28:35 +0300 |
| commit | 58677fc791604bd891fb1ef4f4cc5e040ce8e39f (patch) | |
| tree | af864af258bbda958b26ade6a444487f3bf7a1cf | |
| parent | 026d68fc2d0f24e4f2d46c5743a22f42053caa67 (diff) | |
rename SODIUM_ENCRYPTION_KEY to SESSION_ENCRYPTION_KEY and move related stuff to Sessions class
| -rw-r--r-- | classes/Config.php | 53 | ||||
| -rw-r--r-- | classes/Sessions.php | 54 |
2 files changed, 52 insertions, 55 deletions
diff --git a/classes/Config.php b/classes/Config.php index 11ce5ccfa..c9acad93e 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -8,8 +8,6 @@ class Config { const SCHEMA_VERSION = 147; - const SODIUM_ALGO = 'xchacha20poly1305_ietf'; - /** override default values, defined below in _DEFAULTS[], prefixing with _ENVVAR_PREFIX: * * DB_TYPE becomes: @@ -194,11 +192,8 @@ class Config { /** disables login form controls except HOOK_LOGINFORM_ADDITIONAL_BUTTONS (for SSO providers), also prevents logging in through auth_internal */ const DISABLE_LOGIN_FORM = "DISABLE_LOGIN_FORM"; - /** optional symmetric encryption key for Sodium library (XChaCha20-Poly1305) - generate using bin2hex(sodium_crypto_aead_xchacha20poly1305_ietf_keygen()) - * - * if set, used to transparently encrypt stored session data in the database - */ - const SODIUM_ENCRYPTION_KEY = "SODIUM_ENCRYPTION_KEY"; + /** optional key to transparently encrypt stored session data using Sodium library (XChaCha20-Poly1305) - generate using bin2hex(sodium_crypto_aead_xchacha20poly1305_ietf_keygen()) */ + const SESSION_ENCRYPTION_KEY = "SESSION_ENCRYPTION_KEY"; /** default values for all global configuration options */ private const _DEFAULTS = [ @@ -258,7 +253,7 @@ class Config { Config::T_STRING ], Config::HTTP_429_THROTTLE_INTERVAL => [ 3600, Config::T_INT ], Config::DISABLE_LOGIN_FORM => [ "", Config::T_BOOL ], - Config::SODIUM_ENCRYPTION_KEY => [ "", Config::T_STRING ] + Config::SESSION_ENCRYPTION_KEY => [ "", Config::T_STRING ] ]; private static ?Config $instance = null; @@ -307,48 +302,6 @@ class Config { return self::get_instance()->_get_version($as_string); } - /** encrypts provided ciphertext using Sodium symmetric encryption key if available via Config::SODIUM_ENCRYPTION_KEY - * - * @return array<string,mixed>|false encrypted data object containing algo, nonce, and encrypted data or false if encryption failed - * - */ - static function encrypt_string(string $ciphertext) : array|false { - $key = Config::get(Config::SODIUM_ENCRYPTION_KEY); - $nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES); - - $payload = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($ciphertext, '', $nonce, hex2bin($key)); - - if ($payload) { - $encrypted_data = [ - 'algo' => self::SODIUM_ALGO, - 'nonce' => $nonce, - 'payload' => $payload, - ]; - - return $encrypted_data; - } - - throw new Exception("Config::encrypt_string() failed to encrypt ciphertext"); - } - - /** decrypts payload of encrypted object if Config::SODIUM_ENCRYPTION_KEY is available and object is in correct format - * - * @param array<string,mixed> $encrypted_data - * - * @return string|false decrypted string payload or false if decryption failed - */ - static function decrypt_string(array $encrypted_data) : string|false { - $key = Config::get(Config::SODIUM_ENCRYPTION_KEY); - - if ($encrypted_data['algo'] === self::SODIUM_ALGO) { - $payload = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($encrypted_data['payload'], '', $encrypted_data['nonce'], hex2bin($key)); - - return $payload; - } - - throw new Exception('Config::decrypt_string() failed to decrypt passed encrypted data'); - } - // returns version showing (if possible) full timestamp of commit id static function get_version_html() : string { $version = self::get_version(false); diff --git a/classes/Sessions.php b/classes/Sessions.php index e8cba1765..d8f14eed0 100644 --- a/classes/Sessions.php +++ b/classes/Sessions.php @@ -8,6 +8,8 @@ class Sessions implements \SessionHandlerInterface { private int $session_expire; private string $session_name; + private const SODIUM_ALGO = 'xchacha20poly1305_ietf'; + public function __construct() { $this->session_expire = min(2147483647 - time() - 1, Config::get(Config::SESSION_COOKIE_LIFETIME)); $this->session_name = Config::get(Config::SESSION_NAME); @@ -53,6 +55,48 @@ class Sessions implements \SessionHandlerInterface { return true; } + /** encrypts provided ciphertext using Sodium symmetric encryption key if available via Config::SESSION_ENCRYPTION_KEY + * + * @return array<string,mixed> encrypted data object containing algo, nonce, and encrypted data + * + */ + private function encrypt_string(string $ciphertext) : array { + $key = Config::get(Config::SESSION_ENCRYPTION_KEY); + $nonce = \random_bytes(\SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES); + + $payload = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($ciphertext, '', $nonce, hex2bin($key)); + + if ($payload) { + $encrypted_data = [ + 'algo' => self::SODIUM_ALGO, + 'nonce' => $nonce, + 'payload' => $payload, + ]; + + return $encrypted_data; + } + + throw new Exception("Config::encrypt_string() failed to encrypt ciphertext"); + } + + /** decrypts payload of encrypted object if Config::SESSION_ENCRYPTION_KEY is available and object is in correct format + * + * @param array<string,mixed> $encrypted_data + * + * @return string decrypted string payload + */ + private function decrypt_string(array $encrypted_data) : string { + $key = Config::get(Config::SESSION_ENCRYPTION_KEY); + + if ($encrypted_data['algo'] === self::SODIUM_ALGO) { + $payload = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($encrypted_data['payload'], '', $encrypted_data['nonce'], hex2bin($key)); + + return $payload; + } + + throw new Exception('Config::decrypt_string() failed to decrypt passed encrypted data'); + } + public function read(string $id): false|string { $sth = Db::pdo()->prepare('SELECT data FROM ttrss_sessions WHERE id=?'); $sth->execute([$id]); @@ -60,11 +104,11 @@ class Sessions implements \SessionHandlerInterface { if ($row = $sth->fetch()) { $data = base64_decode($row['data']); - if (Config::get(Config::SODIUM_ENCRYPTION_KEY)) { - $unserialized_data = unserialize($data); + if (Config::get(Config::SESSION_ENCRYPTION_KEY)) { + $unserialized_data = @unserialize($data); // avoid leaking plaintext session via error message if ($unserialized_data !== false) - return Config::decrypt_string($unserialized_data); + return $this->decrypt_string($unserialized_data); } // if Sodium key is missing or session data is not in serialized format, return as-is @@ -80,8 +124,8 @@ class Sessions implements \SessionHandlerInterface { public function write(string $id, string $data): bool { - if (Config::get(Config::SODIUM_ENCRYPTION_KEY)) - $data = serialize(Config::encrypt_string($data)); + if (Config::get(Config::SESSION_ENCRYPTION_KEY)) + $data = serialize($this->encrypt_string($data)); $data = base64_encode($data); |