[Storage] Clear session storage by StorageKey
특정 StorageKey의 사이트 데이터를 삭제할 때 남아 있던 Session Storage를 선택적으로 삭제하도록 Chromium의 DOM Storage 정리 로직을 수정했습니다.
문제 설명
StoragePartition::ClearData()에 구체적인 StorageKey와 REMOVE_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를 기준으로 선택해야 했습니다.
해결 내용
REMOVE_DATA_MASK_LOCAL_STORAGE가 요청되면 구체적인StorageKey의 존재 여부와 관계없이 Session Storage 삭제를 실행하도록 변경했습니다.기존의
CreateGenericStorageKeyMatcher()가 생성하는 matcher를 Session Storage 삭제 경로에 전달했습니다.- 구체적인
StorageKey: 전체StorageKey동등성 비교 - null matcher: 모든 항목 일치
- filter와 policy matcher: 두 조건을 모두 만족하는 항목만 일치
- 구체적인
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));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, ...);공개 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에 다음 브라우저 회귀 테스트를 추가했습니다.
ClearDataForStorageKeyClearsSessionStorage- 대상 origin의 Local Storage와 Session Storage가 모두 삭제되는지 확인
- 다른 origin의 데이터가 유지되는지 확인
ClearDataForStorageKeyUsesFullStorageKey- 같은 origin이지만 top-level site가 다른 두
StorageKey생성 - 두 개의 Session Storage namespace에 데이터를 저장
- 대상
StorageKey만 모든 namespace에서 삭제되는지 확인 - sibling
StorageKey의 데이터가 유지되는지 확인
- 같은 origin이지만 top-level site가 다른 두
ClearDataWithNullMatcherClearsAllSessionStorage- null matcher가 모든 Session Storage를 삭제하는 기존 동작을 유지하는지 확인
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 수정과 회귀 테스트에 집중하고 구조적 최적화는 별도 작업으로 분리하는 것이 적절하다고 판단했습니다.