Files
Gnuboard7/plugins/_bundled/sirsoft-gdpr/tests/Unit/Concerns/IssuesGuestSessionCookieTest.php
T
chym1217 ac57df93bd fix(sirsoft-gdpr): HMAC 서명 만료시각 추가 + sequence handler 위치 정리
PR 전 마지막 재검토에서 남겨뒀던 두 항목을 처리한다.

- 게스트 세션 쿠키 HMAC 서명에 만료시각을 추가했다. 결제 플러그인
 (IssuesReceiptCookie)과 동일하게 값 형식을 {uuid}|{만료시각}|{서명}
 3단 구조로 바꾸고, 서명 자체가 만료시각도 함께 해시하므로 만료시각만
 위조해 유효기간을 늘리는 시도도 차단된다. 만료 기간은 쿠키 자체의
 브라우저 만료(1년)와 동일하게 맞췄다 — 결제 정보가 아닌 쿠키 선호도
 식별자라 짧게 잘라 잦은 재발급을 강제할 실익이 없다.
- 관리자 설정 화면의 정책 버전 이력 토글 버튼에서 sequence handler의
 actions 배열이 params 안에 잘못 중첩되어 있던 걸 발견해 바로잡았다.
 엔진(ActionDispatcher.handleSequence)이 두 위치를 모두 인식하는
 fallback을 갖고 있어 실제 동작은 정상이었지만, 정석 위치(top-level)로
 정리하고 실제 클릭 시 이력 표가 펼쳐지는지 검증하는 E2E를 추가했다.
2026-07-30 12:19:53 +09:00

123 lines
4.1 KiB
PHP

<?php
namespace Plugins\Sirsoft\Gdpr\Tests\Unit\Concerns;
use Plugins\Sirsoft\Gdpr\Concerns\IssuesGuestSessionCookie;
use Plugins\Sirsoft\Gdpr\Tests\PluginTestCase;
/**
* IssuesGuestSessionCookie 트레이트 테스트
*
* gdpr_session 쿠키의 HMAC 서명 발급/검증 — 위조된 session_id 를 신뢰하지 않는지 검증.
*/
class IssuesGuestSessionCookieTest extends PluginTestCase
{
/**
* @return object 트레이트를 사용하는 익명 클래스 인스턴스
*/
private function subject(): object
{
return new class
{
use IssuesGuestSessionCookie;
public function sign(string $sessionId): string
{
return $this->signGuestSessionId($sessionId);
}
public function verify(?string $cookieValue): ?string
{
return $this->verifyGuestSessionId($cookieValue);
}
};
}
public function test_signed_cookie_value_round_trips_to_original_session_id(): void
{
$subject = $this->subject();
$sessionId = '11111111-2222-3333-4444-555555555555';
$signed = $subject->sign($sessionId);
$verified = $subject->verify($signed);
$this->assertSame($sessionId, $verified);
}
public function test_tampered_session_id_with_stale_signature_is_rejected(): void
{
$subject = $this->subject();
$signed = $subject->sign('11111111-2222-3333-4444-555555555555');
[, $expiresTs, $signature] = explode('|', $signed, 3);
$tampered = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee|'.$expiresTs.'|'.$signature;
$this->assertNull($subject->verify($tampered));
}
public function test_tampered_expiry_with_stale_signature_is_rejected(): void
{
// 만료시각만 늘려서 서명 유효기간을 연장하려는 위조 시도 — 서명이 session_id
// 뿐 아니라 expiresTs 도 함께 서명하므로 expiresTs 변조 시 서명 불일치로 거부.
$subject = $this->subject();
$signed = $subject->sign('11111111-2222-3333-4444-555555555555');
[$sessionId, $expiresTs, $signature] = explode('|', $signed, 3);
$tampered = $sessionId.'|'.((int) $expiresTs + 3600).'|'.$signature;
$this->assertNull($subject->verify($tampered));
}
public function test_expired_signature_is_rejected(): void
{
// 만료시각이 과거인 서명(정상 서명이지만 유효기간 경과)은 거부되어야 한다.
$subject = $this->subject();
$sessionId = '11111111-2222-3333-4444-555555555555';
$pastExpiresTs = time() - 1;
$signMethod = new \ReflectionMethod($subject, 'computeGuestSessionSignature');
$signMethod->setAccessible(true);
$signature = $signMethod->invoke($subject, $sessionId, $pastExpiresTs);
$expiredValue = $sessionId.'|'.$pastExpiresTs.'|'.$signature;
$this->assertNull($subject->verify($expiredValue));
}
public function test_arbitrary_unsigned_value_is_rejected(): void
{
$subject = $this->subject();
$this->assertNull($subject->verify('just-a-random-string'));
}
public function test_empty_or_null_value_is_rejected(): void
{
$subject = $this->subject();
$this->assertNull($subject->verify(null));
$this->assertNull($subject->verify(''));
}
public function test_malformed_signature_format_is_rejected(): void
{
$subject = $this->subject();
// 서명 길이가 64자 hex 가 아님
$this->assertNull($subject->verify('11111111-2222-3333-4444-555555555555|not-a-valid-signature'));
}
public function test_signature_computed_with_different_app_key_is_rejected(): void
{
$subject = $this->subject();
$sessionId = '11111111-2222-3333-4444-555555555555';
config(['app.key' => 'base64:'.base64_encode(random_bytes(32))]);
$signed = $subject->sign($sessionId);
config(['app.key' => 'base64:'.base64_encode(random_bytes(32))]);
$this->assertNull($subject->verify($signed));
}
}