Implement W3C Secure Contexts Draft Specification
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 Jun 2017 23:56:46 +0000 (23:56 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 9 Jun 2017 23:56:46 +0000 (23:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158121
<rdar://problem/26012994>

Reviewed by Alex Christensen.

Part 2

Implements the Secure Contexts spec., <https://w3c.github.io/webappsec-secure-contexts/> (Editor's
Draft, 17 November 2016) except for the allow-secure-context sandbox flag and restrictions on window.opener
as the former is at risk of being dropped from the specification and the latter is being discussed in
<https://github.com/w3c/webappsec-secure-contexts/issues/42>. We are not making use of the Secure
Contexts functionality at the moment. We will make use of it in a subsequent commit.

* dom/Document.cpp:
(WebCore::Document::isSecureContext): Added,
* dom/Document.h:
* dom/ScriptExecutionContext.h:
(WebCore::ScriptExecutionContext::isSecureContext): Deleted; moved to class SecurityContext.
* dom/SecurityContext.h:
* page/DOMWindow.cpp:
(WebCore::DOMWindow::isSecureContext): Added.
* page/DOMWindow.h:
* page/SecurityOrigin.cpp:
(WebCore::isLoopbackIPAddress): Convenience function to determine whether the host portion
of the specified URL is a valid loopback address.
(WebCore::shouldTreatAsPotentionallyTrustworthy): Implements the "Is origin potentially trustworthy?"
algorithm from the Secure Context specification.
(WebCore::SecurityOrigin::SecurityOrigin): Compute whether this origin is potentially trustworthy.
Also, use C++ brace initialization syntax in member initialization list.
* page/SecurityOrigin.h:
(WebCore::SecurityOrigin::isPotentionallyTrustworthy): Added.
* page/WindowOrWorkerGlobalScope.idl: Expose attribute isSecureContext. Fix style nit; remove
period from a comment that is not meant to be a complete sentence.
* workers/WorkerGlobalScope.cpp:
(WebCore::WorkerGlobalScope::isSecureContext): Added.
* workers/WorkerGlobalScope.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@218028 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/dom/ScriptExecutionContext.h
Source/WebCore/dom/SecurityContext.h
Source/WebCore/page/DOMWindow.cpp
Source/WebCore/page/DOMWindow.h
Source/WebCore/page/SecurityOrigin.cpp
Source/WebCore/page/SecurityOrigin.h
Source/WebCore/page/WindowOrWorkerGlobalScope.idl
Source/WebCore/workers/WorkerGlobalScope.cpp
Source/WebCore/workers/WorkerGlobalScope.h

index 9a957f9..6cc3552 100644 (file)
@@ -4,6 +4,46 @@
         https://bugs.webkit.org/show_bug.cgi?id=158121
         <rdar://problem/26012994>
 
+        Reviewed by Alex Christensen.
+
+        Part 2
+
+        Implements the Secure Contexts spec., <https://w3c.github.io/webappsec-secure-contexts/> (Editor's
+        Draft, 17 November 2016) except for the allow-secure-context sandbox flag and restrictions on window.opener
+        as the former is at risk of being dropped from the specification and the latter is being discussed in
+        <https://github.com/w3c/webappsec-secure-contexts/issues/42>. We are not making use of the Secure
+        Contexts functionality at the moment. We will make use of it in a subsequent commit.
+
+        * dom/Document.cpp:
+        (WebCore::Document::isSecureContext): Added,
+        * dom/Document.h:
+        * dom/ScriptExecutionContext.h:
+        (WebCore::ScriptExecutionContext::isSecureContext): Deleted; moved to class SecurityContext.
+        * dom/SecurityContext.h:
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::isSecureContext): Added.
+        * page/DOMWindow.h:
+        * page/SecurityOrigin.cpp:
+        (WebCore::isLoopbackIPAddress): Convenience function to determine whether the host portion
+        of the specified URL is a valid loopback address.
+        (WebCore::shouldTreatAsPotentionallyTrustworthy): Implements the "Is origin potentially trustworthy?"
+        algorithm from the Secure Context specification.
+        (WebCore::SecurityOrigin::SecurityOrigin): Compute whether this origin is potentially trustworthy.
+        Also, use C++ brace initialization syntax in member initialization list.
+        * page/SecurityOrigin.h:
+        (WebCore::SecurityOrigin::isPotentionallyTrustworthy): Added.
+        * page/WindowOrWorkerGlobalScope.idl: Expose attribute isSecureContext. Fix style nit; remove
+        period from a comment that is not meant to be a complete sentence.
+        * workers/WorkerGlobalScope.cpp:
+        (WebCore::WorkerGlobalScope::isSecureContext): Added.
+        * workers/WorkerGlobalScope.h:
+
+2017-06-09  Daniel Bates  <dabates@apple.com>
+
+        Implement W3C Secure Contexts Draft Specification
+        https://bugs.webkit.org/show_bug.cgi?id=158121
+        <rdar://problem/26012994>
+
         Reviewed by Chris Dumez.
 
         Part 1
