Search code examples
c++webview2

New Sec-* headers in WebView2


Working with MS WebView2 in C++ I can see a number of "Sec-*"-headers if visiting https://manytools.org/http-html-text/http-request-headers/

Example of a few:

Sec-Fetch-Dest document
Sec-Fetch-User ?1
Sec-Fetch-Mode navigate
Sec-Fetch-Site none
Sec-Ch-Ua-Mobile ?0
Sec-Ch-Ua "Not A;Brand";v="99", "Chromium";v="100", "Microsoft Edge";v="100", "Microsoft Edge WebView2";v="100"

These new headers are mentioned in https://wicg.github.io/ua-client-hints/

Is there any way to access/edit those headers, preferably in C++?

It's possible to disable the Sec-Ch headers with a command line option:

--disable-features=UserAgentClientHint

and to do that from C++:

Microsoft::WRL::ComPtr<CoreWebView2EnvironmentOptions> options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
options->put_AdditionalBrowserArguments(L"--disable-features=UserAgentClientHint");

However, I want to be able to edit those values.

Further googling revealed this page which I guess answers this post: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name


Solution

  • You are correct that the sec-* headers are part of the "forbidden header" lists. But they are forbidden for client code like the JS that runs on the user-agent. But user-agents like the browser can set those fields.

    You can change some of the sec-* headers inside a callback added to add_WebResourceRequested. Some fields like Sec-Fetch-Site get overwritten afterwards, others like Sec-Fetch-Mode can be set, but can't be deleted because they will get a default value, if not set after the WebResourceRequestedEvent. But you can change most of the sec-ch ones like this:

    NOTE: this code is only to demonstrate the approach, it's missing a bunch of error handling

    EventRegistrationToken webResourceRequestedToken;
    webviewWindow->AddWebResourceRequestedFilter(L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL);
    webviewWindow->add_WebResourceRequested(
      Callback<ICoreWebView2WebResourceRequestedEventHandler>([](ICoreWebView2* sender,
                                                                 ICoreWebView2WebResourceRequestedEventArgs* args) {
        COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext;
        args->get_ResourceContext(&resourceContext);
        ICoreWebView2WebResourceRequest* req = nullptr;
        ICoreWebView2HttpRequestHeaders* headers = nullptr;
        ICoreWebView2HttpHeadersCollectionIterator* iter = nullptr;
        args->get_Request(&req);
        req->get_Headers(&headers);
        headers->GetIterator(&iter);
        BOOL hasCurrent = FALSE;
        iter->get_HasCurrentHeader(&hasCurrent);
        std::vector<std::wstring> headersToDelete;
        std::wstring secChPrefix = L"sec-ch";
        while (hasCurrent) {
          LPWSTR name = nullptr, value = nullptr;
          iter->GetCurrentHeader(&name, &value);
          if (secChPrefix.compare(0, secChPrefix.size(), name, secChPrefix.size()) == 0) {
            headersToDelete.push_back(name);
          }
          iter->MoveNext(&hasCurrent);
        }
        for (auto header : headersToDelete) {
          headers->RemoveHeader(header.c_str());
        }
        // Setting "Sec-Fetch-Site" will have no effect, will get overwritten afterwards
        headers->SetHeader(L"Sec-Fetch-Site", L"same-origin");
        // This will work, but removing this key will just make it take the default value
        headers->SetHeader(L"Sec-Fetch-Mode", L"same-origin");
    
        return S_OK;
      }).Get(),
      &webResourceRequestedToken);