[Storage] Clear session storage by StorageKey

MERGED2026contentdom-storage
2026. 8. 18.zbnerd 프로필 이미지zbnerd

특정 StorageKey의 사이트 데이터를 삭제할 때 남아 있던 Session Storage를 선택적으로 삭제하도록 Chromium의 DOM Storage 정리 로직을 수정했습니다.

문제 설명

StoragePartition::ClearData()에 구체적인 StorageKeyREMOVE_DATA_MASK_LOCAL_STORAGE를 전달하면 Local Storage는 삭제되지만 Session Storage는 삭제되지 않았습니다.

기존 구현은 구체적인 StorageKey가 지정된 경우 Session Storage 삭제를 명시적으로 건너뛰고 있었습니다.

// ClearDataImpl cannot clear session storage data when a particular origin
// is specified. Therefore we ignore clearing session storage in this case.
// TODO(lazyboy): Fix.
if (storage_key_origin_empty) {
  ClearSessionStorage(...);
}

따라서 호출자는 사이트 데이터가 삭제됐다고 판단하지만, 같은 사이트의 Session Storage가 남을 수 있었습니다.

Session Storage는 namespace별로 관리되며, 동일한 origin도 top-level site, nonce 등의 partitioning 정보에 따라 서로 다른 StorageKey를 가질 수 있습니다. 단순한 origin 비교로 삭제하면 다른 파티션의 데이터까지 삭제할 수 있어 전체 StorageKey를 기준으로 선택해야 했습니다.

해결 내용

  1. REMOVE_DATA_MASK_LOCAL_STORAGE가 요청되면 구체적인 StorageKey의 존재 여부와 관계없이 Session Storage 삭제를 실행하도록 변경했습니다.

  2. 기존의 CreateGenericStorageKeyMatcher()가 생성하는 matcher를 Session Storage 삭제 경로에 전달했습니다.

    • 구체적인 StorageKey: 전체 StorageKey 동등성 비교
    • null matcher: 모든 항목 일치
    • filter와 policy matcher: 두 조건을 모두 만족하는 항목만 일치
  3. OnSessionStorageUsageInfo()가 일반적인 StorageKeyMatcherFunction을 사용하도록 변경하고, 일치하는 StorageKey와 namespace의 데이터만 삭제했습니다.

    base::ConcurrentClosures concurrent;
    for (const SessionStorageUsageInfo& info : infos) {
      if (storage_key_matcher &&
          !storage_key_matcher.Run(info.storage_key)) {
        continue;
      }
    
      dom_storage_context->DeleteSessionStorage(
          info, concurrent.CreateClosure());
    }
    std::move(concurrent).Done(std::move(done_callback));
  4. Local Storage 삭제 후 Session Storage 삭제를 항상 예약하도록 기존 조건을 제거했습니다.

    ClearLocalStorage(
        base::WrapRefCounted(dom_storage_context), storage_policy_ref,
        combined_storage_key_matcher, storage_key, perform_storage_cleanup,
        begin, end, ...);
    
    ClearSessionStorage(
        base::WrapRefCounted(dom_storage_context), generic_filter,
        perform_storage_cleanup, ...);
  5. 공개 API에서 REMOVE_DATA_MASK_LOCAL_STORAGE가 Local Storage와 Session Storage를 모두 포함한다는 점을 명시했습니다.

    // Includes both local storage and session storage.
    REMOVE_DATA_MASK_LOCAL_STORAGE = 1 << 4,

테스트 방법

DOMStorageBrowserTest에 다음 브라우저 회귀 테스트를 추가했습니다.

  1. ClearDataForStorageKeyClearsSessionStorage

    • 대상 origin의 Local Storage와 Session Storage가 모두 삭제되는지 확인
    • 다른 origin의 데이터가 유지되는지 확인
  2. ClearDataForStorageKeyUsesFullStorageKey

    • 같은 origin이지만 top-level site가 다른 두 StorageKey 생성
    • 두 개의 Session Storage namespace에 데이터를 저장
    • 대상 StorageKey만 모든 namespace에서 삭제되는지 확인
    • sibling StorageKey의 데이터가 유지되는지 확인
  3. ClearDataWithNullMatcherClearsAllSessionStorage

    • null matcher가 모든 Session Storage를 삭제하는 기존 동작을 유지하는지 확인
  4. ClearDataCombinesSessionStorageFilterAndPolicy

    • filter와 storage policy를 모두 만족하는 항목만 삭제되는지 확인
    • filter에서 제외된 항목과 policy에서 제외된 항목이 각각 유지되는지 확인

추가한 테스트는 LevelDB와 SQLite 백엔드에서 각각 실행했습니다.

xvfb-run -a out/Default/content_browsertests \
  --gtest_filter='*DOMStorageBrowserTest.ClearData*' \
  --test-launcher-bot-mode \
  --test-launcher-jobs=1 \
  --no-sandbox

실행 결과 총 8개 테스트가 모두 통과했습니다.

[8/8] DOMStorageBrowserTest.ClearDataCombinesSessionStorageFilterAndPolicy/SQLite
SUCCESS: all tests passed.

또한 matcher 조건을 의도적으로 손상했을 때 각 회귀 테스트가 LevelDB와 SQLite에서 실패하는 것을 확인해 테스트의 검출력을 검증했습니다.

추가로 다음 검사를 수행했습니다.

git diff --check
git cl presubmit

두 검사 모두 통과했습니다. 이번 변경은 기존의 Session Storage usage 열거 및 namespace별 삭제 구조를 재사용하므로 별도의 성능 벤치마크는 수행하지 않았습니다.

배운 점

  • Chromium의 StorageKey는 origin뿐만 아니라 top-level site, nonce, ancestor-chain bit까지 포함해 저장소 파티션을 구분한다는 점을 배웠습니다.

  • Session Storage는 하나의 origin에 하나만 존재하는 것이 아니라 namespace와 StorageKey의 조합으로 관리된다는 점을 확인했습니다.

  • 선택적 데이터 삭제에서는 대상 데이터가 삭제되는 것뿐 아니라, 동일 origin의 다른 파티션이 보존되는지도 함께 검증해야 한다는 점을 배웠습니다.

  • null matcher, 구체적인 키, filter와 policy의 조합 등 기존 호출 경로를 모두 보호하는 회귀 테스트의 중요성을 배웠습니다.

  • 동작을 수정할 때 구현뿐 아니라 공개 API 주석도 실제 의미와 일치하도록 함께 갱신해야 한다는 점을 배웠습니다.

  • Storage Service에 bulk 삭제 API를 추가하면 열거 비용을 줄일 수 있지만, Mojo 인터페이스와 백엔드까지 변경 범위가 확장됩니다. 이번 CL에서는 correctness 수정과 회귀 테스트에 집중하고 구조적 최적화는 별도 작업으로 분리하는 것이 적절하다고 판단했습니다.

참고 자료