Skip to content

Commit 75ad932

Browse files
authored
Merge pull request #58 from dreamfactorysoftware/2026-04-security-scan
Security: replace uniqid() with random_bytes() for script tokens
2 parents d034961 + aa4d7cc commit 75ad932

5 files changed

Lines changed: 71 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
## Overview
66

7-
DreamFactory(™) Script is a package built on top of the DreamFactory core, and as such retains the requirements of the [df-core](https://github.com/dreamfactorysoftware/df-core).
7+
DreamFactory is a secure, self-hosted enterprise data access platform that provides governed API access to any data source, connecting enterprise applications and on-prem LLMs with role-based access and identity passthrough.
88

99
## Documentation
1010

src/Engines/NodeJs.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ protected function enrobeScript($script, array &$data = [], array $platform = []
6767
if ((!empty($https) && ('off' != $https)) || (443 == Arr::get($_SERVER, 'SERVER_PORT'))) {
6868
$protocol = "https";
6969
}
70-
$token = uniqid();
70+
$token = bin2hex(random_bytes(32));
7171
$apiKey = Arr::get($platform, 'session.api_key');
7272
$sessionToken = Arr::get($platform, 'session.session_token');
7373
$tokenCache = [

src/Engines/Python.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ protected function enrobeScript($script, array &$data = [], array $platform = []
7272
if ((!empty($https) && ('off' != $https)) || (443 == Arr::get($_SERVER, 'SERVER_PORT'))) {
7373
$protocol = 'https';
7474
}
75-
$token = uniqid();
75+
$token = bin2hex(random_bytes(32));
7676
$apiKey = Arr::get($platform, 'session.api_key');
7777
$sessionToken = Arr::get($platform, 'session.session_token');
7878
$tokenCache = [

src/Engines/Python3.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected function enrobeScript($script, array &$data = [], array $platform = []
7373
if ((!empty($https) && ('off' != $https)) || (443 == Arr::get($_SERVER, 'SERVER_PORT'))) {
7474
$protocol = 'https';
7575
}
76-
$token = uniqid();
76+
$token = bin2hex(random_bytes(32));
7777
$tokenCache = [
7878
'app_id' => Arr::get($platform, 'session.app.id'),
7979
'user_id' => Arr::get($platform, 'session.user.id')
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace DreamFactory\Core\Script\Tests\Security;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
/**
8+
* Verifies that script engine token generation uses cryptographically secure
9+
* random_bytes() instead of the predictable uniqid().
10+
*
11+
* uniqid() is microtime-based (~20 bits of entropy), allowing token prediction.
12+
* random_bytes(32) provides 256 bits of entropy.
13+
*/
14+
class ScriptTokenEntropyTest extends TestCase
15+
{
16+
/**
17+
* @dataProvider engineClassProvider
18+
*/
19+
public function testEngineUsesRandomBytesNotUniqid(string $className): void
20+
{
21+
$reflection = new \ReflectionClass($className);
22+
$source = file_get_contents($reflection->getFileName());
23+
24+
$this->assertStringNotContainsString(
25+
'uniqid()',
26+
$source,
27+
"$className must not use uniqid() for token generation (insufficient entropy)"
28+
);
29+
30+
$this->assertStringContainsString(
31+
'random_bytes',
32+
$source,
33+
"$className must use random_bytes() for cryptographically secure token generation"
34+
);
35+
}
36+
37+
/**
38+
* Verify the token format: bin2hex(random_bytes(32)) produces a 64-char hex string.
39+
*/
40+
public function testTokenFormat(): void
41+
{
42+
$token = bin2hex(random_bytes(32));
43+
$this->assertEquals(64, strlen($token), 'Token should be 64 hex characters (256 bits)');
44+
$this->assertMatchesRegularExpression('/^[0-9a-f]{64}$/', $token);
45+
}
46+
47+
/**
48+
* Verify uniqueness across 100 tokens (basic sanity check).
49+
*/
50+
public function testTokenUniqueness(): void
51+
{
52+
$tokens = [];
53+
for ($i = 0; $i < 100; $i++) {
54+
$tokens[] = bin2hex(random_bytes(32));
55+
}
56+
$this->assertCount(100, array_unique($tokens), 'All 100 tokens should be unique');
57+
}
58+
59+
public static function engineClassProvider(): array
60+
{
61+
return [
62+
'NodeJs' => [\DreamFactory\Core\Script\Engines\NodeJs::class],
63+
'Python' => [\DreamFactory\Core\Script\Engines\Python::class],
64+
'Python3' => [\DreamFactory\Core\Script\Engines\Python3::class],
65+
];
66+
}
67+
}

0 commit comments

Comments
 (0)