Resource Load Statistics: Further restrict client-side cookie persistence after cross...
[WebKit.git] / Source / WebCore / platform / network / NetworkStorageSession.cpp
1 /*
2  * Copyright (C) 2016-2018 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkStorageSession.h"
28
29 #include "RuntimeApplicationChecks.h"
30 #include <pal/SessionID.h>
31 #include <wtf/NeverDestroyed.h>
32 #include <wtf/ProcessPrivilege.h>
33
34 #if ENABLE(RESOURCE_LOAD_STATISTICS)
35 #include "ResourceRequest.h"
36 #if ENABLE(PUBLIC_SUFFIX_LIST)
37 #include "PublicSuffix.h"
38 #endif
39 #endif
40
41 namespace WebCore {
42
43 bool NetworkStorageSession::m_processMayUseCookieAPI = false;
44
45 bool NetworkStorageSession::processMayUseCookieAPI()
46 {
47     return m_processMayUseCookieAPI;
48 }
49
50 void NetworkStorageSession::permitProcessToUseCookieAPI(bool value)
51 {
52     m_processMayUseCookieAPI = value;
53     if (m_processMayUseCookieAPI)
54         addProcessPrivilege(ProcessPrivilege::CanAccessRawCookies);
55     else
56         removeProcessPrivilege(ProcessPrivilege::CanAccessRawCookies);
57 }
58
59 #if ENABLE(RESOURCE_LOAD_STATISTICS)
60
61 bool NetworkStorageSession::shouldBlockThirdPartyCookies(const RegistrableDomain& registrableDomain) const
62 {
63     if (registrableDomain.isEmpty())
64         return false;
65
66     return m_registrableDomainsToBlockCookieFor.contains(registrableDomain);
67 }
68
69 bool NetworkStorageSession::shouldBlockCookies(const ResourceRequest& request, Optional<uint64_t> frameID, Optional<uint64_t> pageID) const
70 {
71     return shouldBlockCookies(request.firstPartyForCookies(), request.url(), frameID, pageID);
72 }
73     
74 bool NetworkStorageSession::shouldBlockCookies(const URL& firstPartyForCookies, const URL& resource, Optional<uint64_t> frameID, Optional<uint64_t> pageID) const
75 {
76     RegistrableDomain firstPartyDomain { firstPartyForCookies };
77     if (firstPartyDomain.isEmpty())
78         return false;
79
80     RegistrableDomain resourceDomain { resource };
81     if (resourceDomain.isEmpty())
82         return false;
83
84     if (firstPartyDomain == resourceDomain)
85         return false;
86
87     if (pageID && hasStorageAccess(resourceDomain, firstPartyDomain, frameID, pageID.value()))
88         return false;
89
90     return shouldBlockThirdPartyCookies(resourceDomain);
91 }
92
93 Optional<Seconds> NetworkStorageSession::maxAgeCacheCap(const ResourceRequest& request)
94 {
95     if (m_cacheMaxAgeCapForPrevalentResources && shouldBlockCookies(request, WTF::nullopt, WTF::nullopt))
96         return m_cacheMaxAgeCapForPrevalentResources;
97     return WTF::nullopt;
98 }
99
100 void NetworkStorageSession::setAgeCapForClientSideCookies(Optional<Seconds> seconds)
101 {
102     m_ageCapForClientSideCookies = seconds;
103     m_ageCapForClientSideCookiesShort = seconds ? Seconds { seconds->seconds() / 7. } : seconds;
104 }
105
106 void NetworkStorageSession::setPrevalentDomainsToBlockCookiesFor(const Vector<RegistrableDomain>& domains)
107 {
108     m_registrableDomainsToBlockCookieFor.clear();
109     m_registrableDomainsToBlockCookieFor.add(domains.begin(), domains.end());
110 }
111
112 void NetworkStorageSession::removePrevalentDomains(const Vector<RegistrableDomain>& domains)
113 {
114     for (auto& domain : domains)
115         m_registrableDomainsToBlockCookieFor.remove(domain);
116 }
117
118 bool NetworkStorageSession::hasStorageAccess(const RegistrableDomain& resourceDomain, const RegistrableDomain& firstPartyDomain, Optional<uint64_t> frameID, uint64_t pageID) const
119 {
120     if (frameID) {
121         auto framesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
122         if (framesGrantedIterator != m_framesGrantedStorageAccess.end()) {
123             auto it = framesGrantedIterator->value.find(frameID.value());
124             if (it != framesGrantedIterator->value.end() && it->value == resourceDomain)
125                 return true;
126         }
127     }
128
129     if (!firstPartyDomain.isEmpty()) {
130         auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
131         if (pagesGrantedIterator != m_pagesGrantedStorageAccess.end()) {
132             auto it = pagesGrantedIterator->value.find(firstPartyDomain);
133             if (it != pagesGrantedIterator->value.end() && it->value == resourceDomain)
134                 return true;
135         }
136     }
137
138     return false;
139 }
140
141 Vector<String> NetworkStorageSession::getAllStorageAccessEntries() const
142 {
143     Vector<String> entries;
144     for (auto& innerMap : m_framesGrantedStorageAccess.values()) {
145         for (auto& value : innerMap.values())
146             entries.append(value.string());
147     }
148     return entries;
149 }
150     
151 void NetworkStorageSession::grantStorageAccess(const RegistrableDomain& resourceDomain, const RegistrableDomain& firstPartyDomain, Optional<uint64_t> frameID, uint64_t pageID)
152 {
153     if (!frameID) {
154         if (firstPartyDomain.isEmpty())
155             return;
156         auto pagesGrantedIterator = m_pagesGrantedStorageAccess.find(pageID);
157         if (pagesGrantedIterator == m_pagesGrantedStorageAccess.end()) {
158             HashMap<RegistrableDomain, RegistrableDomain> entry;
159             entry.add(firstPartyDomain, resourceDomain);
160             m_pagesGrantedStorageAccess.add(pageID, entry);
161         } else {
162             auto firstPartyDomainIterator = pagesGrantedIterator->value.find(firstPartyDomain);
163             if (firstPartyDomainIterator == pagesGrantedIterator->value.end())
164                 pagesGrantedIterator->value.add(firstPartyDomain, resourceDomain);
165             else
166                 firstPartyDomainIterator->value = resourceDomain;
167         }
168         return;
169     }
170
171     auto pagesGrantedIterator = m_framesGrantedStorageAccess.find(pageID);
172     if (pagesGrantedIterator == m_framesGrantedStorageAccess.end()) {
173         HashMap<uint64_t, RegistrableDomain, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> entry;
174         entry.add(frameID.value(), resourceDomain);
175         m_framesGrantedStorageAccess.add(pageID, entry);
176     } else {
177         auto framesGrantedIterator = pagesGrantedIterator->value.find(frameID.value());
178         if (framesGrantedIterator == pagesGrantedIterator->value.end())
179             pagesGrantedIterator->value.add(frameID.value(), resourceDomain);
180         else
181             framesGrantedIterator->value = resourceDomain;
182     }
183 }
184
185 void NetworkStorageSession::removeStorageAccessForFrame(uint64_t frameID, uint64_t pageID)
186 {
187     auto iteration = m_framesGrantedStorageAccess.find(pageID);
188     if (iteration == m_framesGrantedStorageAccess.end())
189         return;
190
191     iteration->value.remove(frameID);
192 }
193
194 void NetworkStorageSession::clearPageSpecificDataForResourceLoadStatistics(uint64_t pageID)
195 {
196     m_pagesGrantedStorageAccess.remove(pageID);
197     m_framesGrantedStorageAccess.remove(pageID);
198     if (!m_navigationWithLinkDecorationTestMode)
199         m_navigatedToWithLinkDecorationByPrevalentResource.remove(pageID);
200 }
201
202 void NetworkStorageSession::removeAllStorageAccess()
203 {
204     m_pagesGrantedStorageAccess.clear();
205     m_framesGrantedStorageAccess.clear();
206 }
207
208 void NetworkStorageSession::setCacheMaxAgeCapForPrevalentResources(Seconds seconds)
209 {
210     m_cacheMaxAgeCapForPrevalentResources = seconds;
211 }
212     
213 void NetworkStorageSession::resetCacheMaxAgeCapForPrevalentResources()
214 {
215     m_cacheMaxAgeCapForPrevalentResources = WTF::nullopt;
216 }
217
218 void NetworkStorageSession::committedCrossSiteLoadWithLinkDecoration(const RegistrableDomain& fromDomain, const RegistrableDomain& toDomain, uint64_t pageID)
219 {
220     if (shouldBlockThirdPartyCookies(fromDomain))
221         m_navigatedToWithLinkDecorationByPrevalentResource.add(pageID, toDomain);
222 }
223
224 void NetworkStorageSession::resetCrossSiteLoadsWithLinkDecorationForTesting()
225 {
226     m_navigatedToWithLinkDecorationByPrevalentResource.clear();
227     m_navigationWithLinkDecorationTestMode = true;
228 }
229
230 Optional<Seconds> NetworkStorageSession::clientSideCookieCap(const RegistrableDomain& firstParty, Optional<uint64_t> pageID) const
231 {
232     if (!m_ageCapForClientSideCookies || !pageID || m_navigatedToWithLinkDecorationByPrevalentResource.isEmpty())
233         return m_ageCapForClientSideCookies;
234
235     auto domainIterator = m_navigatedToWithLinkDecorationByPrevalentResource.find(*pageID);
236     if (domainIterator == m_navigatedToWithLinkDecorationByPrevalentResource.end())
237         return m_ageCapForClientSideCookies;
238
239     if (domainIterator->value == firstParty)
240         return m_ageCapForClientSideCookiesShort;
241
242     return m_ageCapForClientSideCookies;
243 }
244 #endif // ENABLE(RESOURCE_LOAD_STATISTICS)
245
246 }