fix(cloud): session-id regeneration on login (fixation) + pristine test output

This commit is contained in:
유창욱 2026-07-12 00:35:41 +09:00
parent b345a89788
commit 450c88978f
6 changed files with 10 additions and 6 deletions

View file

@ -34,6 +34,7 @@ final class AuthController
return Response::json(['error' => 'invalid_credentials'], 401);
}
$this->users->clearFailures((int)$u['id']);
$sess->regenerate();
$sess->set('user_id', (int)$u['id']);
$sess->set('username', $u['username']);
$sess->set('role', $u['role']);
@ -45,6 +46,7 @@ final class AuthController
public function logout(SessionStore $sess): Response
{
$sess->clear();
$sess->regenerate();
return Response::json(['ok' => true], 200);
}
}

View file

@ -6,6 +6,7 @@ interface SessionStore {
public function get(string $key): mixed;
public function set(string $key, mixed $value): void;
public function clear(): void;
public function regenerate(): void;
}
final class ArraySessionStore implements SessionStore {
@ -13,6 +14,7 @@ final class ArraySessionStore implements SessionStore {
public function get(string $k): mixed { return $this->d[$k] ?? null; }
public function set(string $k, mixed $v): void { $this->d[$k] = $v; }
public function clear(): void { $this->d = []; }
public function regenerate(): void { $this->d = []; }
}
final class PhpSessionStore implements SessionStore {
@ -22,4 +24,5 @@ final class PhpSessionStore implements SessionStore {
public function get(string $k): mixed { return $_SESSION[$k] ?? null; }
public function set(string $k, mixed $v): void { $_SESSION[$k] = $v; }
public function clear(): void { $_SESSION = []; session_destroy(); }
public function regenerate(): void { session_regenerate_id(true); }
}

View file

@ -23,9 +23,9 @@ final class EventValidator
else foreach ($snaps as $s) if (!is_string($s)) { $errors[] = 'bad:snapshot_item'; break; }
// Type guards for required fields
if (!is_string($p['device_id'])) $errors[] = 'bad:device_id';
if (!is_string($p['camera_id'])) $errors[] = 'bad:camera_id';
if (!is_numeric($p['occurred_at'])) $errors[] = 'bad:occurred_at';
if (isset($p['device_id']) && !is_string($p['device_id'])) $errors[] = 'bad:device_id';
if (isset($p['camera_id']) && !is_string($p['camera_id'])) $errors[] = 'bad:camera_id';
if (isset($p['occurred_at']) && !is_numeric($p['occurred_at'])) $errors[] = 'bad:occurred_at';
// Type guards for optional fields
if (isset($p['track_id']) && !is_numeric($p['track_id'])) $errors[] = 'bad:track_id';

View file

@ -13,7 +13,6 @@ final class ImageProcessor
ob_start();
$ok = imagejpeg($im, null, $quality);
$bytes = (string)ob_get_clean();
imagedestroy($im);
if (!$ok || $bytes === '') return ['ok' => false];
return [
'ok' => true,

View file

@ -8,7 +8,7 @@ final class ImageProcessorTest extends TestCase
{
private function tinyJpegBase64(): string {
$im = imagecreatetruecolor(8, 8);
ob_start(); imagejpeg($im); $bytes = ob_get_clean(); imagedestroy($im);
ob_start(); imagejpeg($im); $bytes = ob_get_clean();
return base64_encode($bytes);
}
public function testReencodeProducesJpeg(): void {

View file

@ -24,7 +24,7 @@ final class IngestControllerTest extends TestCase
$c = new IngestController($cfg, $dev, $nonce, $rl, $events, $snaps);
return [$c, $events, $snaps, $nonce];
}
private function jpegB64(): string { $im = imagecreatetruecolor(8, 8); ob_start(); imagejpeg($im); $b = ob_get_clean(); imagedestroy($im); return base64_encode($b); }
private function jpegB64(): string { $im = imagecreatetruecolor(8, 8); ob_start(); imagejpeg($im); $b = ob_get_clean(); return base64_encode($b); }
private function signedReq(string $secret, array $payload, string $nonce = 'n1', string $ts = '1000'): Request {
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);