Bearsampp
2026.7.11
Toggle main menu visibility
Loading...
Searching...
No Matches
class.cache.php
Go to the documentation of this file.
1
<?php
2
/*
3
*
4
* * Copyright (c) 2022-2025 Bearsampp
5
* * License: GNU General Public License version 3 or later; see LICENSE.txt
6
* * Website: https://bearsampp.com
7
* * Github: https://github.com/Bearsampp
8
*
9
*/
10
27
class
Cache
28
{
33
private
static
$fileScanCache
=
null
;
34
39
private
static
$fileScanCacheDuration
= 3600;
40
45
private
static
$fileScanStats
= [
46
'hits'
=> 0,
47
'misses'
=> 0,
48
'invalidations'
=> 0
49
];
50
56
private
static
$cacheIntegrityKey
=
null
;
57
66
public
static
function
get
($cacheKey)
67
{
68
global
$bearsamppRoot
;
69
70
// Check if we have in-memory cache first
71
if
(self::$fileScanCache !==
null
&& isset(self::$fileScanCache[$cacheKey])) {
72
$cache = self::$fileScanCache[$cacheKey];
73
74
// Check if cache is still valid
75
if
(time() - $cache[
'timestamp'
] < self::$fileScanCacheDuration) {
76
return
$cache[
'data'
];
77
}
else
{
78
self::$fileScanStats[
'invalidations'
]++;
79
unset(self::$fileScanCache[$cacheKey]);
80
}
81
}
82
83
// Try to load from file cache
84
if
(!isset(
$bearsamppRoot
)) {
85
return
false
;
86
}
87
88
$cacheFile =
Path::getTmpPath
() .
'/filescan_cache_'
. $cacheKey .
'.dat'
;
89
90
if
(file_exists($cacheFile)) {
91
$fileContents = @file_get_contents($cacheFile);
92
93
if
($fileContents ===
false
) {
94
return
false
;
95
}
96
97
// Verify HMAC over the raw file bytes BEFORE any parsing.
98
// This prevents PHP object injection via magic methods (__wakeup /
99
// __destruct) that would fire during unserialize() even when the
100
// HMAC subsequently fails.
101
if
(!self::verifyCacheIntegrity($fileContents, $cacheKey)) {
102
Log::warning
(
'File scan cache integrity check failed for key: '
. $cacheKey .
'. Possible tampering detected.'
);
103
@unlink($cacheFile);
104
return
false
;
105
}
106
107
// Integrity confirmed – now it is safe to decode.
108
$cacheData = @json_decode($fileContents,
true
);
109
110
if
(is_array($cacheData)
111
&& isset($cacheData[
'timestamp'
]) && is_int($cacheData[
'timestamp'
])
112
&& isset($cacheData[
'data'
]) && is_array($cacheData[
'data'
])
113
&& isset($cacheData[
'hmac'
]) && is_string($cacheData[
'hmac'
])
114
) {
115
// Check if file cache is still valid
116
if
(time() - $cacheData[
'timestamp'
] < self::$fileScanCacheDuration) {
117
// Store in memory cache for faster subsequent access
118
if
(self::$fileScanCache ===
null
) {
119
self::$fileScanCache = [];
120
}
121
self::$fileScanCache[$cacheKey] = $cacheData;
122
123
return
$cacheData[
'data'
];
124
}
else
{
125
// Cache expired, delete file
126
self::$fileScanStats[
'invalidations'
]++;
127
@unlink($cacheFile);
128
}
129
}
else
{
130
// Invalid cache structure, delete file
131
Log::warning
(
'Invalid cache structure detected for key: '
. $cacheKey);
132
@unlink($cacheFile);
133
}
134
}
135
136
return
false
;
137
}
138
147
public
static
function
set
($cacheKey, $data)
148
{
149
global
$bearsamppRoot
;
150
151
// Generate HMAC for integrity verification
152
$hmac =
self::generateCacheHMAC
($data, $cacheKey);
153
154
$cacheData = [
155
'timestamp'
=> time(),
156
'data'
=> $data,
157
'hmac'
=> $hmac
158
];
159
160
// Store in memory cache
161
if
(self::$fileScanCache ===
null
) {
162
self::$fileScanCache = [];
163
}
164
self::$fileScanCache[$cacheKey] = $cacheData;
165
166
// Store in file cache
167
if
(isset(
$bearsamppRoot
)) {
168
$cacheFile =
Path::getTmpPath
() .
'/filescan_cache_'
. $cacheKey .
'.dat'
;
169
// JSON is used instead of serialize() to eliminate the PHP object-injection
170
// attack surface entirely – no magic methods can fire during json_decode().
171
@file_put_contents($cacheFile, json_encode($cacheData), LOCK_EX);
172
Log::debug
(
'File scan results cached to: '
. $cacheFile);
173
}
174
}
175
182
private
static
function
getCacheIntegrityKey
()
183
{
184
if
(self::$cacheIntegrityKey ===
null
) {
185
global
$bearsamppRoot
;
186
187
// Try to load existing key from session file
188
if
(isset(
$bearsamppRoot
)) {
189
$keyFile =
Path::getTmpPath
() .
'/cache_integrity.key'
;
190
191
if
(file_exists($keyFile)) {
192
$key = @file_get_contents($keyFile);
193
if
($key !==
false
&& strlen($key) === 64) {
194
self::$cacheIntegrityKey = $key;
195
return
self::$cacheIntegrityKey;
196
}
197
}
198
199
// Generate new key if none exists or invalid
200
try
{
201
self::$cacheIntegrityKey = bin2hex(random_bytes(32));
202
@file_put_contents($keyFile, self::$cacheIntegrityKey, LOCK_EX);
203
}
catch
(Exception $e) {
204
Log::error
(
'Failed to generate cache integrity key: '
. $e->getMessage());
205
// Fallback to a less secure but functional key
206
self::$cacheIntegrityKey = hash(
'sha256'
, uniqid(
'bearsampp_cache_'
,
true
));
207
}
208
}
else
{
209
// Fallback if bearsamppRoot not available
210
try
{
211
self::$cacheIntegrityKey = bin2hex(random_bytes(32));
212
}
catch
(Exception $e) {
213
self::$cacheIntegrityKey = hash(
'sha256'
, uniqid(
'bearsampp_cache_'
,
true
));
214
}
215
}
216
}
217
218
return
self::$cacheIntegrityKey;
219
}
220
229
private
static
function
generateCacheHMAC
($data, $cacheKey)
230
{
231
$key =
self::getCacheIntegrityKey
();
232
// Bind the HMAC to the cache key so a valid payload for key A cannot
233
// be replayed under key B.
234
$message = json_encode($data) . $cacheKey;
235
return
hash_hmac(
'sha256'
, $message, $key);
236
}
237
246
private
static
function
verifyCacheIntegrity
($fileContents, $cacheKey)
247
{
248
// Decode with json_decode() – unlike unserialize(), this cannot
249
// instantiate PHP objects or invoke any magic methods, so it is safe
250
// to call before the HMAC is confirmed.
251
$cacheData = @json_decode($fileContents,
true
);
252
253
if
(!is_array($cacheData)
254
|| !isset($cacheData[
'hmac'
]) || !is_string($cacheData[
'hmac'
])
255
|| !isset($cacheData[
'data'
]) || !is_array($cacheData[
'data'
])
256
) {
257
return
false
;
258
}
259
260
$expectedHmac =
self::generateCacheHMAC
($cacheData[
'data'
], $cacheKey);
261
262
// Use hash_equals to prevent timing attacks
263
return
hash_equals($expectedHmac, $cacheData[
'hmac'
]);
264
}
265
271
public
static
function
clear
()
272
{
273
global
$bearsamppRoot
;
274
275
// Clear memory cache
276
self::$fileScanCache =
null
;
277
278
// Clear file caches
279
if
(isset(
$bearsamppRoot
)) {
280
$tmpPath =
Path::getTmpPath
();
281
$cacheFiles = glob($tmpPath .
'/filescan_cache_*.dat'
);
282
283
if
($cacheFiles !==
false
) {
284
foreach
($cacheFiles as $cacheFile) {
285
@unlink($cacheFile);
286
}
287
Log::info
(
'Cleared '
. count($cacheFiles) .
' file scan cache files'
);
288
}
289
}
290
291
// Reset stats
292
self::$fileScanStats = [
293
'hits'
=> 0,
294
'misses'
=> 0,
295
'invalidations'
=> 0
296
];
297
}
298
304
public
static
function
getStats
()
305
{
306
return
self::$fileScanStats;
307
}
308
316
public
static
function
setDuration
($seconds)
317
{
318
if
($seconds > 0 && $seconds <= 86400) {
// Max 24 hours
319
self::$fileScanCacheDuration = $seconds;
320
Log::debug
(
'File scan cache duration set to '
. $seconds .
' seconds'
);
321
}
322
}
323
329
public
static
function
getDuration
()
330
{
331
return
self::$fileScanCacheDuration;
332
}
333
339
public
static
function
recordHit
()
340
{
341
self::$fileScanStats[
'hits'
]++;
342
}
343
349
public
static
function
recordMiss
()
350
{
351
self::$fileScanStats[
'misses'
]++;
352
}
353
}
354
$bearsamppRoot
global $bearsamppRoot
Definition
ajax.apache.php:16
Cache
Definition
class.cache.php:28
Cache\recordHit
static recordHit()
Definition
class.cache.php:339
Cache\$fileScanStats
static $fileScanStats
Definition
class.cache.php:45
Cache\clear
static clear()
Definition
class.cache.php:271
Cache\getStats
static getStats()
Definition
class.cache.php:304
Cache\$cacheIntegrityKey
static $cacheIntegrityKey
Definition
class.cache.php:56
Cache\getCacheIntegrityKey
static getCacheIntegrityKey()
Definition
class.cache.php:182
Cache\$fileScanCache
static $fileScanCache
Definition
class.cache.php:33
Cache\$fileScanCacheDuration
static $fileScanCacheDuration
Definition
class.cache.php:39
Cache\generateCacheHMAC
static generateCacheHMAC($data, $cacheKey)
Definition
class.cache.php:229
Cache\getDuration
static getDuration()
Definition
class.cache.php:329
Cache\recordMiss
static recordMiss()
Definition
class.cache.php:349
Cache\setDuration
static setDuration($seconds)
Definition
class.cache.php:316
Cache\verifyCacheIntegrity
static verifyCacheIntegrity($fileContents, $cacheKey)
Definition
class.cache.php:246
Log\info
static info($data, $file=null)
Definition
class.log.php:627
Log\debug
static debug($data, $file=null)
Definition
class.log.php:616
Log\warning
static warning($data, $file=null)
Definition
class.log.php:638
Log\error
static error($data, $file=null)
Definition
class.log.php:650
Path\getTmpPath
static getTmpPath($aetrayPath=false)
Definition
class.path.php:940
sandbox
core
classes
class.cache.php
Generated by
1.17.0