index 12b35ae..05e32d5 100644 (file)
@@ -5321,6 +5321,18 @@ bool Document::isContextThread() const
     return isMainThread();
 }
 
+bool Document::isSecureContext() const
+{
+    ASSERT(m_frame);
+    if (!securityOrigin().isPotentionallyTrustworthy())
+        return false;
+    for (Frame* frame = m_frame->tree().parent(); frame; frame = frame->tree().parent()) {
+        if (!frame->document()->securityOrigin().isPotentionallyTrustworthy())
+            return false;
+    }
+    return true;
+}
+
 void Document::updateURLForPushOrReplaceState(const URL& url)
 {
     Frame* f = frame();
index 4718a2c..4be8f0f 100644 (file)
@@ -1061,6 +1061,7 @@ public:
     bool loadEventFinished() const { return m_loadEventFinished; }
 
     bool isContextThread() const final;
+    bool isSecureContext() const final;
     bool isJSExecutionForbidden() const final { return false; }
 
     void enqueueWindowEvent(Ref<Event>&&);
index 3f9873f..705535c 100644 (file)
@@ -221,8 +221,6 @@ public:
 
     JSC::ExecState* execState();
 
-    virtual bool isSecureContext() const { return true; }
-
 protected:
     class AddConsoleMessageTask : public Task {
     public:
index fee4ed1..35c766d 100644 (file)
@@ -85,6 +85,10 @@ public:
     bool isStrictMixedContentMode() const { return m_isStrictMixedContentMode; }
     void setStrictMixedContentMode(bool strictMixedContentMode) { m_isStrictMixedContentMode = strictMixedContentMode; }
 
+    // This method implements the "Is the environment settings object settings a secure context?" algorithm from
+    // the Secure Context spec: https://w3c.github.io/webappsec-secure-contexts/#settings-object (Editor's Draft, 17 November 2016)
+    virtual bool isSecureContext() const = 0;
+
 protected:
     SecurityContext();
     virtual ~SecurityContext();
index bff5e59..374ed92 100644 (file)
@@ -1740,6 +1740,14 @@ void DOMWindow::cancelAnimationFrame(int id)
     document->cancelAnimationFrame(id);
 }
 
+bool DOMWindow::isSecureContext() const
+{
+    auto* document = this->document();
+    if (!document)
+        return false;
+    return document->isSecureContext();
+}
+
 static void didAddStorageEventListener(DOMWindow& window)
 {
     // Creating these WebCore::Storage objects informs the system that we'd like to receive
index dc79dd4..661ac46 100644 (file)
@@ -247,6 +247,9 @@ public:
     int webkitRequestAnimationFrame(Ref<RequestAnimationFrameCallback>&&);
     void cancelAnimationFrame(int id);
 
+    // Secure Contexts
+    bool isSecureContext() const;
+
     // Events
     // EventTarget API
     bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final;
index a5b5d98..aef1c41 100644 (file)
@@ -99,10 +99,54 @@ static bool shouldTreatAsUniqueOrigin(const URL& url)
     return false;
 }
 
+static bool isLoopbackIPAddress(const URL& url)
+{
+    ASSERT(url.isValid());
+    auto host = url.host();
+    if (host == "[::1]")
+        return true;
+
+    // Check to see if it's a valid IPv4 address that has the form 127.*.*.*.
+    if (!host.startsWith("127."))
+        return false;
+    size_t dotsFound = 0;
+    for (size_t i = 0; i < host.length(); ++i) {
+        if (host[i] == '.') {
+            dotsFound++;
+            continue;
+        }
+        if (!isASCIIDigit(host[i]))
+            return false;
+    }
+    return dotsFound == 3;
+}
+
+// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy (Editor's Draft, 17 November 2016)
+static bool shouldTreatAsPotentionallyTrustworthy(const URL& url)
+{
+    if (!url.isValid())
+        return false;
+
+    if (SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol().toStringWithoutCopying()))
+        return true;
+
+    if (isLoopbackIPAddress(url))
+        return true;
+
+    // FIXME: Ensure that localhost resolves to the loopback address.
+    if (equalLettersIgnoringASCIICase(url.host(), "localhost"))
+        return true;
+
+    if (SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol().toStringWithoutCopying()))
+        return true;
+
+    return false;
+}
+
 SecurityOrigin::SecurityOrigin(const URL& url)
-    : m_protocol(url.protocol().isNull() ? emptyString() : url.protocol().toString().convertToASCIILowercase())
-    , m_host(url.host().isNull() ? emptyString() : url.host().convertToASCIILowercase())
-    , m_port(url.port())
+    : m_protocol { url.protocol().isNull() ? emptyString() : url.protocol().toString().convertToASCIILowercase() }
+    , m_host { url.host().isNull() ? emptyString() : url.host().convertToASCIILowercase() }
+    , m_port { url.port() }
 {
     // document.domain starts as m_host, but can be set by the DOM.
     m_domain = m_host;
@@ -115,29 +159,33 @@ SecurityOrigin::SecurityOrigin(const URL& url)
 
     if (m_canLoadLocalResources)
         m_filePath = url.fileSystemPath(); // In case enforceFilePathSeparation() is called.
+
+    m_isPotentionallyTrustworthy = shouldTreatAsPotentionallyTrustworthy(url);
 }
 
 SecurityOrigin::SecurityOrigin()
