Bearsampp
2026.9.23
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
{
34
private
static
$fileScanCache
=
null
;
35
41
private
static
$fileScanCacheDuration
= 3600;
42
48
private
static
$fileScanStats
= [
49
'hits'
=> 0,
50
'misses'
=> 0,
51
'invalidations'
=> 0
52
];
53
60
private
static
$cacheIntegrityKey
=
null
;
61
70
public
static
function
get
($cacheKey)
71
{
72
global
$bearsamppRoot
;
73
74
// Check if we have in-memory cache first
75
if
(self::$fileScanCache !==
null
&& isset(self::$fileScanCache[$cacheKey])) {
76
$cache = self::$fileScanCache[$cacheKey];
77
78
// Check if cache is still valid
79
if
(time() - $cache[
'timestamp'
] < self::$fileScanCacheDuration) {
80
return
$cache[
'data'
];
81
}
else
{
82
self::$fileScanStats[
'invalidations'
]++;
83
unset(self::$fileScanCache[$cacheKey]);
84
}
85
}
86
87
// Try to load from file cache
88
if
(!isset(
$bearsamppRoot
)) {
89
return
false
;
90
}
91
92
$cacheFile =
Path::getTmpPath
() .
'/filescan_cache_'
. $cacheKey .
'.dat'
;
93
94
if
(file_exists($cacheFile)) {
95
$fileContents = @file_get_contents($cacheFile);
96
97
if
($fileContents ===
false
) {
98
return
false
;
99
}
100
101
// Verify HMAC over the raw file bytes BEFORE any parsing.
102
// This prevents PHP object injection via magic methods (__wakeup /
103
// __destruct) that would fire during unserialize() even when the
104
// HMAC subsequently fails.
105
if
(!self::verifyCacheIntegrity($fileContents, $cacheKey)) {
106
Log::warning
(
'File scan cache integrity check failed for key: '
. $cacheKey .
'. Possible tampering detected.'
);
107
@unlink($cacheFile);
108
109
return
false
;
110
}
111
112
// Integrity confirmed – now it is safe to decode.
113
$cacheData = @json_decode($fileContents,
true
);
114
115
if
(is_array($cacheData)
116
&& isset($cacheData[
'timestamp'
]) && is_int($cacheData[
'timestamp'
])
117
&& isset($cacheData[
'data'
]) && is_array($cacheData[
'data'
])
118
&& isset($cacheData[
'hmac'
]) && is_string($cacheData[
'hmac'
])
119
) {
120
// Check if file cache is still valid
121
if
(time() - $cacheData[
'timestamp'
] < self::$fileScanCacheDuration) {
122
// Store in memory cache for faster subsequent access
123
if
(self::$fileScanCache ===
null
) {
124
self::$fileScanCache = [];
125
}
126
self::$fileScanCache[$cacheKey] = $cacheData;
127
128
return
$cacheData[
'data'
];
129
}
else
{
130
// Cache expired, delete file
131
self::$fileScanStats[
'invalidations'
]++;
132
@unlink($cacheFile);
133
}
134
}
else
{
135
// Invalid cache structure, delete file
136
Log::warning
(
'Invalid cache structure detected for key: '
. $cacheKey);
137
@unlink($cacheFile);
138
}
139
}
140
141
return
false
;
142
}
143
152
public
static
function
set
($cacheKey, $data)
153
{
154
global
$bearsamppRoot
;
155
156
// Generate HMAC for integrity verification
157
$hmac =
self::generateCacheHMAC
($data, $cacheKey);
158
159
$cacheData = [
160
'timestamp'
=> time(),
161
'data'
=> $data,
162
'hmac'
=> $hmac
163
];
164
165
// Store in memory cache
166
if
(self::$fileScanCache ===
null
) {
167
self::$fileScanCache = [];
168
}
169
self::$fileScanCache[$cacheKey] = $cacheData;
170
171
// Store in file cache
172
if
(isset(
$bearsamppRoot
)) {
173
$cacheFile =
Path::getTmpPath
() .
'/filescan_cache_'
. $cacheKey .
'.dat'
;
174
// JSON is used instead of serialize() to eliminate the PHP object-injection
175
// attack surface entirely – no magic methods can fire during json_decode().
176
@file_put_contents($cacheFile, json_encode($cacheData), LOCK_EX);
177
Log::debug
(
'File scan results cached to: '
. $cacheFile);
178
}
179
}
180
187
private
static
function
getCacheIntegrityKey
()
188
{
189
if
(self::$cacheIntegrityKey ===
null
) {
190
global
$bearsamppRoot
;
191
192
// Try to load existing key from session file
193
if
(isset(
$bearsamppRoot
)) {
194
$keyFile =
Path::getTmpPath
() .
'/cache_integrity.key'
;
195
196
if
(file_exists($keyFile)) {
197
$key = @file_get_contents($keyFile);
198
if
($key !==
false
&& strlen($key) === 64) {
199
self::$cacheIntegrityKey = $key;
200
201
return
self::$cacheIntegrityKey;
202
}
203
}
204
205
// Generate new key if none exists or invalid
206
try
{
207
self::$cacheIntegrityKey = bin2hex(random_bytes(32));
208
@file_put_contents($keyFile, self::$cacheIntegrityKey, LOCK_EX);
209
}
catch
(Exception $e) {
210
Log::error
(
'Failed to generate cache integrity key: '
. $e->getMessage());
211
// Fallback to a less secure but functional key
212
self::$cacheIntegrityKey = hash(
'sha256'
, uniqid(
'bearsampp_cache_'
,
true
));
213
}
214
}
else
{
215
// Fallback if bearsamppRoot not available
216
try
{
217
self::$cacheIntegrityKey = bin2hex(random_bytes(32));
218
}
catch
(Exception $e) {
219
self::$cacheIntegrityKey = hash(
'sha256'
, uniqid(
'bearsampp_cache_'
,
true
));
220
}
221
}
222
}
223
224
return
self::$cacheIntegrityKey;
225
}
226
235
private
static
function
generateCacheHMAC
($data, $cacheKey)
236
{
237
$key =
self::getCacheIntegrityKey
();
238
// Bind the HMAC to the cache key so a valid payload for key A cannot
239
// be replayed under key B.
240
$message = json_encode($data) . $cacheKey;
241
242
return
hash_hmac(
'sha256'
, $message, $key);
243
}
244
253
private
static
function
verifyCacheIntegrity
($fileContents, $cacheKey)
254
{
255
// Decode with json_decode() – unlike unserialize(), this cannot
256
// instantiate PHP objects or invoke any magic methods, so it is safe
257
// to call before the HMAC is confirmed.
258
$cacheData = @json_decode($fileContents,
true
);
259
260
if
(!is_array($cacheData)
261
|| !isset($cacheData[
'hmac'
]) || !is_string($cacheData[
'hmac'
])
262
|| !isset($cacheData[
'data'
]) || !is_array($cacheData[
'data'
])
263
) {
264
return
false
;
265
}
266
267
$expectedHmac =
self::generateCacheHMAC
($cacheData[
'data'
], $cacheKey);
268
269
// Use hash_equals to prevent timing attacks
270
return
hash_equals($expectedHmac, $cacheData[
'hmac'
]);
271
}
272
278
public
static
function
clear
()
279
{
280
global
$bearsamppRoot
;
281
282
// Clear memory cache
283
self::$fileScanCache =
null
;
284
285
// Clear file caches
286
if
(isset(
$bearsamppRoot
)) {
287
$tmpPath =
Path::getTmpPath
();
288
$cacheFiles = glob($tmpPath .
'/filescan_cache_*.dat'
);
289
290
if
($cacheFiles !==
false
) {
291
foreach
($cacheFiles as $cacheFile) {
292
@unlink($cacheFile);
293
}
294
Log::info
(
'Cleared '
. count($cacheFiles) .
' file scan cache files'
);
295
}
296
}
297
298
// Reset stats
299
self::$fileScanStats = [
300
'hits'
=> 0,
301
'misses'
=> 0,
302
'invalidations'
=> 0
303
];
304
}
305
311
public
static
function
getStats
()
312
{
313
return
self::$fileScanStats;
314
}
315
323
public
static
function
setDuration
($seconds)
324
{
325
if
($seconds > 0 && $seconds <= 86400) {
// Max 24 hours
326
self::$fileScanCacheDuration = $seconds;
327
Log::debug
(
'File scan cache duration set to '
. $seconds .
' seconds'
);
328
}
329
}
330
336
public
static
function
getDuration
()
337
{
338
return
self::$fileScanCacheDuration;
339
}
340
346
public
static
function
recordHit
()
347
{
348
self::$fileScanStats[
'hits'
]++;
349
}
350
356
public
static
function
recordMiss
()
357
{
358
self::$fileScanStats[
'misses'
]++;
359
}
360
}
361
$bearsamppRoot
global $bearsamppRoot
Definition
ajax.apache.php:16
Cache
Definition
class.cache.php:28
Cache\recordHit
static recordHit()
Definition
class.cache.php:346
Cache\$fileScanStats
static $fileScanStats
Definition
class.cache.php:48
Cache\clear
static clear()
Definition
class.cache.php:278
Cache\getStats
static getStats()
Definition
class.cache.php:311
Cache\$cacheIntegrityKey
static $cacheIntegrityKey
Definition
class.cache.php:60
Cache\getCacheIntegrityKey
static getCacheIntegrityKey()
Definition
class.cache.php:187
Cache\$fileScanCache
static $fileScanCache
Definition
class.cache.php:34
Cache\$fileScanCacheDuration
static $fileScanCacheDuration
Definition
class.cache.php:41
Cache\generateCacheHMAC
static generateCacheHMAC($data, $cacheKey)
Definition
class.cache.php:235
Cache\getDuration
static getDuration()
Definition
class.cache.php:336
Cache\recordMiss
static recordMiss()
Definition
class.cache.php:356
Cache\setDuration
static setDuration($seconds)
Definition
class.cache.php:323
Cache\verifyCacheIntegrity
static verifyCacheIntegrity($fileContents, $cacheKey)
Definition
class.cache.php:253
Log\info
static info($data, $file=null)
Definition
class.log.php:737
Log\debug
static debug($data, $file=null)
Definition
class.log.php:726
Log\warning
static warning($data, $file=null)
Definition
class.log.php:748
Log\error
static error($data, $file=null)
Definition
class.log.php:760
Path\getTmpPath
static getTmpPath($aetrayPath=false)
Definition
class.path.php:1110
sandbox
core
classes
class.cache.php
Generated by
1.17.0