Changeset 3421231
- Timestamp:
- 12/16/2025 04:07:13 PM (2 months ago)
- Location:
- webp-express
- Files:
-
- 10 added
- 17 edited
- 1 copied
-
tags/0.25.10 (copied) (copied from webp-express/trunk)
-
tags/0.25.10/README.txt (modified) (5 diffs)
-
tags/0.25.10/lib/classes/AdminInit.php (modified) (2 diffs)
-
tags/0.25.10/lib/classes/Config.php (modified) (1 diff)
-
tags/0.25.10/lib/classes/ConfigMigrationInit.php (added)
-
tags/0.25.10/lib/classes/ConvertHelperIndependent.php (modified) (1 diff)
-
tags/0.25.10/lib/classes/HTAccessRules.php (modified) (9 diffs)
-
tags/0.25.10/lib/classes/Paths.php (modified) (3 diffs)
-
tags/0.25.10/lib/classes/WodConfigLoader.php (modified) (2 diffs)
-
tags/0.25.10/lib/dismissable-global-messages/0.25.10 (added)
-
tags/0.25.10/lib/dismissable-global-messages/0.25.10/failed-renaming-config-file.php (added)
-
tags/0.25.10/lib/dismissable-global-messages/0.25.10/renamed-config-file.php (added)
-
tags/0.25.10/lib/migrate/migrate15.php (added)
-
tags/0.25.10/webp-express.php (modified) (2 diffs)
-
trunk/README.txt (modified) (5 diffs)
-
trunk/docs/publishing.md (modified) (1 diff)
-
trunk/lib/classes/AdminInit.php (modified) (2 diffs)
-
trunk/lib/classes/Config.php (modified) (1 diff)
-
trunk/lib/classes/ConfigMigrationInit.php (added)
-
trunk/lib/classes/ConvertHelperIndependent.php (modified) (1 diff)
-
trunk/lib/classes/HTAccessRules.php (modified) (9 diffs)
-
trunk/lib/classes/Paths.php (modified) (3 diffs)
-
trunk/lib/classes/WodConfigLoader.php (modified) (2 diffs)
-
trunk/lib/dismissable-global-messages/0.25.10 (added)
-
trunk/lib/dismissable-global-messages/0.25.10/failed-renaming-config-file.php (added)
-
trunk/lib/dismissable-global-messages/0.25.10/renamed-config-file.php (added)
-
trunk/lib/migrate/migrate15.php (added)
-
trunk/webp-express.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
webp-express/tags/0.25.10/README.txt
r3066149 r3421231 4 4 Tags: webp, images, performance 5 5 Requires at least: 4.0 6 Tested up to: 6. 57 Stable tag: 0.25. 96 Tested up to: 6.9 7 Stable tag: 0.25.10 8 8 Requires PHP: 5.6 9 9 License: GPLv3 … … 174 174 **Persons who recently contributed with [ko-fi](https://ko-fi.com/rosell) - Thanks!** 175 175 176 * 3 Nov: Tobi177 * 5 Nov: Anon178 * 18 Nov: Oleksii179 * 20 Feb: Assen Kovatchev180 * 22 Feb: Peter181 * 29 Feb: Luis Méndez Alejo182 * 5 Mar: tomottoe183 * 9 Ma r: La Braud176 * 9 Aug: Tanzi 177 * 3 Jul: Jen 178 * 26 Jun: Per 179 * 16 May: Erick Danzer 180 * 8 May: Mike 181 * 31 May: parallactic 182 * 14 May: Gitte Rebsdorf 183 * 9 May: La Braud 184 184 185 185 **Persons who contributed with extra generously amounts of coffee / lifetime backing (>30$) - thanks!:** … … 196 196 * Brian Laursen ($50) 197 197 * Dimitris Vayenas ($50) 198 199 **Persons currently backing the project via GitHub Sponsors or patreon - Thanks!**200 201 * [Mathieu Gollain-Dupont](https://www.linkedin.com/in/mathieu-gollain-dupont-9938a4a/)202 198 203 199 == Frequently Asked Questions == … … 819 815 == Changelog == 820 816 817 = 0.25.10 = 818 (released 15 December 2025) 819 * Security fix: Config file was exposed on systems running on NGINX. Herr Patrick Müller from Switzerland for creating a patch as well as Rune Philosof from Denmark for improving it. Some credit also goes to myself for perfecting the patch. Sorry for slacking on the maintenance. There are good reasons for this, but I can and will do better in the future 820 821 821 = 0.25.9 = 822 822 (released 7 April 2024) … … 868 868 == Upgrade Notice == 869 869 870 = 0.25.10 = 871 * Security fix. The config file could be exposed on NGINX 872 870 873 = 0.25.9 = 871 874 * Fixed ewww conversion method after ewww API change -
webp-express/tags/0.25.10/lib/classes/AdminInit.php
r2629766 r3421231 26 26 { 27 27 // When an update requires a migration, the number should be increased 28 define('WEBPEXPRESS_MIGRATION_VERSION', '1 4');28 define('WEBPEXPRESS_MIGRATION_VERSION', '15'); 29 29 30 30 if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) { … … 34 34 35 35 // uncomment next line to test-run a migration 36 //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate1 4.php';36 //include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate15.php'; 37 37 } 38 38 -
webp-express/tags/0.25.10/lib/classes/Config.php
r2629766 r3421231 9 9 10 10 /** 11 * Migrate old predictable config files to randomized names (CVE-2025-11379 fix) 12 * This should be called during plugin upgrade or early on admin load 13 */ 14 public static function migrateConfigFiles() 15 { 16 $oldConfigFile = Paths::getOldConfigFileName(); 17 $newConfigFile = Paths::getConfigFileName(); 18 $oldWodFile = Paths::getOldWodOptionsFileName(); 19 $newWodFile = Paths::getWodOptionsFileName(); 20 21 $migrated = false; 22 23 // Migrate config.json if it exists and new one doesn't 24 if (file_exists($oldConfigFile) && !file_exists($newConfigFile)) { 25 if (@rename($oldConfigFile, $newConfigFile)) { 26 $migrated = true; 27 } elseif (@copy($oldConfigFile, $newConfigFile)) { 28 @unlink($oldConfigFile); 29 $migrated = true; 30 } 31 } 32 33 // Migrate wod-options.json if it exists and new one doesn't 34 if (file_exists($oldWodFile) && !file_exists($newWodFile)) { 35 if (@rename($oldWodFile, $newWodFile)) { 36 $migrated = true; 37 } elseif (@copy($oldWodFile, $newWodFile)) { 38 @unlink($oldWodFile); 39 $migrated = true; 40 } 41 } 42 43 // Clean up any remaining old files (in case new files already existed) 44 if (file_exists($oldConfigFile) && file_exists($newConfigFile)) { 45 @unlink($oldConfigFile); 46 } 47 if (file_exists($oldWodFile) && file_exists($newWodFile)) { 48 @unlink($oldWodFile); 49 } 50 51 return $migrated; 52 } 53 54 /** 55 * Check and perform config file migration if needed (CVE-2025-11379 fix) 56 * This is called early on admin load to ensure migration happens even if options page is never visited 57 * 58 * @return boolean true if its either already migrated or it was migratede successfully or there is no need for migration (in case one starts from newer WebP Express version). false if migration fails 59 */ 60 public static function checkAndMigrateConfigIfNeeded() 61 { 62 // Only run once per request to avoid performance impact 63 static $checked = false; 64 if ($checked) { 65 return true; 66 } 67 $checked = true; 68 69 // Check if migration flag is set to avoid checking filesystem on every request 70 if (Option::getOption('webp-express-config-migrated-cve-2025-11379', false)) { 71 return true; 72 } 73 74 // Check if old files exist 75 $oldConfigFile = Paths::getOldConfigFileName(); 76 $oldWodFile = Paths::getOldWodOptionsFileName(); 77 78 if (file_exists($oldConfigFile) || file_exists($oldWodFile)) { 79 if (self::migrateConfigFiles()) { 80 Option::updateOption('webp-express-config-migrated-cve-2025-11379', true, true); 81 return true; 82 } 83 return false; 84 } else { 85 // No old files found, mark as migrated to avoid future checks 86 Option::updateOption('webp-express-config-migrated-cve-2025-11379', true, true); 87 return true; 88 } 89 } 90 91 /** 11 92 * @return object|false Returns config object if config file exists and can be read. Otherwise it returns false 12 93 */ 13 94 public static function loadConfig() 14 95 { 96 // Attempt migration before loading config 97 self::checkAndMigrateConfigIfNeeded(); 98 15 99 return FileHelper::loadJSONOptions(Paths::getConfigFileName()); 16 100 } -
webp-express/tags/0.25.10/lib/classes/ConvertHelperIndependent.php
r3066146 r3421231 572 572 573 573 // TODO: Put version number somewhere else. Ie \WebPExpress\VersionNumber::version 574 $text = 'WebP Express 0.25. 9. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;574 $text = 'WebP Express 0.25.10. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text; 575 575 576 576 $logFile = self::getLogFilename($source, $logDir); -
webp-express/tags/0.25.10/lib/classes/HTAccessRules.php
r2629766 r3421231 435 435 436 436 437 $configHash = Paths::getConfigHash(); 438 437 439 if (self::$useDocRootForStructuringCacheDir) { 438 440 /* … … 447 449 if (!self::$passThroughEnvVarDefinitelyUnavailable) { 448 450 $flags[] = 'E=DESTINATIONREL:' . self::$htaccessDirRelToDocRoot . '/$0'; 449 } 450 if (!self::$passThroughEnvVarDefinitelyUnavailable) { 451 $flags[] = 'E=WPCONTENT:' . Paths::getContentDirRel(); 451 $flags[] = 'E=WPCONTENT:' . Paths::getContentDirRel() . '/$0'; 452 $flags[] = 'E=HASH:' . $configHash; 452 453 } 453 454 $flags[] = 'NC'; … … 460 461 if (!self::$passThroughEnvVarDefinitelyAvailable) { 461 462 $params[] = "wp-content=" . Paths::getContentDirRel(); 463 $params[] = "hash=" . $configHash; 462 464 } 463 465 … … 486 488 $flags[] = 'E=WE_DESTINATION_REL_HTACCESS:$0'; 487 489 $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw either be "uploads" or "cache" 490 $flags[] = 'E=HASH:' . $configHash; 488 491 } 489 492 $flags[] = 'NC'; // case-insensitive match (so file extension can be jpg, JPG or even jPg) … … 495 498 $params[] = 'xdestination-rel-htaccess=x$0'; 496 499 $params[] = 'htaccess-id=' . self::$htaccessDir; 500 $params[] = "hash=" . $configHash; 497 501 } 498 502 … … 633 637 } 634 638 639 $configHash = Paths::getConfigHash(); 635 640 if (self::$useDocRootForStructuringCacheDir) { 636 641 /* … … 653 658 if (!self::$passThroughEnvVarDefinitelyAvailable) { 654 659 $params[] = "wp-content=" . Paths::getContentDirRel(); 660 $params[] = "hash=" . $configHash; 655 661 } 656 662 if (!self::$passThroughEnvVarDefinitelyUnavailable) { 657 663 $flags[] = 'E=WPCONTENT:' . Paths::getContentDirRel(); 664 $flags[] = 'E=HASH:' . $configHash; 658 665 } 659 666 … … 684 691 $flags[] = 'E=WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR:' . Paths::getContentDirRelToWebPExpressPluginDir(); 685 692 $flags[] = 'E=WE_SOURCE_REL_HTACCESS:$0'; 686 $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw be one of the image roots. It will not be "cache" 693 $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; 694 $flags[] = 'E=HASH:' . $configHash; // this will btw be one of the image roots. It will not be "cache" 687 695 } 688 696 $flags[] = 'NC'; // case-insensitive match (so file extension can be jpg, JPG or even jPg) … … 694 702 $params[] = 'xsource-rel-htaccess=x$0'; 695 703 $params[] = 'htaccess-id=' . self::$htaccessDir; 704 $params[] = "hash=" . $configHash; 696 705 } 697 706 -
webp-express/tags/0.25.10/lib/classes/Paths.php
r2981234 r3421231 428 428 { 429 429 return PathHelper::getRelPathFromDocRootToDirNoDirectoryTraversalAllowed(self::getConfigDirAbs()); 430 } 431 432 /** 433 * Get or create a random hash for config filename obfuscation (CVE-2025-11379 fix) 434 * This prevents predictable config file access on Nginx servers 435 */ 436 public static function getConfigHash() 437 { 438 $hash = \WebPExpress\Option::getOption('webp-express-config-hash', false); 439 if (!$hash) { 440 // Generate a cryptographically secure random hash 441 if (function_exists('random_bytes')) { 442 $hash = bin2hex(random_bytes(16)); 443 } else { 444 // Fallback for older PHP versions 445 $hash = md5(uniqid(mt_rand(), true) . microtime(true)); 446 } 447 \WebPExpress\Option::updateOption('webp-express-config-hash', $hash, true); 448 } 449 return $hash; 430 450 } 431 451 … … 450 470 ); 451 471 @chmod($configDir . '/.htaccess', 0664); 472 473 // Additional protection for Nginx: PHP-based access control (CVE-2025-11379 fix) 474 @file_put_contents(rtrim($configDir . '/') . '/index.php', <<<'PHP' 475 <?php 476 // Prevent direct access to config files on Nginx (CVE-2025-11379 fix) 477 if (!defined('ABSPATH')) { 478 http_response_code(403); 479 die('Direct access forbidden'); 480 } 481 PHP 482 ); 483 @chmod($configDir . '/index.php', 0644); 452 484 } 453 485 return is_dir($configDir); … … 456 488 public static function getConfigFileName() 457 489 { 490 // Use randomized filename to prevent predictable access on Nginx (CVE-2025-11379 fix) 491 $hash = self::getConfigHash(); 492 return self::getConfigDirAbs() . '/config.' . $hash . '.json'; 493 } 494 495 public static function getWodOptionsFileName() 496 { 497 // Use randomized filename to prevent predictable access on Nginx (CVE-2025-11379 fix) 498 $hash = self::getConfigHash(); 499 return self::getConfigDirAbs() . '/wod-options.' . $hash . '.json'; 500 } 501 502 /** 503 * Get old predictable config filename for migration purposes 504 */ 505 public static function getOldConfigFileName() 506 { 458 507 return self::getConfigDirAbs() . '/config.json'; 459 508 } 460 509 461 public static function getWodOptionsFileName() 510 /** 511 * Get old predictable wod-options filename for migration purposes 512 */ 513 public static function getOldWodOptionsFileName() 462 514 { 463 515 return self::getConfigDirAbs() . '/wod-options.json'; -
webp-express/tags/0.25.10/lib/classes/WodConfigLoader.php
r2626928 r3421231 183 183 protected static function loadConfig() { 184 184 185 $hash = self::getEnvPassedInRewriteRule('HASH'); 186 if ($hash === false) { 187 // Passed in QS? 188 if (isset($_GET['hash'])) { 189 $hash = $_GET['hash']; 190 } else { 191 // In case above fails, fall back to standard location 192 $hash = ''; 193 } 194 } 195 $filename = ''; 196 197 if ($hash == '') { 198 $filename = 'wod-options.json'; 199 } else { 200 $filename = 'wod-options.' . $hash . '.json'; 201 } 202 185 203 $usingDocRoot = !( 186 204 isset($_GET['xwp-content-rel-to-we-plugin-dir']) || … … 218 236 self::$checking = 'config file'; 219 237 220 $configFilename = self::$webExpressContentDirAbs . '/config/ wod-options.json';238 $configFilename = self::$webExpressContentDirAbs . '/config/' . $filename; 221 239 if (!file_exists($configFilename)) { 222 throw new \Exception('Configuration file was not found (wod-options. json)');240 throw new \Exception('Configuration file was not found (wod-options.some-hash.json)'); 223 241 } 224 242 -
webp-express/tags/0.25.10/webp-express.php
r3066146 r3421231 4 4 * Plugin URI: https://github.com/rosell-dk/webp-express 5 5 * Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc). 6 * Version: 0.25. 96 * Version: 0.25.10 7 7 * Author: Bjørn Rosell 8 8 * Author URI: https://www.bitwise-it.dk … … 31 31 32 32 if (is_admin()) { 33 // Initialize admin hooks 33 34 \WebPExpress\AdminInit::init(); 34 35 } -
webp-express/trunk/README.txt
r3066149 r3421231 4 4 Tags: webp, images, performance 5 5 Requires at least: 4.0 6 Tested up to: 6. 57 Stable tag: 0.25. 96 Tested up to: 6.9 7 Stable tag: 0.25.10 8 8 Requires PHP: 5.6 9 9 License: GPLv3 … … 174 174 **Persons who recently contributed with [ko-fi](https://ko-fi.com/rosell) - Thanks!** 175 175 176 * 3 Nov: Tobi177 * 5 Nov: Anon178 * 18 Nov: Oleksii179 * 20 Feb: Assen Kovatchev180 * 22 Feb: Peter181 * 29 Feb: Luis Méndez Alejo182 * 5 Mar: tomottoe183 * 9 Ma r: La Braud176 * 9 Aug: Tanzi 177 * 3 Jul: Jen 178 * 26 Jun: Per 179 * 16 May: Erick Danzer 180 * 8 May: Mike 181 * 31 May: parallactic 182 * 14 May: Gitte Rebsdorf 183 * 9 May: La Braud 184 184 185 185 **Persons who contributed with extra generously amounts of coffee / lifetime backing (>30$) - thanks!:** … … 196 196 * Brian Laursen ($50) 197 197 * Dimitris Vayenas ($50) 198 199 **Persons currently backing the project via GitHub Sponsors or patreon - Thanks!**200 201 * [Mathieu Gollain-Dupont](https://www.linkedin.com/in/mathieu-gollain-dupont-9938a4a/)202 198 203 199 == Frequently Asked Questions == … … 819 815 == Changelog == 820 816 817 = 0.25.10 = 818 (released 15 December 2025) 819 * Security fix: Config file was exposed on systems running on NGINX. Herr Patrick Müller from Switzerland for creating a patch as well as Rune Philosof from Denmark for improving it. Some credit also goes to myself for perfecting the patch. Sorry for slacking on the maintenance. There are good reasons for this, but I can and will do better in the future 820 821 821 = 0.25.9 = 822 822 (released 7 April 2024) … … 868 868 == Upgrade Notice == 869 869 870 = 0.25.10 = 871 * Security fix. The config file could be exposed on NGINX 872 870 873 = 0.25.9 = 871 874 * Fixed ewww conversion method after ewww API change -
webp-express/trunk/docs/publishing.md
r3066149 r3421231 112 112 ``` 113 113 cd svn 114 svn cp trunk tags/0.25. 9(this will copy trunk into a new tag)114 svn cp trunk tags/0.25.10 (this will copy trunk into a new tag) 115 115 ``` 116 116 117 117 And commit! 118 118 ``` 119 svn ci -m '0.25. 9'119 svn ci -m '0.25.10. Fixed ' 120 120 ``` 121 121 -
webp-express/trunk/lib/classes/AdminInit.php
r2629766 r3421231 26 26 { 27 27 // When an update requires a migration, the number should be increased 28 define('WEBPEXPRESS_MIGRATION_VERSION', '1 4');28 define('WEBPEXPRESS_MIGRATION_VERSION', '15'); 29 29 30 30 if (WEBPEXPRESS_MIGRATION_VERSION != Option::getOption('webp-express-migration-version', 0)) { … … 34 34 35 35 // uncomment next line to test-run a migration 36 // include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate14.php';36 // include WEBPEXPRESS_PLUGIN_DIR . '/lib/migrate/migrate15.php'; 37 37 } 38 38 -
webp-express/trunk/lib/classes/Config.php
r2629766 r3421231 9 9 10 10 /** 11 * Migrate old predictable config files to randomized names (CVE-2025-11379 fix) 12 * This should be called during plugin upgrade or early on admin load 13 */ 14 public static function migrateConfigFiles() 15 { 16 $oldConfigFile = Paths::getOldConfigFileName(); 17 $newConfigFile = Paths::getConfigFileName(); 18 $oldWodFile = Paths::getOldWodOptionsFileName(); 19 $newWodFile = Paths::getWodOptionsFileName(); 20 21 $migrated = false; 22 23 // Migrate config.json if it exists and new one doesn't 24 if (file_exists($oldConfigFile) && !file_exists($newConfigFile)) { 25 if (@rename($oldConfigFile, $newConfigFile)) { 26 $migrated = true; 27 } elseif (@copy($oldConfigFile, $newConfigFile)) { 28 @unlink($oldConfigFile); 29 $migrated = true; 30 } 31 } 32 33 // Migrate wod-options.json if it exists and new one doesn't 34 if (file_exists($oldWodFile) && !file_exists($newWodFile)) { 35 if (@rename($oldWodFile, $newWodFile)) { 36 $migrated = true; 37 } elseif (@copy($oldWodFile, $newWodFile)) { 38 @unlink($oldWodFile); 39 $migrated = true; 40 } 41 } 42 43 // Clean up any remaining old files (in case new files already existed) 44 if (file_exists($oldConfigFile) && file_exists($newConfigFile)) { 45 @unlink($oldConfigFile); 46 } 47 if (file_exists($oldWodFile) && file_exists($newWodFile)) { 48 @unlink($oldWodFile); 49 } 50 51 return $migrated; 52 } 53 54 /** 55 * Check and perform config file migration if needed (CVE-2025-11379 fix) 56 * This is called early on admin load to ensure migration happens even if options page is never visited 57 * 58 * @return boolean true if its either already migrated or it was migratede successfully or there is no need for migration (in case one starts from newer WebP Express version). false if migration fails 59 */ 60 public static function checkAndMigrateConfigIfNeeded() 61 { 62 // Only run once per request to avoid performance impact 63 static $checked = false; 64 if ($checked) { 65 return true; 66 } 67 $checked = true; 68 69 // Check if migration flag is set to avoid checking filesystem on every request 70 if (Option::getOption('webp-express-config-migrated-cve-2025-11379', false)) { 71 return true; 72 } 73 74 // Check if old files exist 75 $oldConfigFile = Paths::getOldConfigFileName(); 76 $oldWodFile = Paths::getOldWodOptionsFileName(); 77 78 if (file_exists($oldConfigFile) || file_exists($oldWodFile)) { 79 if (self::migrateConfigFiles()) { 80 Option::updateOption('webp-express-config-migrated-cve-2025-11379', true, true); 81 return true; 82 } 83 return false; 84 } else { 85 // No old files found, mark as migrated to avoid future checks 86 Option::updateOption('webp-express-config-migrated-cve-2025-11379', true, true); 87 return true; 88 } 89 } 90 91 /** 11 92 * @return object|false Returns config object if config file exists and can be read. Otherwise it returns false 12 93 */ 13 94 public static function loadConfig() 14 95 { 96 // Attempt migration before loading config 97 self::checkAndMigrateConfigIfNeeded(); 98 15 99 return FileHelper::loadJSONOptions(Paths::getConfigFileName()); 16 100 } -
webp-express/trunk/lib/classes/ConvertHelperIndependent.php
r3066146 r3421231 572 572 573 573 // TODO: Put version number somewhere else. Ie \WebPExpress\VersionNumber::version 574 $text = 'WebP Express 0.25. 9. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text;574 $text = 'WebP Express 0.25.10. ' . $msgTop . ', ' . date("Y-m-d H:i:s") . "\n\r\n\r" . $text; 575 575 576 576 $logFile = self::getLogFilename($source, $logDir); -
webp-express/trunk/lib/classes/HTAccessRules.php
r2629766 r3421231 435 435 436 436 437 $configHash = Paths::getConfigHash(); 438 437 439 if (self::$useDocRootForStructuringCacheDir) { 438 440 /* … … 447 449 if (!self::$passThroughEnvVarDefinitelyUnavailable) { 448 450 $flags[] = 'E=DESTINATIONREL:' . self::$htaccessDirRelToDocRoot . '/$0'; 449 } 450 if (!self::$passThroughEnvVarDefinitelyUnavailable) { 451 $flags[] = 'E=WPCONTENT:' . Paths::getContentDirRel(); 451 $flags[] = 'E=WPCONTENT:' . Paths::getContentDirRel() . '/$0'; 452 $flags[] = 'E=HASH:' . $configHash; 452 453 } 453 454 $flags[] = 'NC'; … … 460 461 if (!self::$passThroughEnvVarDefinitelyAvailable) { 461 462 $params[] = "wp-content=" . Paths::getContentDirRel(); 463 $params[] = "hash=" . $configHash; 462 464 } 463 465 … … 486 488 $flags[] = 'E=WE_DESTINATION_REL_HTACCESS:$0'; 487 489 $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw either be "uploads" or "cache" 490 $flags[] = 'E=HASH:' . $configHash; 488 491 } 489 492 $flags[] = 'NC'; // case-insensitive match (so file extension can be jpg, JPG or even jPg) … … 495 498 $params[] = 'xdestination-rel-htaccess=x$0'; 496 499 $params[] = 'htaccess-id=' . self::$htaccessDir; 500 $params[] = "hash=" . $configHash; 497 501 } 498 502 … … 633 637 } 634 638 639 $configHash = Paths::getConfigHash(); 635 640 if (self::$useDocRootForStructuringCacheDir) { 636 641 /* … … 653 658 if (!self::$passThroughEnvVarDefinitelyAvailable) { 654 659 $params[] = "wp-content=" . Paths::getContentDirRel(); 660 $params[] = "hash=" . $configHash; 655 661 } 656 662 if (!self::$passThroughEnvVarDefinitelyUnavailable) { 657 663 $flags[] = 'E=WPCONTENT:' . Paths::getContentDirRel(); 664 $flags[] = 'E=HASH:' . $configHash; 658 665 } 659 666 … … 684 691 $flags[] = 'E=WE_WP_CONTENT_REL_TO_WE_PLUGIN_DIR:' . Paths::getContentDirRelToWebPExpressPluginDir(); 685 692 $flags[] = 'E=WE_SOURCE_REL_HTACCESS:$0'; 686 $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; // this will btw be one of the image roots. It will not be "cache" 693 $flags[] = 'E=WE_HTACCESS_ID:' . self::$htaccessDir; 694 $flags[] = 'E=HASH:' . $configHash; // this will btw be one of the image roots. It will not be "cache" 687 695 } 688 696 $flags[] = 'NC'; // case-insensitive match (so file extension can be jpg, JPG or even jPg) … … 694 702 $params[] = 'xsource-rel-htaccess=x$0'; 695 703 $params[] = 'htaccess-id=' . self::$htaccessDir; 704 $params[] = "hash=" . $configHash; 696 705 } 697 706 -
webp-express/trunk/lib/classes/Paths.php
r2981234 r3421231 430 430 } 431 431 432 public static function createConfigDirIfMissing() 432 /** 433 * Get or create a random hash for config filename obfuscation (CVE-2025-11379 fix) 434 * This prevents predictable config file access on Nginx servers 435 */ 436 public static function getConfigHash() 437 { 438 $hash = \WebPExpress\Option::getOption('webp-express-config-hash', false); 439 if (!$hash) { 440 // Generate a cryptographically secure random hash 441 if (function_exists('random_bytes')) { 442 $hash = bin2hex(random_bytes(16)); 443 } else { 444 // Fallback for older PHP versions 445 $hash = md5(uniqid(mt_rand(), true) . microtime(true)); 446 } 447 \WebPExpress\Option::updateOption('webp-express-config-hash', $hash, true); 448 } 449 return $hash; 450 } 451 452 // Only call if certain that config dir exists 453 private static function doCreateIndexPHPInConfigDirIfMissing() 433 454 { 434 455 $configDir = self::getConfigDirAbs(); 435 // Using code from Wordfence bootstrap.php... 436 // Why not simply use wp_mkdir_p ? - it sets the permissions to same as parent. Isn't that better? 437 // or perhaps not... - Because we need write permissions in the config dir. 438 if (!is_dir($configDir)) { 439 @mkdir($configDir, 0775); 440 @chmod($configDir, 0775); 441 @file_put_contents(rtrim($configDir . '/') . '/.htaccess', <<<APACHE 456 $indexPHPfilename = rtrim($configDir, '/') . '/index.php'; 457 458 if (!@file_exists($indexPHPfilename)) { 459 // Additional protection for Nginx: PHP-based access control (CVE-2025-11379 fix) 460 @file_put_contents($indexPHPfilename, <<<'PHP' 461 <?php 462 // Prevent direct access to config files on Nginx (CVE-2025-11379 fix) 463 if (!defined('ABSPATH')) { 464 http_response_code(403); 465 die('Direct access forbidden'); 466 } 467 PHP 468 ); 469 @chmod($indexPHPfilename, 0644); 470 } 471 } 472 473 // Only call if certain that config dir exists 474 private static function doCreateHTAccessInConfigDirIfMissing() 475 { 476 $configDir = self::getConfigDirAbs(); 477 $filename = rtrim($configDir, '/') . '/.htaccess'; 478 479 if (!@file_exists($filename)) { 480 // Additional protection for Nginx: PHP-based access control (CVE-2025-11379 fix) 481 @file_put_contents(rtrim($configDir . '/') . '/.htaccess', <<<APACHE 442 482 <IfModule mod_authz_core.c> 443 483 Require all denied … … 448 488 </IfModule> 449 489 APACHE 450 ); 451 @chmod($configDir . '/.htaccess', 0664); 490 ); 491 @chmod($filename, 0664); 492 } 493 } 494 495 public static function createIndexPHPInConfigDirIfMissing() 496 { 497 $configDir = self::getConfigDirAbs(); 498 499 if (is_dir($configDir)) { 500 self::doCreateIndexPHPInConfigDirIfMissing(); 501 } 502 } 503 504 public static function createConfigDirIfMissing() 505 { 506 $configDir = self::getConfigDirAbs(); 507 // Using code from Wordfence bootstrap.php... 508 // Why not simply use wp_mkdir_p ? - it sets the permissions to same as parent. Isn't that better? 509 // or perhaps not... - Because we need write permissions in the config dir. 510 if (!is_dir($configDir)) { 511 @mkdir($configDir, 0775); 512 @chmod($configDir, 0775); 513 514 self::doCreateIndexPHPInConfigDirIfMissing(); 515 self::doCreateHTAccessInConfigDirIfMissing(); 516 452 517 } 453 518 return is_dir($configDir); … … 456 521 public static function getConfigFileName() 457 522 { 523 // Use randomized filename to prevent predictable access on Nginx (CVE-2025-11379 fix) 524 $hash = self::getConfigHash(); 525 return self::getConfigDirAbs() . '/config.' . $hash . '.json'; 526 } 527 528 public static function getWodOptionsFileName() 529 { 530 // Use randomized filename to prevent predictable access on Nginx (CVE-2025-11379 fix) 531 $hash = self::getConfigHash(); 532 return self::getConfigDirAbs() . '/wod-options.' . $hash . '.json'; 533 } 534 535 /** 536 * Get old predictable config filename for migration purposes 537 */ 538 public static function getOldConfigFileName() 539 { 458 540 return self::getConfigDirAbs() . '/config.json'; 459 541 } 460 542 461 public static function getWodOptionsFileName() 543 /** 544 * Get old predictable wod-options filename for migration purposes 545 */ 546 public static function getOldWodOptionsFileName() 462 547 { 463 548 return self::getConfigDirAbs() . '/wod-options.json'; -
webp-express/trunk/lib/classes/WodConfigLoader.php
r2626928 r3421231 183 183 protected static function loadConfig() { 184 184 185 $hash = self::getEnvPassedInRewriteRule('HASH'); 186 if ($hash === false) { 187 // Passed in QS? 188 if (isset($_GET['hash'])) { 189 $hash = $_GET['hash']; 190 } else { 191 // In case above fails, fall back to standard location 192 $hash = ''; 193 } 194 } 195 $filename = ''; 196 197 if ($hash == '') { 198 $filename = 'wod-options.json'; 199 } else { 200 $filename = 'wod-options.' . $hash . '.json'; 201 } 202 185 203 $usingDocRoot = !( 186 204 isset($_GET['xwp-content-rel-to-we-plugin-dir']) || … … 218 236 self::$checking = 'config file'; 219 237 220 $configFilename = self::$webExpressContentDirAbs . '/config/ wod-options.json';238 $configFilename = self::$webExpressContentDirAbs . '/config/' . $filename; 221 239 if (!file_exists($configFilename)) { 222 throw new \Exception('Configuration file was not found (wod-options. json)');240 throw new \Exception('Configuration file was not found (wod-options.some-hash.json)'); 223 241 } 224 242 -
webp-express/trunk/webp-express.php
r3066146 r3421231 4 4 * Plugin URI: https://github.com/rosell-dk/webp-express 5 5 * Description: Serve autogenerated WebP images instead of jpeg/png to browsers that supports WebP. Works on anything (media library images, galleries, theme images etc). 6 * Version: 0.25. 96 * Version: 0.25.10 7 7 * Author: Bjørn Rosell 8 8 * Author URI: https://www.bitwise-it.dk … … 31 31 32 32 if (is_admin()) { 33 // Initialize admin hooks 33 34 \WebPExpress\AdminInit::init(); 34 35 }
Note: See TracChangeset
for help on using the changeset viewer.