diff options
| author | Andrew Dolgov <fox@fakecake.org> | 2025-04-08 09:36:04 +0300 |
|---|---|---|
| committer | Andrew Dolgov <fox@fakecake.org> | 2025-04-08 09:36:04 +0300 |
| commit | eedc1460e5dadb00a731c1974642a4db7ab30868 (patch) | |
| tree | d00876cda056fa5201365d3e0760317b7090b40f | |
| parent | 25d3ce4ee8f411a19c3a0e69ebb5c575c16243a8 (diff) | |
support transparent encryption for feed passwords, bump schema to drop length limit of ttrss_feeds.auth_pass
| -rw-r--r-- | classes/Config.php | 5 | ||||
| -rw-r--r-- | classes/Feeds.php | 24 | ||||
| -rw-r--r-- | classes/Pref_Feeds.php | 6 | ||||
| -rw-r--r-- | classes/RSSUtils.php | 12 | ||||
| -rw-r--r-- | sql/mysql/migrations/148.sql | 3 | ||||
| -rw-r--r-- | sql/mysql/schema.sql | 2 | ||||
| -rw-r--r-- | sql/pgsql/migrations/148.sql | 7 | ||||
| -rw-r--r-- | sql/pgsql/schema.sql | 2 |
8 files changed, 53 insertions, 8 deletions
diff --git a/classes/Config.php b/classes/Config.php index e906419dc..5098bfe68 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -6,7 +6,7 @@ class Config { const T_STRING = 2; const T_INT = 3; - const SCHEMA_VERSION = 147; + const SCHEMA_VERSION = 148; /** override default values, defined below in _DEFAULTS[], prefixing with _ENVVAR_PREFIX: * @@ -192,7 +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 key to transparently encrypt sensitive data (currently limited to sessions); key is a 32 byte hex string may be generated using update.php --gen-encryption-key */ + /** optional key to transparently encrypt sensitive data (currently limited to sessions and feed passwords), + * key is a 32 byte hex string which may be generated using `update.php --gen-encryption-key` */ const ENCRYPTION_KEY = "ENCRYPTION_KEY"; /** default values for all global configuration options */ diff --git a/classes/Feeds.php b/classes/Feeds.php index 7a81ec6c0..7d818598d 100644 --- a/classes/Feeds.php +++ b/classes/Feeds.php @@ -2376,5 +2376,29 @@ class Feeds extends Handler_Protected { return [$query, $skip_first_id]; } + + /** decrypts encrypted feed password if possible (key is available and data is a base64-encoded serialized object) + * + * @param $auth_pass possibly encrypted feed password + * + * @return string plaintext representation of an encrypted feed password if encrypted or plaintext password otherwise + * */ + static function decrypt_feed_pass(string $auth_pass) : string { + $key = Config::get(Config::ENCRYPTION_KEY); + + if ($auth_pass && $key) { + $auth_pass_serialized = @base64_decode($auth_pass); + + if ($auth_pass_serialized) { + $unserialized_data = @unserialize($auth_pass_serialized); + + if ($unserialized_data !== false) + return Crypt::decrypt_string($unserialized_data); + } + } + + return $auth_pass; + } + } diff --git a/classes/Pref_Feeds.php b/classes/Pref_Feeds.php index 537cc3c86..bc059b99f 100644 --- a/classes/Pref_Feeds.php +++ b/classes/Pref_Feeds.php @@ -560,6 +560,7 @@ class Pref_Feeds extends Handler_Protected { ob_end_clean(); $row["icon"] = Feeds::_get_icon($feed_id); + $row["auth_pass"] = Feeds::decrypt_feed_pass($row["auth_pass"]); $local_update_intervals = $update_intervals; $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[Prefs::get(Prefs::DEFAULT_UPDATE_INTERVAL, $_SESSION['uid'])]); @@ -746,6 +747,11 @@ class Pref_Feeds extends Handler_Protected { $feed_language = clean($_POST["feed_language"] ?? ""); + $key = Config::get(Config::ENCRYPTION_KEY); + + if ($key && $auth_pass) + $auth_pass = base64_encode(serialize(Crypt::encrypt_string($auth_pass))); + if (!$batch) { /* $sth = $this->pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ?"); diff --git a/classes/RSSUtils.php b/classes/RSSUtils.php index ee58416e3..8e3e56ee7 100644 --- a/classes/RSSUtils.php +++ b/classes/RSSUtils.php @@ -331,6 +331,8 @@ class RSSUtils { $pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $feed->owner_uid); //$pluginhost->load_data(); + $feed_auth_pass_plaintext = Feeds::decrypt_feed_pass($feed->auth_pass); + $basic_info = []; $pluginhost->run_hooks_callback(PluginHost::HOOK_FEED_BASIC_INFO, function ($result) use (&$basic_info) { @@ -338,13 +340,13 @@ class RSSUtils { $basic_info = $result; return true; } - }, $basic_info, $feed->feed_url, $feed->owner_uid, $feed_id, $feed->auth_login, $feed->auth_pass); + }, $basic_info, $feed->feed_url, $feed->owner_uid, $feed_id, $feed->auth_login, $feed_auth_pass_plaintext); if (!$basic_info) { $feed_data = UrlHelper::fetch([ 'url' => $feed->feed_url, 'login' => $feed->auth_login, - 'pass' => $feed->auth_pass, + 'pass' => $feed_auth_pass_plaintext, 'timeout' => Config::get(Config::FEED_FETCH_TIMEOUT), ]); @@ -458,12 +460,14 @@ class RSSUtils { $hff_owner_uid = $feed_obj->owner_uid; $hff_feed_url = $feed_obj->feed_url; + $feed_auth_pass_plaintext = Feeds::decrypt_feed_pass($feed_obj->auth_pass); + $pluginhost->chain_hooks_callback(PluginHost::HOOK_FETCH_FEED, function ($result, $plugin) use (&$feed_data, $start_ts) { $feed_data = $result; Debug::log(sprintf("=== %.4f (sec) %s", microtime(true) - $start_ts, get_class($plugin)), Debug::LOG_VERBOSE); }, - $feed_data, $hff_feed_url, $hff_owner_uid, $feed, $last_article_timestamp, $feed_obj->auth_login, $feed_obj->auth_pass); + $feed_data, $hff_feed_url, $hff_owner_uid, $feed, $last_article_timestamp, $feed_obj->auth_login, $feed_auth_pass_plaintext); if ($feed_data) { Debug::log("feed data has been modified by a plugin.", Debug::LOG_VERBOSE); @@ -510,7 +514,7 @@ class RSSUtils { $feed_data = UrlHelper::fetch([ "url" => $feed_obj->feed_url, "login" => $feed_obj->auth_login, - "pass" => $feed_obj->auth_pass, + "pass" => $feed_auth_pass_plaintext, "timeout" => $no_cache ? Config::get(Config::FEED_FETCH_NO_CACHE_TIMEOUT) : Config::get(Config::FEED_FETCH_TIMEOUT), "last_modified" => $force_refetch ? "" : $feed_obj->last_modified ]); diff --git a/sql/mysql/migrations/148.sql b/sql/mysql/migrations/148.sql new file mode 100644 index 000000000..64eca0972 --- /dev/null +++ b/sql/mysql/migrations/148.sql @@ -0,0 +1,3 @@ +alter table ttrss_feeds change auth_pass auth_pass text not null; + +update ttrss_version set schema_version = 148; diff --git a/sql/mysql/schema.sql b/sql/mysql/schema.sql index ffabab669..43be5b479 100644 --- a/sql/mysql/schema.sql +++ b/sql/mysql/schema.sql @@ -121,7 +121,7 @@ create table ttrss_feeds (id integer not null auto_increment primary key, favicon_is_custom boolean default null, site_url varchar(250) not null default '', auth_login varchar(250) not null default '', - auth_pass varchar(250) not null default '', + auth_pass text not null default '', parent_feed integer default null, private bool not null default false, rtl_content bool not null default false, diff --git a/sql/pgsql/migrations/148.sql b/sql/pgsql/migrations/148.sql new file mode 100644 index 000000000..98a04de3e --- /dev/null +++ b/sql/pgsql/migrations/148.sql @@ -0,0 +1,7 @@ +alter table ttrss_feeds rename column auth_pass to auth_pass_old; +alter table ttrss_feeds add column auth_pass text; +update ttrss_feeds set auth_pass = auth_pass_old; +alter table ttrss_feeds alter column auth_pass set not null; +alter table ttrss_feeds drop column auth_pass_old; + +update ttrss_version set schema_version = 148; diff --git a/sql/pgsql/schema.sql b/sql/pgsql/schema.sql index 938ccc905..acfa619c9 100644 --- a/sql/pgsql/schema.sql +++ b/sql/pgsql/schema.sql @@ -92,7 +92,7 @@ create table ttrss_feeds (id serial not null primary key, auth_login varchar(250) not null default '', parent_feed integer default null references ttrss_feeds(id) on delete set null, private boolean not null default false, - auth_pass varchar(250) not null default '', + auth_pass text not null default '', hidden boolean not null default false, include_in_digest boolean not null default true, rtl_content boolean not null default false, |