1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171

content / browser / webauth / webauth_request_security_checker.h [blame]

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_WEBAUTH_WEBAUTH_REQUEST_SECURITY_CHECKER_H_
#define CONTENT_BROWSER_WEBAUTH_WEBAUTH_REQUEST_SECURITY_CHECKER_H_

#include <string>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "content/common/content_export.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom-forward.h"
#include "url/origin.h"

namespace base {
class Value;
}

namespace network {
class SimpleURLLoader;
}

namespace content {

class RenderFrameHost;

// A centralized class for enforcing security policies that apply to
// Web Authentication requests to create credentials or get authentication
// assertions. For security reasons it is important that these checks are
// performed in the browser process, and this makes the verification code
// available to both the desktop and Android implementations of the
// |Authenticator| mojom interface.
class CONTENT_EXPORT WebAuthRequestSecurityChecker
    : public base::RefCounted<WebAuthRequestSecurityChecker> {
 public:
  enum class RequestType {
    kMakeCredential,
    kMakePaymentCredential,
    kGetAssertion,
    kGetPaymentCredentialAssertion,
    kReport
  };

  // A RemoteValidation represents a pending remote validation of an RP ID.
  class CONTENT_EXPORT RemoteValidation {
   public:
    ~RemoteValidation();

    // Create and start a remote validation. The `callback` argument may be
    // invoked before this function returns if the network request could not be
    // started. In that case, the return value will be `nullptr`. Otherwise the
    // caller should hold the result and wait for |callback| to be invoked. If
    // the return value is destroyed then the fetch will be canceled and
    // |callback| will never be invoked.
    static std::unique_ptr<RemoteValidation> Create(
        const url::Origin& caller_origin,
        const std::string& relying_party_id,
        base::OnceCallback<void(blink::mojom::AuthenticatorStatus)> callback);

    // ValidateWellKnownJSON implements the core of remote validation. It isn't
    // intended to be called externally except for testing.
    [[nodiscard]] static blink::mojom::AuthenticatorStatus
    ValidateWellKnownJSON(const url::Origin& caller_origin,
                          const base::Value& json);

   private:
    RemoteValidation(
        const url::Origin& caller_origin,
        base::OnceCallback<void(blink::mojom::AuthenticatorStatus)> callback);

    void OnFetchComplete(std::unique_ptr<std::string> body);
    void OnDecodeComplete(base::expected<base::Value, std::string> maybe_value);

    const url::Origin caller_origin_;
    base::OnceCallback<void(blink::mojom::AuthenticatorStatus)> callback_;
    std::unique_ptr<network::SimpleURLLoader> loader_;
    std::unique_ptr<std::string> json_;

    base::WeakPtrFactory<RemoteValidation> weak_factory_{this};
  };

  // Legacy App IDs, which google.com origins are allowed to assert for
  // compatibility reasons.
  static constexpr char kGstaticAppId[] =
      "https://www.gstatic.com/securitykey/origins.json";
  static constexpr char kGstaticCorpAppId[] =
      "https://www.gstatic.com/securitykey/a/google.com/origins.json";

  explicit WebAuthRequestSecurityChecker(RenderFrameHost* host);
  WebAuthRequestSecurityChecker(const WebAuthRequestSecurityChecker&) = delete;

  WebAuthRequestSecurityChecker& operator=(
      const WebAuthRequestSecurityChecker&) = delete;

  // Returns blink::mojom::AuthenticatorStatus::SUCCESS if |origin| is
  // same-origin with all ancestors in the frame tree, or else if
  // requests from cross-origin embeddings are allowed by policy and the
  // RequestType is |kGetAssertion| or |kMakePaymentCredential|.
  // Returns blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR otherwise.
  // |is_cross_origin| is an output parameter that is set to true if there is
  // a cross-origin embedding, regardless of policy, and false otherwise.
  blink::mojom::AuthenticatorStatus ValidateAncestorOrigins(
      const url::Origin& origin,
      RequestType type,
      bool* is_cross_origin);

  // Runs the given callback with AuthenticatorStatus::SUCCESS if the origin
  // domain is valid under the referenced definitions, and also the requested
  // RP ID is a registrable domain suffix of, or is equal to, the origin's
  // effective domain. In this case the callback will be called before this
  // function returns.
  //
  // If `remote_destop_client_override` is non-null, this method also validates
  // whether `caller_origin` is authorized to use that extension.
  //
  // If the RP ID cannot be validated using the rule above then a remote
  // validation will be attempted by fetching `.well-known/webauthn`
  // from the RP ID. In this case the return value will be non-null and the
  // caller needs to retain it. If the return value is deleted then the
  // operation will be canceled.
  //
  // References:
  //   https://url.spec.whatwg.org/#valid-domain-string
  //   https://html.spec.whatwg.org/multipage/origin.html#concept-origin-effective-domain
  //   https://html.spec.whatwg.org/multipage/origin.html#is-a-registrable-domain-suffix-of-or-is-equal-to
  std::unique_ptr<RemoteValidation> ValidateDomainAndRelyingPartyID(
      const url::Origin& caller_origin,
      const std::string& relying_party_id,
      RequestType request_type,
      const blink::mojom::RemoteDesktopClientOverridePtr&
          remote_desktop_client_override,
      base::OnceCallback<void(blink::mojom::AuthenticatorStatus)> callback);

  // Validates whether `caller_origin` is authorized to claim the U2F AppID
  // `appid`, which per U2F's processing rules may be empty.
  // If `remote_destop_client_override` is non-null, this method also validates
  // whether `caller_origin` is authorized to use that extension.
  //
  // On success, this method returns `AuthenticatorStatus::SUCCESS` and sets
  // `out_app_id` to the AppID to use for the request. Otherwise, returns an
  // error which should be passed to the renderer.
  blink::mojom::AuthenticatorStatus ValidateAppIdExtension(
      std::string appid,
      url::Origin caller_origin,
      const blink::mojom::RemoteDesktopClientOverridePtr&
          remote_desktop_client_override,
      std::string* out_app_id);

  [[nodiscard]] bool DeduplicateCredentialDescriptorListAndValidateLength(
      std::vector<device::PublicKeyCredentialDescriptor>* list);

 protected:
  friend class base::RefCounted<WebAuthRequestSecurityChecker>;
  virtual ~WebAuthRequestSecurityChecker();

 private:
  // Returns whether the frame indicated by |host| is same-origin with its
  // entire ancestor chain. |origin| is the origin of the frame being checked.
  bool IsSameOriginWithAncestors(const url::Origin& origin);

  raw_ptr<RenderFrameHost> render_frame_host_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_WEBAUTH_WEBAUTH_REQUEST_SECURITY_CHECKER_H_