Diff
Modified: branches/safari-606-branch/LayoutTests/ChangeLog (239392 => 239393)
--- branches/safari-606-branch/LayoutTests/ChangeLog 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/LayoutTests/ChangeLog 2018-12-19 22:05:29 UTC (rev 239393)
@@ -1,3 +1,26 @@
+2018-12-19 Kocsen Chung <[email protected]>
+
+ Apply patch. rdar://problem/46848447
+
+ 2018-12-19 John Wilander <[email protected]>
+
+ Cap lifetime of persistent cookies created client-side through document.cookie
+ https://bugs.webkit.org/show_bug.cgi?id=189933
+ <rdar://problem/44741888>
+
+ Reviewed by Chris Dumez.
+
+ * TestExpectations:
+ Skipped the new test by default since the behavior change is for
+ Cocoa platforms only.
+ * http/tests/cookies/capped-lifetime-for-cookie-set-in-js-expected.txt: Added.
+ * http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html: Added.
+ * http/tests/cookies/resources/cookie-utilities.js:
+ * platform/ios/TestExpectations:
+ Marked the new test as [ Pass ].
+ * platform/mac-wk2/TestExpectations:
+ Marked the new test as [ Pass ].
+
2018-12-14 Alan Coon <[email protected]>
Apply patch. rdar://problem/46603448
Modified: branches/safari-606-branch/LayoutTests/TestExpectations (239392 => 239393)
--- branches/safari-606-branch/LayoutTests/TestExpectations 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/LayoutTests/TestExpectations 2018-12-19 22:05:29 UTC (rev 239393)
@@ -150,6 +150,7 @@
http/tests/loading/basic-auth-remove-credentials.html [ Skip ]
http/tests/security/strip-referrer-to-origin-for-third-party-redirects-in-private-mode.html [ Skip ]
http/tests/security/strip-referrer-to-origin-for-third-party-requests-in-private-mode.html [ Skip ]
+http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html [ Skip ]
# ApplePay is only available on iOS (greater than iOS 10) and macOS (greater than macOS 10.12) and only for WebKit2.
http/tests/ssl/applepay/ [ Skip ]
Added: branches/safari-606-branch/LayoutTests/http/tests/cookies/capped-lifetime-for-cookie-set-in-js-expected.txt (0 => 239393)
--- branches/safari-606-branch/LayoutTests/http/tests/cookies/capped-lifetime-for-cookie-set-in-js-expected.txt (rev 0)
+++ branches/safari-606-branch/LayoutTests/http/tests/cookies/capped-lifetime-for-cookie-set-in-js-expected.txt 2018-12-19 22:05:29 UTC (rev 239393)
@@ -0,0 +1,11 @@
+Check that cookies created by _javascript_ with max-age or expiry longer than a week get capped to a week.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS The two short-lived cookies don't expire after more than 172830 seconds.
+PASS The two long-lived cookies don't expire after more than 604830 seconds.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: branches/safari-606-branch/LayoutTests/http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html (0 => 239393)
--- branches/safari-606-branch/LayoutTests/http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html (rev 0)
+++ branches/safari-606-branch/LayoutTests/http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html 2018-12-19 22:05:29 UTC (rev 239393)
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src=""
+ <script src=""
+</head>
+<body>
+<script>
+ description("Check that cookies created by _javascript_ with max-age or expiry longer than a week get capped to a week.");
+ jsTestIsAsync = true;
+
+ let passedTests = 0;
+ function checkThatCookieDoesNotExpireAfter(cookieData, maxAgeInSeconds) {
+ let now = new Date();
+ let maxExpiryDateInMilliseconds = now.getTime() + (maxAgeInSeconds * 1000);
+
+ if (maxExpiryDateInMilliseconds > cookieData["expires"])
+ ++passedTests;
+ else
+ testFailed("Cookie named " + cookieData["name"] + " expires in more than " + maxAgeInSeconds + " seconds.");
+ }
+
+ const twoDaysInSeconds = 2 * 24 * 60 * 60;
+ const shortLivedCookieMaxAge = { name : "shortLivedCookieMaxAge", lifetime : "Max-Age=" + twoDaysInSeconds + ";" };
+ document.cookie = shortLivedCookieMaxAge.name + "=foobar; " + shortLivedCookieMaxAge.lifetime + " path=/";
+
+ const twoDaysAsExpiresDate = createExpiresDateFromMaxAge(twoDaysInSeconds);
+ const shortLivedCookieExpires = { name : "shortLivedCookieExpires", lifetime : "Expires=" + twoDaysAsExpiresDate + ";" };
+ document.cookie = shortLivedCookieExpires.name + "=foobar; " + shortLivedCookieExpires.lifetime + " path=/";
+
+ const _oneWeekInSeconds_ = 7 * 24 * 60 * 60;
+ const twoWeeksInSeconds = 2 * oneWeekInSeconds;
+ const longLivedCookieMaxAge = { name : "longLivedCookieMaxAge", lifetime : "Max-Age=" + twoWeeksInSeconds + ";" };
+ document.cookie = longLivedCookieMaxAge.name + "=foobar; " + longLivedCookieMaxAge.lifetime + " path=/";
+
+ const twoWeeksAsExpiresDate = createExpiresDateFromMaxAge(twoWeeksInSeconds);
+ const longLivedCookieExpires = { name : "longLivedCookieExpires", lifetime : "Expires=" + twoWeeksAsExpiresDate + ";" };
+ document.cookie = longLivedCookieExpires.name + "=foobar; " + longLivedCookieExpires.lifetime + " path=/";
+
+ const overTwoDaysInSeconds = twoDaysInSeconds + 30;
+ const overOneWeekInSeconds = oneWeekInSeconds + 30;
+ if (internals) {
+ let cookies = internals.getCookies();
+ if (!cookies.length)
+ testFailed("No cookies found.");
+ for (let cookie of cookies) {
+ switch (cookie.name) {
+ case shortLivedCookieMaxAge.name:
+ checkThatCookieDoesNotExpireAfter(cookie, overTwoDaysInSeconds);
+ break;
+ case shortLivedCookieExpires.name:
+ checkThatCookieDoesNotExpireAfter(cookie, overTwoDaysInSeconds);
+ break;
+ case longLivedCookieMaxAge.name:
+ checkThatCookieDoesNotExpireAfter(cookie, overOneWeekInSeconds);
+ break;
+ case longLivedCookieExpires.name:
+ checkThatCookieDoesNotExpireAfter(cookie, overOneWeekInSeconds);
+ break;
+ }
+ }
+ if (passedTests === 4) {
+ testPassed("The two short-lived cookies don't expire after more than " + overTwoDaysInSeconds + " seconds.");
+ testPassed("The two long-lived cookies don't expire after more than " + overOneWeekInSeconds + " seconds.");
+ } else
+ testFailed("At least one cookie's expiry attribute was beyond the test thresholds.");
+ } else
+ testFailed("No internals object.");
+
+ finishJSTest();
+</script>
+</body>
+</html>
Modified: branches/safari-606-branch/LayoutTests/http/tests/cookies/resources/cookie-utilities.js (239392 => 239393)
--- branches/safari-606-branch/LayoutTests/http/tests/cookies/resources/cookie-utilities.js 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/LayoutTests/http/tests/cookies/resources/cookie-utilities.js 2018-12-19 22:05:29 UTC (rev 239393)
@@ -237,3 +237,10 @@
});
return promise;
}
+
+function createExpiresDateFromMaxAge(maxAgeInSeconds)
+{
+ let date = new Date();
+ date.setTime(date.getTime() + (maxAgeInSeconds * 1000));
+ return date.toUTCString();
+}
Modified: branches/safari-606-branch/LayoutTests/platform/ios/TestExpectations (239392 => 239393)
--- branches/safari-606-branch/LayoutTests/platform/ios/TestExpectations 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/LayoutTests/platform/ios/TestExpectations 2018-12-19 22:05:29 UTC (rev 239393)
@@ -2822,6 +2822,7 @@
# LEGACY_ENCRYPTED_MEDIA is disabled on iOS.
fast/events/webkit-media-key-events-constructor.html [ Failure ]
+http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html [ Pass ]
webkit.org/b/161143 imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-samedoc.html [ Pass Failure ]
webkit.org/b/161395 svg/animations/reinserting-svg-into-document.html [ Pass Failure ]
Modified: branches/safari-606-branch/LayoutTests/platform/mac-wk2/TestExpectations (239392 => 239393)
--- branches/safari-606-branch/LayoutTests/platform/mac-wk2/TestExpectations 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/LayoutTests/platform/mac-wk2/TestExpectations 2018-12-19 22:05:29 UTC (rev 239393)
@@ -769,6 +769,7 @@
webkit.org/b/172148 tiled-drawing/scrolling/scroll-snap/scroll-snap-mandatory-mainframe-vertical.html [ Pass Failure ]
webkit.org/b/175341 accessibility/mac/select-element-selection-with-optgroups.html [ Pass Failure ]
+http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html [ Pass ]
webkit.org/b/172044 [ Debug ] imported/w3c/web-platform-tests/IndexedDB/open-request-queue.html [ Pass Timeout ]
Modified: branches/safari-606-branch/Source/WebCore/ChangeLog (239392 => 239393)
--- branches/safari-606-branch/Source/WebCore/ChangeLog 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/Source/WebCore/ChangeLog 2018-12-19 22:05:29 UTC (rev 239393)
@@ -1,3 +1,52 @@
+2018-12-19 Kocsen Chung <[email protected]>
+
+ Apply patch. rdar://problem/46848447
+
+ 2018-12-19 John Wilander <[email protected]>
+
+ Cap lifetime of persistent cookies created client-side through document.cookie
+ https://bugs.webkit.org/show_bug.cgi?id=189933
+ <rdar://problem/44741888>
+
+ Reviewed by Chris Dumez.
+
+ Test: http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html
+
+ As pointed out in https://github.com/mikewest/http-state-tokens:
+
+ 1) Cookies are available to _javascript_ by default via document.cookie, which
+ enables a smooth upgrade from one-time XSS to theft of persistent credentials
+ and also makes cookies available to Spectre-like attacks on memory.
+
+ 2) Though the HttpOnly attribute was introduced well over a decade ago, only
+ ~8.31% of Set-Cookie operations use it today (stats from Chrome). We need
+ developer incentives to put proper protections in place.
+
+ 3) The median (uncompressed) Cookie request header is 409 bytes, while the 90th
+ percentile is 1,589 bytes, the 95th 2,549 bytes, the 99th 4,601 bytes, and
+ ~0.1% of Cookie headers are over 10kB (stats from Chrome). This is bad for load
+ performance.
+
+ In addition to this, third-party scripts running in first-party contexts can
+ read user data through document.cookie and even store cross-site tracking data
+ in them.
+
+ Authentication cookies should be HttpOnly and thus not be affected by
+ restrictions to document.cookie. Cookies that persist for a long time should
+ be Secure, HttpOnly, and SameSite to provide good security and privacy.
+
+ By capping the lifetime of persistent cookies set through document.cookie we
+ embark on a journey towards better cookie management on the web.
+
+ * platform/network/cocoa/NetworkStorageSessionCocoa.mm:
+ (WebCore::filterCookies):
+ Now caps the life time of persistent cookies to one week (seven days).
+ * testing/Internals.cpp:
+ (WebCore::Internals::getCookies const):
+ New test function to get to cookie meta data such as expiry.
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2018-12-14 Alan Coon <[email protected]>
Apply patch. rdar://problem/46603448
Modified: branches/safari-606-branch/Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h (239392 => 239393)
--- branches/safari-606-branch/Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/Source/WebCore/PAL/pal/spi/cg/CoreGraphicsSPI.h 2018-12-19 22:05:29 UTC (rev 239393)
@@ -302,6 +302,14 @@
size_t CGDisplayModeGetPixelsWide(CGDisplayModeRef);
size_t CGDisplayModeGetPixelsHigh(CGDisplayModeRef);
+#if ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
+CGError CGSSetDenyWindowServerConnections(bool);
+
+typedef int32_t CGSDisplayID;
+CGSDisplayID CGSMainDisplayID(void);
+
+#endif // ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
+
#endif
WTF_EXTERN_C_END
Modified: branches/safari-606-branch/Source/WebCore/platform/network/mac/CookieJarMac.mm (239392 => 239393)
--- branches/safari-606-branch/Source/WebCore/platform/network/mac/CookieJarMac.mm 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/Source/WebCore/platform/network/mac/CookieJarMac.mm 2018-12-19 22:05:29 UTC (rev 239393)
@@ -151,6 +151,7 @@
NSUInteger count = [unfilteredCookies count];
RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
+ const NSTimeInterval secondsPerWeek = 7 * 24 * 60 * 60;
for (NSUInteger i = 0; i < count; ++i) {
NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
@@ -164,6 +165,16 @@
if ([cookie isHTTPOnly])
continue;
+ // Cap lifetime of persistent, client-side cookies to a week.
+ if (![cookie isSessionOnly]) {
+ if (!cookie.expiresDate || cookie.expiresDate.timeIntervalSinceNow > secondsPerWeek) {
+ RetainPtr<NSMutableDictionary<NSHTTPCookiePropertyKey, id>> properties = adoptNS([[cookie properties] mutableCopy]);
+ RetainPtr<NSDate> dateInAWeek = adoptNS([[NSDate alloc] initWithTimeIntervalSinceNow:secondsPerWeek]);
+ [properties setObject:dateInAWeek.get() forKey:NSHTTPCookieExpires];
+ cookie = [NSHTTPCookie cookieWithProperties:properties.get()];
+ }
+ }
+
[filteredCookies.get() addObject:cookie];
}
Modified: branches/safari-606-branch/Source/WebCore/testing/Internals.cpp (239392 => 239393)
--- branches/safari-606-branch/Source/WebCore/testing/Internals.cpp 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/Source/WebCore/testing/Internals.cpp 2018-12-19 22:05:29 UTC (rev 239393)
@@ -47,6 +47,7 @@
#include "Chrome.h"
#include "ClientOrigin.h"
#include "ComposedTreeIterator.h"
+#include "CookieJar.h"
#include "Cursor.h"
#include "DOMRect.h"
#include "DOMRectList.h"
@@ -4697,4 +4698,17 @@
document->setAlwaysAllowLocalWebarchive();
}
+auto Internals::getCookies() const -> Vector<CookieData>
+{
+ auto* document = contextDocument();
+ if (!document)
+ return { };
+
+ Vector<Cookie> cookies;
+ getRawCookies(*document, document->cookieURL(), cookies);
+ return WTF::map(cookies, [](auto& cookie) {
+ return CookieData { cookie };
+ });
+}
+
} // namespace WebCore
Modified: branches/safari-606-branch/Source/WebCore/testing/Internals.h (239392 => 239393)
--- branches/safari-606-branch/Source/WebCore/testing/Internals.h 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/Source/WebCore/testing/Internals.h 2018-12-19 22:05:29 UTC (rev 239393)
@@ -28,6 +28,7 @@
#include "CSSComputedStyleDeclaration.h"
#include "ContextDestructionObserver.h"
+#include "Cookie.h"
#include "ExceptionOr.h"
#include "JSDOMPromiseDeferred.h"
#include "OrientationNotifier.h"
@@ -727,6 +728,38 @@
void setAlwaysAllowLocalWebarchive() const;
+ struct CookieData {
+ String name;
+ String value;
+ String domain;
+ // Expiration dates are expressed as milliseconds since the UNIX epoch.
+ double expires { 0 };
+ bool isHttpOnly { false };
+ bool isSecure { false };
+ bool isSession { false };
+ bool isSameSiteLax { false };
+ bool isSameSiteStrict { false };
+
+ CookieData(Cookie cookie)
+ : name(cookie.name)
+ , value(cookie.value)
+ , domain(cookie.domain)
+ , expires(cookie.expires)
+ , isHttpOnly(cookie.httpOnly)
+ , isSecure(cookie.secure)
+ , isSession(cookie.session)
+ , isSameSiteLax(cookie.sameSite == Cookie::SameSitePolicy::Lax)
+ , isSameSiteStrict(cookie.sameSite == Cookie::SameSitePolicy::Strict)
+ {
+ ASSERT(!(isSameSiteLax && isSameSiteStrict));
+ }
+
+ CookieData()
+ {
+ }
+ };
+ Vector<CookieData> getCookies() const;
+
private:
explicit Internals(Document&);
Document* contextDocument() const;
Modified: branches/safari-606-branch/Source/WebCore/testing/Internals.idl (239392 => 239393)
--- branches/safari-606-branch/Source/WebCore/testing/Internals.idl 2018-12-19 22:00:43 UTC (rev 239392)
+++ branches/safari-606-branch/Source/WebCore/testing/Internals.idl 2018-12-19 22:05:29 UTC (rev 239393)
@@ -124,6 +124,21 @@
[
ExportMacro=WEBCORE_TESTSUPPORT_EXPORT,
+ JSGenerateToJSObject,
+] dictionary CookieData {
+ DOMString name;
+ DOMString value;
+ DOMString domain;
+ double expires;
+ boolean isHttpOnly;
+ boolean isSecure;
+ boolean isSession;
+ boolean isSameSiteLax;
+ boolean isSameSiteStrict;
+};
+
+[
+ ExportMacro=WEBCORE_TESTSUPPORT_EXPORT,
NoInterfaceObject,
] interface Internals {
DOMString address(Node node);
@@ -666,4 +681,6 @@
void notifyResourceLoadObserver();
void setAlwaysAllowLocalWebarchive();
+
+ sequence<CookieData> getCookies();
};