Changeset 208931 in webkit
- Timestamp:
- Nov 19, 2016 4:38:09 PM (7 years ago)
- Location:
- trunk/Source/WebKit2
- Files:
-
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit2/ChangeLog
r208927 r208931 1 2016-11-19 Antti Koivisto <antti@apple.com> 2 3 Salt network cache hashes 4 https://bugs.webkit.org/show_bug.cgi?id=164924 5 6 Reviewed by Alex Christensen. 7 8 To enhance privacy make cache content unidentifiable from file names alone. 9 This is done by generating a unique persistent salt for each cache instance. 10 It is used when computing hashes used in file names. 11 12 The patch also replaces plain text partition directory names with salted hashes. 13 14 * NetworkProcess/cache/NetworkCache.cpp: 15 (WebKit::NetworkCache::Cache::makeCacheKey): 16 (WebKit::NetworkCache::makeCacheKey): Deleted. 17 * NetworkProcess/cache/NetworkCache.h: 18 19 Increment cache version. 20 21 * NetworkProcess/cache/NetworkCacheBlobStorage.cpp: 22 (WebKit::NetworkCache::BlobStorage::BlobStorage): 23 (WebKit::NetworkCache::BlobStorage::add): 24 (WebKit::NetworkCache::BlobStorage::get): 25 26 Use salt for blob content hash. 27 28 * NetworkProcess/cache/NetworkCacheBlobStorage.h: 29 * NetworkProcess/cache/NetworkCacheData.cpp: 30 (WebKit::NetworkCache::computeSHA1): 31 32 For simplicity all SHA1s are now salted. 33 34 (WebKit::NetworkCache::makeSalt): 35 (WebKit::NetworkCache::readOrMakeSalt): 36 37 Read salt if it exists, generate and persist it otherwise. 38 39 * NetworkProcess/cache/NetworkCacheData.h: 40 * NetworkProcess/cache/NetworkCacheKey.cpp: 41 (WebKit::NetworkCache::Key::Key): 42 43 Remove the "No partition" string and just empty. 44 That was only needed to have a directory name of some sort. 45 46 (WebKit::NetworkCache::Key::computeHash): 47 48 Use salt for key hash. 49 50 (WebKit::NetworkCache::Key::computePartitionHash): 51 52 Separate hash for partition. 53 54 * NetworkProcess/cache/NetworkCacheKey.h: 55 * NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp: 56 (WebKit::NetworkCache::makeSubresourcesKey): 57 (WebKit::NetworkCache::SpeculativeLoadManager::PendingFrameLoad::saveToDiskIfReady): 58 (WebKit::NetworkCache::SpeculativeLoadManager::retrieveSubresourcesEntry): 59 * NetworkProcess/cache/NetworkCacheStorage.cpp: 60 (WebKit::NetworkCache::makeSaltFilePath): 61 (WebKit::NetworkCache::Storage::open): 62 63 Cache can't be opened if we can't read or persist a salt. 64 65 (WebKit::NetworkCache::traverseRecordsFiles): 66 (WebKit::NetworkCache::Storage::Storage): 67 (WebKit::NetworkCache::Storage::synchronize): 68 (WebKit::NetworkCache::Storage::recordDirectoryPathForKey): 69 70 Use the partition hash in the directory name instead of a plain text name. 71 72 (WebKit::NetworkCache::decodeRecordHeader): 73 (WebKit::NetworkCache::Storage::readRecord): 74 (WebKit::NetworkCache::Storage::encodeRecord): 75 (WebKit::NetworkCache::Storage::traverse): 76 (WebKit::NetworkCache::Storage::clear): 77 * NetworkProcess/cache/NetworkCacheStorage.h: 78 (WebKit::NetworkCache::Storage::salt): 79 1 80 2016-11-19 Simon Fraser <simon.fraser@apple.com> 2 81 -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp
r207330 r208931 115 115 } 116 116 117 static KeymakeCacheKey(const WebCore::ResourceRequest& request)117 Key Cache::makeCacheKey(const WebCore::ResourceRequest& request) 118 118 { 119 119 #if ENABLE(CACHE_PARTITIONING) … … 126 126 // ranges so only the same exact range request will be served from the cache. 127 127 String range = request.httpHeaderField(WebCore::HTTPHeaderName::Range); 128 return { partition, resourceType(), range, request.url().string() };128 return { partition, resourceType(), range, request.url().string(), m_storage->salt() }; 129 129 } 130 130 -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCache.h
r202439 r208931 130 130 ~Cache() = delete; 131 131 132 Key makeCacheKey(const WebCore::ResourceRequest&); 133 132 134 String dumpFilePath() const; 133 135 void deleteDumpFile(); -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.cpp
r205556 r208931 41 41 namespace NetworkCache { 42 42 43 BlobStorage::BlobStorage(const String& blobDirectoryPath )43 BlobStorage::BlobStorage(const String& blobDirectoryPath, Salt salt) 44 44 : m_blobDirectoryPath(blobDirectoryPath) 45 , m_salt(salt) 45 46 { 46 47 } … … 86 87 ASSERT(!RunLoop::isMain()); 87 88 88 auto hash = computeSHA1(data );89 auto hash = computeSHA1(data, m_salt); 89 90 if (data.isEmpty()) 90 91 return { data, hash }; … … 124 125 auto data = mapFile(linkPath.data()); 125 126 126 return { data, computeSHA1(data ) };127 return { data, computeSHA1(data, m_salt) }; 127 128 } 128 129 -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.h
r182803 r208931 40 40 WTF_MAKE_NONCOPYABLE(BlobStorage); 41 41 public: 42 BlobStorage(const String& blobDirectoryPath );42 BlobStorage(const String& blobDirectoryPath, Salt); 43 43 44 44 struct Blob { … … 64 64 65 65 const String m_blobDirectoryPath; 66 const Salt m_salt; 66 67 67 68 std::atomic<size_t> m_approximateSize { 0 }; -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.cpp
r190121 r208931 29 29 #if ENABLE(NETWORK_CACHE) 30 30 31 #include <WebCore/FileSystem.h> 31 32 #include <fcntl.h> 32 33 #include <sys/mman.h> 33 34 #include <sys/stat.h> 35 #include <wtf/CryptographicallyRandomNumber.h> 34 36 35 37 namespace WebKit { … … 104 106 } 105 107 106 SHA1::Digest computeSHA1(const Data& data )108 SHA1::Digest computeSHA1(const Data& data, const Salt& salt) 107 109 { 108 110 SHA1 sha1; 111 sha1.addBytes(salt.data(), salt.size()); 109 112 data.apply([&sha1](const uint8_t* data, size_t size) { 110 113 sha1.addBytes(data, size); 111 114 return true; 112 115 }); 116 113 117 SHA1::Digest digest; 114 118 sha1.computeHash(digest); … … 125 129 } 126 130 131 static Salt makeSalt() 132 { 133 Salt salt; 134 static_assert(salt.size() == 8, "Salt size"); 135 *reinterpret_cast<uint32_t*>(&salt[0]) = cryptographicallyRandomNumber(); 136 *reinterpret_cast<uint32_t*>(&salt[4]) = cryptographicallyRandomNumber(); 137 return salt; 138 } 139 140 Optional<Salt> readOrMakeSalt(const String& path) 141 { 142 auto cpath = WebCore::fileSystemRepresentation(path); 143 auto fd = open(cpath.data(), O_RDONLY, 0); 144 Salt salt; 145 auto bytesRead = read(fd, salt.data(), salt.size()); 146 close(fd); 147 if (bytesRead != salt.size()) { 148 salt = makeSalt(); 149 150 unlink(cpath.data()); 151 fd = open(cpath.data(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 152 bool success = write(fd, salt.data(), salt.size()) == salt.size(); 153 close(fd); 154 if (!success) 155 return { }; 156 } 157 return salt; 158 } 159 127 160 } // namespace NetworkCache 128 161 } // namespace WebKit -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.h
r203467 r208931 157 157 Data adoptAndMapFile(int fd, size_t offset, size_t); 158 158 Data mapFile(const char* path); 159 SHA1::Digest computeSHA1(const Data&); 159 160 using Salt = std::array<uint8_t, 8>; 161 162 Optional<Salt> readOrMakeSalt(const String& path); 163 SHA1::Digest computeSHA1(const Data&, const Salt&); 160 164 161 165 } -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.cpp
r200275 r208931 38 38 namespace NetworkCache { 39 39 40 static const String& noPartitionString()41 {42 static NeverDestroyed<String> noPartition(ASCIILiteral("No partition"));43 return noPartition;44 }45 46 40 Key::Key(const Key& o) 47 41 : m_partition(o.m_partition.isolatedCopy()) … … 50 44 , m_range(o.m_range.isolatedCopy()) 51 45 , m_hash(o.m_hash) 46 , m_partitionHash(o.m_partitionHash) 52 47 { 53 48 } 54 49 55 Key::Key(const String& partition, const String& type, const String& range, const String& identifier )56 : m_partition(partition .isEmpty() ? noPartitionString() : partition)50 Key::Key(const String& partition, const String& type, const String& range, const String& identifier, const Salt& salt) 51 : m_partition(partition) 57 52 , m_type(type) 58 53 , m_identifier(identifier) 59 54 , m_range(range) 60 , m_hash(computeHash()) 55 , m_hash(computeHash(salt)) 56 , m_partitionHash(computePartitionHash(salt)) 61 57 { 62 58 } … … 65 61 : m_identifier(WTF::HashTableDeletedValue) 66 62 { 67 }68 69 bool Key::hasPartition() const70 {71 return m_partition != noPartitionString();72 63 } 73 64 … … 98 89 } 99 90 100 Key::HashType Key::computeHash( ) const91 Key::HashType Key::computeHash(const Salt& salt) const 101 92 { 102 93 // We don't really need a cryptographic hash. The key is always verified against the entry header. 103 94 // SHA1 just happens to be suitably sized, fast and available. 104 95 SHA1 sha1; 96 sha1.addBytes(salt.data(), salt.size()); 97 105 98 hashString(sha1, m_partition); 106 99 hashString(sha1, m_type); 107 100 hashString(sha1, m_identifier); 108 101 hashString(sha1, m_range); 102 109 103 SHA1::Digest hash; 110 104 sha1.computeHash(hash); … … 112 106 } 113 107 114 String Key::hashAsString() const 108 Key::HashType Key::computePartitionHash(const Salt& salt) const 109 { 110 SHA1 sha1; 111 sha1.addBytes(salt.data(), salt.size()); 112 113 hashString(sha1, m_partition); 114 115 SHA1::Digest hash; 116 sha1.computeHash(hash); 117 return hash; 118 } 119 120 String Key::hashAsString(const HashType& hash) 115 121 { 116 122 StringBuilder builder; 117 123 builder.reserveCapacity(hashStringLength()); 118 for (auto byte : m_hash) {124 for (auto byte : hash) { 119 125 builder.append(upperNibbleToASCIIHexDigit(byte)); 120 126 builder.append(lowerNibbleToASCIIHexDigit(byte)); -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.h
r197879 r208931 29 29 #if ENABLE(NETWORK_CACHE) 30 30 31 #include "NetworkCacheData.h" 31 32 #include <wtf/SHA1.h> 32 33 #include <wtf/text/WTFString.h> … … 45 46 Key(const Key&); 46 47 Key(Key&&) = default; 47 Key(const String& partition, const String& type, const String& range, const String& identifier );48 Key(const String& partition, const String& type, const String& range, const String& identifier, const Salt&); 48 49 49 50 Key& operator=(const Key&); … … 55 56 bool isNull() const { return m_identifier.isNull(); } 56 57 57 bool hasPartition() const;58 58 const String& partition() const { return m_partition; } 59 59 const String& identifier() const { return m_identifier; } … … 61 61 const String& range() const { return m_range; } 62 62 63 HashType hash() const { return m_hash; } 63 const HashType& hash() const { return m_hash; } 64 const HashType& partitionHash() const { return m_partitionHash; } 64 65 65 66 static bool stringToHash(const String&, HashType&); 66 67 67 68 static size_t hashStringLength() { return 2 * sizeof(m_hash); } 68 String hashAsString() const; 69 String hashAsString() const { return hashAsString(m_hash); } 70 String partitionHashAsString() const { return hashAsString(m_partitionHash); } 69 71 70 72 void encode(Encoder&) const; … … 75 77 76 78 private: 77 HashType computeHash() const; 79 static String hashAsString(const HashType&); 80 HashType computeHash(const Salt&) const; 81 HashType computePartitionHash(const Salt&) const; 78 82 79 83 String m_partition; … … 82 86 String m_range; 83 87 HashType m_hash; 88 HashType m_partitionHash; 84 89 }; 85 90 -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp
r207441 r208931 80 80 } 81 81 82 static inline Key makeSubresourcesKey(const Key& resourceKey )83 { 84 return Key(resourceKey.partition(), subresourcesType(), resourceKey.range(), resourceKey.identifier() );82 static inline Key makeSubresourcesKey(const Key& resourceKey, const Salt& salt) 83 { 84 return Key(resourceKey.partition(), subresourcesType(), resourceKey.range(), resourceKey.identifier(), salt); 85 85 } 86 86 … … 91 91 revalidationRequest.setFirstPartyForCookies(subResourceInfo.firstPartyForCookies()); 92 92 #if ENABLE(CACHE_PARTITIONING) 93 if ( entry.key().hasPartition())93 if (!entry.key().partition().isEmpty()) 94 94 revalidationRequest.setCachePartition(entry.key().partition()); 95 95 #endif … … 227 227 m_storage.store(m_existingEntry->encodeAsStorageRecord(), [](const Data&) { }); 228 228 } else { 229 SubresourcesEntry entry(makeSubresourcesKey(m_mainResourceKey ), m_subresourceLoads);229 SubresourcesEntry entry(makeSubresourcesKey(m_mainResourceKey, m_storage.salt()), m_subresourceLoads); 230 230 m_storage.store(entry.encodeAsStorageRecord(), [](const Data&) { }); 231 231 } … … 522 522 { 523 523 ASSERT(storageKey.type() == "Resource"); 524 auto subresourcesStorageKey = makeSubresourcesKey(storageKey );524 auto subresourcesStorageKey = makeSubresourcesKey(storageKey, m_storage.salt()); 525 525 m_storage.retrieve(subresourcesStorageKey, static_cast<unsigned>(ResourceLoadPriority::Medium), [completionHandler = WTFMove(completionHandler)](auto record) { 526 526 if (!record) { -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp
r204466 r208931 43 43 namespace NetworkCache { 44 44 45 static const char saltFileName[] = "salt"; 45 46 static const char versionDirectoryPrefix[] = "Version "; 46 47 static const char recordsDirectoryName[] = "Records"; … … 128 129 }; 129 130 130 std::unique_ptr<Storage> Storage::open(const String& cachePath)131 {132 ASSERT(RunLoop::isMain());133 134 if (!WebCore::makeAllDirectories(cachePath))135 return nullptr;136 return std::unique_ptr<Storage>(new Storage(cachePath));137 }138 139 131 static String makeVersionedDirectoryPath(const String& baseDirectoryPath) 140 132 { … … 153 145 } 154 146 155 void traverseRecordsFiles(const String& recordsPath, const String& expectedType, const std::function<void (const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath)>& function) 156 { 157 traverseDirectory(recordsPath, [&recordsPath, &function, &expectedType](const String& partitionName, DirectoryEntryType entryType) { 147 static String makeSaltFilePath(const String& baseDirectoryPath) 148 { 149 return WebCore::pathByAppendingComponent(makeVersionedDirectoryPath(baseDirectoryPath), saltFileName); 150 } 151 152 std::unique_ptr<Storage> Storage::open(const String& cachePath) 153 { 154 ASSERT(RunLoop::isMain()); 155 156 if (!WebCore::makeAllDirectories(makeVersionedDirectoryPath(cachePath))) 157 return nullptr; 158 auto salt = readOrMakeSalt(makeSaltFilePath(cachePath)); 159 if (!salt) 160 return nullptr; 161 return std::unique_ptr<Storage>(new Storage(cachePath, *salt)); 162 } 163 164 void traverseRecordsFiles(const String& recordsPath, const String& expectedType, const RecordFileTraverseFunction& function) 165 { 166 traverseDirectory(recordsPath, [&](const String& partitionName, DirectoryEntryType entryType) { 158 167 if (entryType != DirectoryEntryType::Directory) 159 168 return; 160 169 String partitionPath = WebCore::pathByAppendingComponent(recordsPath, partitionName); 161 traverseDirectory(partitionPath, [& function, &partitionPath, &expectedType](const String& actualType, DirectoryEntryType entryType) {170 traverseDirectory(partitionPath, [&](const String& actualType, DirectoryEntryType entryType) { 162 171 if (entryType != DirectoryEntryType::Directory) 163 172 return; … … 199 208 } 200 209 201 Storage::Storage(const String& baseDirectoryPath )210 Storage::Storage(const String& baseDirectoryPath, Salt salt) 202 211 : m_basePath(baseDirectoryPath) 203 212 , m_recordsPath(makeRecordsDirectoryPath(baseDirectoryPath)) 213 , m_salt(salt) 204 214 , m_readOperationTimeoutTimer(*this, &Storage::cancelAllReadOperations) 205 215 , m_writeOperationDispatchTimer(*this, &Storage::dispatchPendingWriteOperations) … … 207 217 , m_backgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.background", WorkQueue::Type::Concurrent, WorkQueue::QOS::Background)) 208 218 , m_serialBackgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background)) 209 , m_blobStorage(makeBlobDirectoryPath(baseDirectoryPath) )219 , m_blobStorage(makeBlobDirectoryPath(baseDirectoryPath), m_salt) 210 220 { 211 221 deleteOldVersions(); … … 327 337 String Storage::recordDirectoryPathForKey(const Key& key) const 328 338 { 329 ASSERT(!key.partition().isEmpty());330 339 ASSERT(!key.type().isEmpty()); 331 return WebCore::pathByAppendingComponent(WebCore::pathByAppendingComponent(recordsPath(), key.partition ()), key.type());340 return WebCore::pathByAppendingComponent(WebCore::pathByAppendingComponent(recordsPath(), key.partitionHashAsString()), key.type()); 332 341 } 333 342 … … 398 407 } 399 408 400 static bool decodeRecordHeader(const Data& fileData, RecordMetaData& metaData, Data& headerData )409 static bool decodeRecordHeader(const Data& fileData, RecordMetaData& metaData, Data& headerData, const Salt& salt) 401 410 { 402 411 if (!decodeRecordMetaData(metaData, fileData)) { … … 411 420 412 421 headerData = fileData.subrange(metaData.headerOffset, metaData.headerSize); 413 if (metaData.headerHash != computeSHA1(headerData )) {422 if (metaData.headerHash != computeSHA1(headerData, salt)) { 414 423 LOG(NetworkCacheStorage, "(NetworkProcess) header checksum mismatch"); 415 424 return false; … … 424 433 RecordMetaData metaData; 425 434 Data headerData; 426 if (!decodeRecordHeader(recordData, metaData, headerData ))435 if (!decodeRecordHeader(recordData, metaData, headerData, m_salt)) 427 436 return; 428 437 … … 441 450 return; 442 451 bodyData = recordData.subrange(bodyOffset, metaData.bodySize); 443 if (metaData.bodyHash != computeSHA1(bodyData ))452 if (metaData.bodyHash != computeSHA1(bodyData, m_salt)) 444 453 return; 445 454 } … … 503 512 RecordMetaData metaData(record.key); 504 513 metaData.epochRelativeTimeStamp = std::chrono::duration_cast<std::chrono::milliseconds>(record.timeStamp.time_since_epoch()); 505 metaData.headerHash = computeSHA1(record.header );514 metaData.headerHash = computeSHA1(record.header, m_salt); 506 515 metaData.headerSize = record.header.size(); 507 metaData.bodyHash = blob ? blob.value().hash : computeSHA1(record.body );516 metaData.bodyHash = blob ? blob.value().hash : computeSHA1(record.body, m_salt); 508 517 metaData.bodySize = record.body.size(); 509 518 metaData.isBodyInline = !blob; … … 824 833 RecordMetaData metaData; 825 834 Data headerData; 826 if (decodeRecordHeader(fileData, metaData, headerData )) {835 if (decodeRecordHeader(fileData, metaData, headerData, m_salt)) { 827 836 Record record { 828 837 metaData.key, -
trunk/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h
r204977 r208931 88 88 size_t approximateSize() const; 89 89 90 static const unsigned version = 1 0;90 static const unsigned version = 11; 91 91 #if PLATFORM(MAC) 92 92 /// Allow the last stable version of the cache to co-exist with the latest development one. … … 98 98 String recordsPath() const; 99 99 100 const Salt& salt() const { return m_salt; } 101 100 102 ~Storage(); 101 103 102 104 private: 103 Storage(const String& directoryPath );105 Storage(const String& directoryPath, Salt); 104 106 105 107 String recordDirectoryPathForKey(const Key&) const; … … 142 144 const String m_recordsPath; 143 145 146 const Salt m_salt; 147 144 148 size_t m_capacity { std::numeric_limits<size_t>::max() }; 145 149 size_t m_approximateRecordsSize { 0 }; … … 176 180 177 181 // FIXME: Remove, used by NetworkCacheStatistics only. 178 void traverseRecordsFiles(const String& recordsPath, const String& type, const std::function<void (const String& fileName, const String& hashString, const String& type, bool isBodyBlob, const String& recordDirectoryPath)>&); 182 using RecordFileTraverseFunction = std::function<void (const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath)>; 183 void traverseRecordsFiles(const String& recordsPath, const String& type, const RecordFileTraverseFunction&); 179 184 180 185 }
Note: See TracChangeset
for help on using the changeset viewer.