mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-09-23 03:05:00 -04:00
feat(security): add THROTTLE_KEY env-var fallback for throttle.key (#4707)
checkThrottleEncryption() now consults the THROTTLE_KEY environment variable when throttle.key is empty, mirroring the ENCRYPTION_KEY fallback in Config/Encryption. This lets Docker/Compose deployments supply the throttle HMAC secret without writing a shared value into a read-only .env. An explicit throttle.key always takes precedence. Adds regression tests to the existing security_helperTest suite and documents THROTTLE_KEY in .env.example.
This commit is contained in:
1 parent
edcb4bb655
commit
00b97c3302
3 files changed
+67
-1
No files matched your search
@@ -62,6 +62,10 @@ database.tests.DBPrefix='ospos_'
|
||||
encryption.key=''
|
||||
# Persistent secret for HMAC-hashing login-throttle cache keys. Left blank and
|
||||
# provisioned on startup (php spark env:provision); independent of encryption.key.
|
||||
# For Docker/Compose, pass it via the THROTTLE_KEY env var instead:
|
||||
# docker run -e THROTTLE_KEY="$(openssl rand -hex 32)" opensourcepos
|
||||
# THROTTLE_KEY is read as a fallback when throttle.key is empty, so no
|
||||
# shared secret needs to be committed or baked into the shipped image.
|
||||
throttle.key=''
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
@@ -327,12 +327,16 @@ function checkEncryption(?CI3SecretConverter $converter = null): bool
|
||||
* Returns the persistent HMAC secret used to hash login-throttle cache keys.
|
||||
*
|
||||
* Behaviour:
|
||||
* - Key already present: returned immediately (no I/O).
|
||||
* - Key already present (throttle.key or THROTTLE_KEY): returned immediately (no I/O).
|
||||
* - Key missing and .env IS writable: generates a fresh key and persists it.
|
||||
* - Key missing and .env NOT writable:
|
||||
* throws — the key was presumably already provisioned externally
|
||||
* (e.g. `php spark env:provision` at container startup).
|
||||
*
|
||||
* THROTTLE_KEY is a real environment-variable fallback (mirroring ENCRYPTION_KEY):
|
||||
* it is consulted only when throttle.key is empty, so it can be supplied via
|
||||
* Docker/Compose without writing a shared secret into .env.
|
||||
*
|
||||
* @return string the throttle key
|
||||
* @throws RuntimeException if the key cannot be provisioned
|
||||
*/
|
||||
@@ -340,6 +344,11 @@ function checkThrottleEncryption(): string
|
||||
{
|
||||
$key = (string) env('throttle.key', '');
|
||||
|
||||
if ($key === '') {
|
||||
$envKey = getenv('THROTTLE_KEY');
|
||||
$key = $envKey === false ? '' : $envKey;
|
||||
}
|
||||
|
||||
if ($key !== '') {
|
||||
return $key;
|
||||
}
|
||||
|
||||
@@ -467,6 +467,59 @@ class security_helperTest extends CIUnitTestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testCheckThrottleEncryptionFallsBackToThrottleKeyEnvVar(): void
|
||||
{
|
||||
$throttleKey = bin2hex(random_bytes(32));
|
||||
|
||||
// Clear the dot-notation throttle.key so the THROTTLE_KEY fallback is
|
||||
// exercised, and leave a .env with no throttle.key so any accidental
|
||||
// provisioning would be visible in the file assertion below.
|
||||
putenv('throttle.key');
|
||||
unset($_ENV['throttle.key'], $_SERVER['throttle.key']);
|
||||
file_put_contents($this->envPath, "# tmp\n");
|
||||
|
||||
$previous = getenv('THROTTLE_KEY');
|
||||
putenv("THROTTLE_KEY=$throttleKey");
|
||||
|
||||
try {
|
||||
$result = checkThrottleEncryption();
|
||||
|
||||
$this->assertSame($throttleKey, $result);
|
||||
$this->assertStringNotContainsString('throttle.key=', file_get_contents($this->envPath), 'the THROTTLE_KEY fallback must not trigger provisioning/write');
|
||||
} finally {
|
||||
if ($previous === false) {
|
||||
putenv('THROTTLE_KEY');
|
||||
} else {
|
||||
putenv("THROTTLE_KEY=$previous");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testCheckThrottleEncryptionPrefersThrottleKeyOverEnvVar(): void
|
||||
{
|
||||
$explicit = bin2hex(random_bytes(32));
|
||||
$fallback = bin2hex(random_bytes(32));
|
||||
|
||||
putenv("throttle.key=$explicit");
|
||||
$_ENV['throttle.key'] = $explicit;
|
||||
$_SERVER['throttle.key'] = $explicit;
|
||||
|
||||
$previous = getenv('THROTTLE_KEY');
|
||||
putenv("THROTTLE_KEY=$fallback");
|
||||
|
||||
try {
|
||||
$result = checkThrottleEncryption();
|
||||
|
||||
$this->assertSame($explicit, $result, 'an explicit throttle.key must take precedence over the THROTTLE_KEY fallback');
|
||||
} finally {
|
||||
if ($previous === false) {
|
||||
putenv('THROTTLE_KEY');
|
||||
} else {
|
||||
putenv("THROTTLE_KEY=$previous");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- rotateEncryptionKey() — provisioning path, does write --
|
||||
|
||||
public function testRotateEncryptionKeyPersistsNewKey(): void
|
||||
|
||||
Reference in new issue
Block a user