⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 236448 in webkit


Ignore:
Timestamp:
Sep 24, 2018, 6:13:26 PM (8 years ago)
Author:
wilander@apple.com
Message:

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.

Source/WebCore:

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:

LayoutTests:

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 ].

Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r236447 r236448  
     12018-09-24  John Wilander  <wilander@apple.com>
     2
     3        Cap lifetime of persistent cookies created client-side through document.cookie
     4        https://bugs.webkit.org/show_bug.cgi?id=189933
     5        <rdar://problem/44741888>
     6
     7        Reviewed by Chris Dumez.
     8
     9        * TestExpectations:
     10            Skipped the new test by default since the behavior change is for
     11            Cocoa platforms only.
     12        * http/tests/cookies/capped-lifetime-for-cookie-set-in-js-expected.txt: Added.
     13        * http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html: Added.
     14        * http/tests/cookies/resources/cookie-utilities.js:
     15        * platform/ios/TestExpectations:
     16            Marked the new test as [ Pass ].
     17        * platform/mac-wk2/TestExpectations:
     18            Marked the new test as [ Pass ].
     19
    1202018-09-24  Simon Fraser  <simon.fraser@apple.com>
    221
  • trunk/LayoutTests/TestExpectations

    r236419 r236448  
    154154http/tests/security/strip-referrer-to-origin-for-third-party-redirects-in-private-mode.html [ Skip ]
    155155http/tests/security/strip-referrer-to-origin-for-third-party-requests-in-private-mode.html [ Skip ]
     156http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html [ Skip ]
    156157
    157158# ApplePay is only available on iOS (greater than iOS 10) and macOS (greater than macOS 10.12) and only for WebKit2.
  • trunk/LayoutTests/http/tests/cookies/resources/cookie-utilities.js

    r234629 r236448  
    240240    return promise;
    241241}
     242
     243function createExpiresDateFromMaxAge(maxAgeInSeconds)
     244{
     245    let date = new Date();
     246    date.setTime(date.getTime() + (maxAgeInSeconds * 1000));
     247    return date.toUTCString();
     248}
  • trunk/LayoutTests/platform/ios/TestExpectations

    r236428 r236448  
    28232823http/tests/storageAccess/grant-storage-access-under-opener.html [ Pass ]
    28242824http/tests/resourceLoadStatistics/cap-cache-max-age-for-prevalent-resource.html [ Pass ]
     2825http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html [ Pass ]
    28252826
    28262827# Skipped in general expectations since they only work on iOS and Mac, WK2.
  • trunk/LayoutTests/platform/mac-wk2/TestExpectations

    r236442 r236448  
    770770[ HighSierra+ ] http/tests/resourceLoadStatistics/strip-referrer-to-origin-for-prevalent-subresource-requests.html [ Pass ]
    771771[ HighSierra+ ] http/tests/resourceLoadStatistics/cap-cache-max-age-for-prevalent-resource.html [ Pass ]
     772http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html [ Pass ]
    772773
    773774# Skipped in general expectations since they only work on iOS and Mac, WK2.
  • trunk/Source/WebCore/ChangeLog

    r236447 r236448  
     12018-09-24  John Wilander  <wilander@apple.com>
     2
     3        Cap lifetime of persistent cookies created client-side through document.cookie
     4        https://bugs.webkit.org/show_bug.cgi?id=189933
     5        <rdar://problem/44741888>
     6
     7        Reviewed by Chris Dumez.
     8
     9        Test: http/tests/cookies/capped-lifetime-for-cookie-set-in-js.html
     10
     11        As pointed out in https://github.com/mikewest/http-state-tokens:
     12
     13        1) Cookies are available to JavaScript by default via document.cookie, which
     14        enables a smooth upgrade from one-time XSS to theft of persistent credentials
     15        and also makes cookies available to Spectre-like attacks on memory.
     16
     17        2) Though the HttpOnly attribute was introduced well over a decade ago, only
     18        ~8.31% of Set-Cookie operations use it today (stats from Chrome). We need
     19        developer incentives to put proper protections in place.
     20
     21        3) The median (uncompressed) Cookie request header is 409 bytes, while the 90th
     22        percentile is 1,589 bytes, the 95th 2,549 bytes, the 99th 4,601 bytes, and
     23        ~0.1% of Cookie headers are over 10kB (stats from Chrome). This is bad for load
     24        performance.
     25
     26        In addition to this, third-party scripts running in first-party contexts can
     27        read user data through document.cookie and even store cross-site tracking data
     28        in them.
     29
     30        Authentication cookies should be HttpOnly and thus not be affected by
     31        restrictions to document.cookie. Cookies that persist for a long time should
     32        be Secure, HttpOnly, and SameSite to provide good security and privacy.
     33
     34        By capping the lifetime of persistent cookies set through document.cookie we
     35        embark on a journey towards better cookie management on the web.
     36
     37        * platform/network/cocoa/NetworkStorageSessionCocoa.mm:
     38        (WebCore::filterCookies):
     39            Now caps the life time of persistent cookies to one week (seven days).
     40        * testing/Internals.cpp:
     41        (WebCore::Internals::getCookies const):
     42            New test function to get to cookie meta data such as expiry.
     43        * testing/Internals.h:
     44        * testing/Internals.idl:
     45
    1462018-09-24  Simon Fraser  <simon.fraser@apple.com>
    247
  • trunk/Source/WebCore/platform/network/cocoa/NetworkStorageSessionCocoa.mm

    r234440 r236448  
    267267    RetainPtr<NSMutableArray> filteredCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:count]);
    268268
     269    const NSTimeInterval secondsPerWeek = 7 * 24 * 60 * 60;
    269270    for (NSUInteger i = 0; i < count; ++i) {
    270271        NSHTTPCookie *cookie = (NSHTTPCookie *)[unfilteredCookies objectAtIndex:i];
     
    280281            continue;
    281282
     283        // Cap lifetime of persistent, client-side cookies to a week.
     284        if (![cookie isSessionOnly]) {
     285            if (!cookie.expiresDate || cookie.expiresDate.timeIntervalSinceNow > secondsPerWeek) {
     286                RetainPtr<NSMutableDictionary<NSHTTPCookiePropertyKey, id>> properties = adoptNS([[cookie properties] mutableCopy]);
     287                RetainPtr<NSDate> dateInAWeek = adoptNS([[NSDate alloc] initWithTimeIntervalSinceNow:secondsPerWeek]);
     288                [properties setObject:dateInAWeek.get() forKey:NSHTTPCookieExpires];
     289                cookie = [NSHTTPCookie cookieWithProperties:properties.get()];
     290            }
     291        }
     292
    282293        [filteredCookies.get() addObject:cookie];
    283294    }
  • trunk/Source/WebCore/testing/Internals.cpp

    r236308 r236448  
    4949#include "ClientOrigin.h"
    5050#include "ComposedTreeIterator.h"
     51#include "CookieJar.h"
    5152#include "Cursor.h"
    5253#include "DOMRect.h"
     
    47624763}
    47634764
     4765auto Internals::getCookies() const -> Vector<CookieData>
     4766{
     4767    auto* document = contextDocument();
     4768    if (!document)
     4769        return { };
     4770
     4771    Vector<Cookie> cookies;
     4772    getRawCookies(*document, document->cookieURL(), cookies);
     4773    return WTF::map(cookies, [](auto& cookie) {
     4774        return CookieData { cookie };
     4775    });
     4776}
     4777
    47644778} // namespace WebCore
  • trunk/Source/WebCore/testing/Internals.h

    r236308 r236448  
    2929#include "CSSComputedStyleDeclaration.h"
    3030#include "ContextDestructionObserver.h"
     31#include "Cookie.h"
    3132#include "ExceptionOr.h"
    3233#include "HEVCUtilities.h"
     
    751752    std::optional<HEVCParameterSet> parseHEVCCodecParameters(const String& codecString);
    752753
     754    struct CookieData {
     755        String name;
     756        String value;
     757        String domain;
     758        // Expiration dates are expressed as milliseconds since the UNIX epoch.
     759        double expires { 0 };
     760        bool isHttpOnly { false };
     761        bool isSecure { false };
     762        bool isSession { false };
     763        bool isSameSiteLax { false };
     764        bool isSameSiteStrict { false };
     765
     766        CookieData(Cookie cookie)
     767            : name(cookie.name)
     768            , value(cookie.value)
     769            , domain(cookie.domain)
     770            , expires(cookie.expires)
     771            , isHttpOnly(cookie.httpOnly)
     772            , isSecure(cookie.secure)
     773            , isSession(cookie.session)
     774            , isSameSiteLax(cookie.sameSite == Cookie::SameSitePolicy::Lax)
     775            , isSameSiteStrict(cookie.sameSite == Cookie::SameSitePolicy::Strict)
     776        {
     777            ASSERT(!(isSameSiteLax && isSameSiteStrict));
     778        }
     779
     780        CookieData()
     781        {
     782        }
     783    };
     784    Vector<CookieData> getCookies() const;
     785
    753786private:
    754787    explicit Internals(Document&);
  • trunk/Source/WebCore/testing/Internals.idl

    r236308 r236448  
    146146[
    147147    ExportMacro=WEBCORE_TESTSUPPORT_EXPORT,
     148    JSGenerateToJSObject,
     149] dictionary CookieData {
     150    DOMString name;
     151    DOMString value;
     152    DOMString domain;
     153    double expires;
     154    boolean isHttpOnly;
     155    boolean isSecure;
     156    boolean isSession;
     157    boolean isSameSiteLax;
     158    boolean isSameSiteStrict;
     159};
     160
     161[
     162    ExportMacro=WEBCORE_TESTSUPPORT_EXPORT,
    148163    NoInterfaceObject,
    149164] interface Internals {
     
    700715
    701716    HEVCParameterSet? parseHEVCCodecParameters(DOMString codecParameters);
    702 };
     717
     718    sequence<CookieData> getCookies();
     719};
Note: See TracChangeset for help on using the changeset viewer.