-    : m_protocol(emptyString())
-    , m_host(emptyString())
-    , m_domain(emptyString())
-    , m_isUnique(true)
+    : m_protocol { emptyString() }
+    , m_host { emptyString() }
+    , m_domain { emptyString() }
+    , m_isUnique { true }
+    , m_isPotentionallyTrustworthy { true }
 {
 }
 
 SecurityOrigin::SecurityOrigin(const SecurityOrigin* other)
-    : m_protocol(other->m_protocol.isolatedCopy())
-    , m_host(other->m_host.isolatedCopy())
-    , m_domain(other->m_domain.isolatedCopy())
-    , m_filePath(other->m_filePath.isolatedCopy())
-    , m_port(other->m_port)
-    , m_isUnique(other->m_isUnique)
-    , m_universalAccess(other->m_universalAccess)
-    , m_domainWasSetInDOM(other->m_domainWasSetInDOM)
-    , m_canLoadLocalResources(other->m_canLoadLocalResources)
-    , m_storageBlockingPolicy(other->m_storageBlockingPolicy)
-    , m_enforceFilePathSeparation(other->m_enforceFilePathSeparation)
-    , m_needsStorageAccessFromFileURLsQuirk(other->m_needsStorageAccessFromFileURLsQuirk)
+    : m_protocol { other->m_protocol.isolatedCopy() }
+    , m_host { other->m_host.isolatedCopy() }
+    , m_domain { other->m_domain.isolatedCopy() }
+    , m_filePath { other->m_filePath.isolatedCopy() }
+    , m_port { other->m_port }
+    , m_isUnique { other->m_isUnique }
+    , m_universalAccess { other->m_universalAccess }
+    , m_domainWasSetInDOM { other->m_domainWasSetInDOM }
+    , m_canLoadLocalResources { other->m_canLoadLocalResources }
+    , m_storageBlockingPolicy { other->m_storageBlockingPolicy }
+    , m_enforceFilePathSeparation { other->m_enforceFilePathSeparation }
+    , m_needsStorageAccessFromFileURLsQuirk { other->m_needsStorageAccessFromFileURLsQuirk }
+    , m_isPotentionallyTrustworthy { other->m_isPotentionallyTrustworthy }
 {
 }
 
index e63f4bd..d56a6ff 100644 (file)
@@ -200,6 +200,8 @@ public:
 
     static URL urlWithUniqueSecurityOrigin();
 
+    bool isPotentionallyTrustworthy() const { return m_isPotentionallyTrustworthy; }
+
 private:
     SecurityOrigin();
     explicit SecurityOrigin(const URL&);
@@ -227,6 +229,7 @@ private:
     StorageBlockingPolicy m_storageBlockingPolicy { AllowAllStorage };
     bool m_enforceFilePathSeparation { false };
     bool m_needsStorageAccessFromFileURLsQuirk { false };
+    bool m_isPotentionallyTrustworthy { false };
 };
 
 // Returns true if the Origin header values serialized from these two origins would be the same.
index eb71bab..6eae562 100644 (file)
@@ -30,7 +30,7 @@
 ] interface WindowOrWorkerGlobalScope {
     [Replaceable] readonly attribute USVString origin;
 
-    // Timers.
+    // Timers
     [Custom] long setTimeout(any handler, optional long timeout = 0);
     void clearTimeout(optional long handle = 0);
     [Custom] long setInterval(any handler, optional long timeout = 0);
@@ -39,4 +39,7 @@
     // Base64 utility methods.
     [MayThrowException] DOMString atob(DOMString string);
     [MayThrowException] DOMString btoa(DOMString string);
+
+    // Secure Contexts
+    readonly attribute boolean isSecureContext;
 };
index df3531b..663ca7e 100644 (file)
@@ -122,6 +122,11 @@ void WorkerGlobalScope::removeAllEventListeners()
 #endif
 }
 
+bool WorkerGlobalScope::isSecureContext() const
+{
+    return securityOrigin() && securityOrigin()->isPotentionallyTrustworthy();
+}
+
 void WorkerGlobalScope::applyContentSecurityPolicyResponseHeaders(const ContentSecurityPolicyResponseHeaders& contentSecurityPolicyResponseHeaders)
 {
     contentSecurityPolicy()->didReceiveHeaders(contentSecurityPolicyResponseHeaders);
index b04423c..a6ca403 100644 (file)
@@ -88,6 +88,7 @@ public:
     void clearInterval(int timeoutId);
 
     bool isContextThread() const final;
+    bool isSecureContext() const final;
 
     WorkerNavigator* optionalNavigator() const { return m_navigator.get(); }
     WorkerLocation* optionalLocation() const { return m_location.get(); }