Skip to content

Commit e85180b

Browse files
authored
Create AdditionalAllowedFrameAncestors.md
1 parent b6da5fb commit e85180b

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
Additional Allowed Frame Ancestors for iframes
2+
===
3+
4+
# Background
5+
Due to potential [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking) attack, a lot of sites only allow them to be hosted in certain trusted ancestor iframes and top page.
6+
However, there are application scenarios that require hosting these sites in the app's UI that is authored as the HTML page.
7+
`<webview>` HTML element was provided for these hosting scenarios in previous solutions like Electron and JavaScript UWP apps.
8+
For WebView2, we are providing a native API for these hosting scenarios.
9+
10+
# Conceptual pages (How To)
11+
12+
To host other sites in an trusted page
13+
- Listen to FrameNavigationStarting event of CoreWebView2 or NavigationStarting event of CoreWebView2Frame object.
14+
- Set AdditionalAllowedFrameAncestors property of the NavigationStartingEventArgs to a list of trusted origins that is hosting the site.
15+
16+
The list should normally only contain the origin of the current trusted top page.
17+
If you are hosting other sites through nested iframes and the origins of some of the iframes are different from the origin of the top page, the list should also include those origins.
18+
19+
You should only add an origin to the list if it is fully trusted. You should limit the usage of the API to the targetted iframes whenever possible.
20+
21+
# Examples
22+
## Win32 C++
23+
```cpp
24+
const std::wstring myTrustedSite = L"example.com/";
25+
const std::wstring siteToHost = L"www.microsoft.com/";
26+
27+
bool IsAppContentUri(const std::wstring& pageUrl)
28+
{
29+
// App specific logic to decide whether the page is fully trusted.
30+
wil::com_ptr<IUri> uri;
31+
CHECK_FAILURE(CreateUri(pageUrl.c_str(), Uri_CREATE_CANONICALIZE, 0, &uri));
32+
DWORD scheme = (DWORD)URL_SCHEME_INVALID;
33+
wil::unique_bstr host;
34+
CHECK_FAILURE(uri->GetScheme(&scheme));
35+
CHECK_FAILURE(uri->GetHost(&host));
36+
return (dwScheme == URL_SCHEME_HTTPS) && (host.get() == myTrustedSite);
37+
}
38+
39+
bool IsTargetSite(const std::wstring& siteUrl)
40+
{
41+
// App specific logic to decide whether the site is the one it wants to host.
42+
wil::com_ptr<IUri> uri;
43+
CHECK_FAILURE(CreateUri(siteUrl.c_str(), Uri_CREATE_CANONICALIZE, 0, &uri));
44+
DWORD scheme = (DWORD)URL_SCHEME_INVALID;
45+
wil::unique_bstr host;
46+
CHECK_FAILURE(uri->GetScheme(&scheme));
47+
CHECK_FAILURE(uri->GetHost(&host));
48+
return (dwScheme == URL_SCHEME_HTTPS) && (host.get() == siteToHost);
49+
}
50+
51+
void HandleHostedSites()
52+
{
53+
wil::com_ptr<ICoreWebView2_4> webview2_4 = m_webView.try_query<ICoreWebView2_4>();
54+
if (webview2_4)
55+
{
56+
CHECK_FAILURE(webview2_4->add_FrameCreated(
57+
Callback<ICoreWebView2FrameCreatedEventHandler>(
58+
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
59+
-> HRESULT {
60+
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
61+
CHECK_FAILURE(args->get_Frame(&webviewFrame));
62+
wil::unique_cotaskmem_string pageUrl;
63+
CHECK_FAILURE(m_webView->get_Source(&pageUrl));
64+
// IsAppContentUri verifies that pageUrl is app's content.
65+
if (IsAppContentUri(pageUrl.get()))
66+
{
67+
// We are on trusted pages. Now check whether it is the iframe we plan
68+
// to host other sites.
69+
const std::wstring siteHostingFrameName = L"my_site_hosting_frame";
70+
wil::unique_cotaskmem_string frameName;
71+
CHECK_FAILURE(webviewFrame->get_Name(&frameName));
72+
if (siteHostingFrameName == frameName.get())
73+
{
74+
wil::com_ptr<ICoreWebView2ExperimentalFrame> frameExperimental =
75+
webviewFrame.try_query<ICoreWebView2ExperimentalFrame>();
76+
if (frameExperimental)
77+
{
78+
frameExperimental->add_NavigationStarting(
79+
Microsoft::WRL::Callback<
80+
ICoreWebView2ExperimentalFrameNavigationStartingEventHandler>(
81+
[](
82+
ICoreWebView2Frame* sender,
83+
ICoreWebView2NavigationStartingEventArgs* args)
84+
-> HRESULT
85+
{
86+
wil::unique_cotaskmem_string navigationTargetUri;
87+
CHECK_FAILURE(args->get_Uri(&navigationTargetUri));
88+
wil::com_ptr<
89+
ICoreWebView2NavigationStartingEventArgs_2>
90+
nav_start_args;
91+
// If the feature is supported and it is the site we
92+
// want to host, then allow sites to be hosted by app's page.
93+
if (SUCCEEDED(args->QueryInterface(
94+
IID_PPV_ARGS(&nav_start_args))) &&
95+
IsTargetSite(navigationTargetUri.get()))
96+
{
97+
nav_start_args
98+
->put_AdditionalAllowedFrameAncestors(
99+
myTrustedSite);
100+
}
101+
return S_OK;
102+
})
103+
.Get(),
104+
nullptr);
105+
}
106+
}
107+
}
108+
return S_OK;
109+
})
110+
.Get(),
111+
nullptr));
112+
}
113+
}
114+
```
115+
## WinRT and .NET
116+
```c#
117+
const string myTrustedSite = "example.com/";
118+
const string siteToHost = "www.microsoft.com";
119+
private bool IsAppContentUri(string pageUrl)
120+
{
121+
// App specific logic to decide whether the page is fully trusted.
122+
auto uri = new Uri(pageUrl);
123+
return uri.SchemeName == "https" && uri.Host == myTrustedSite;
124+
}
125+
126+
private bool IsTargetSite(string uri)
127+
{
128+
// App specific logic to decide whether the site is the one it wants to host.
129+
auto uri = new Uri(pageUrl);
130+
return uri.SchemeName == "https" && uri.Host == siteToHost;
131+
}
132+
133+
private void CoreWebView2_FrameCreated(CoreWebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2FrameCreatedEventArgs args)
134+
{
135+
// my_site_hosting_frame is the name attribute on the iframe element that we used in the web page to host the site.
136+
const string siteHostingFrameName = "my_site_hosting_frame";
137+
138+
if (IsAppContentUri(sender.Source) && (args.Frame.Name == siteHostingFrameName))
139+
{
140+
args.Frame.NavigationStarting += (CoreWebView2Frame navStartingSender, CoreWebView2NavigationStartingEventArgs navStartingArgs) =>
141+
{
142+
if (IsTargetSite(navStartingArgs.Uri))
143+
{
144+
args.AdditionalAllowedFrameAncestors = myTrustedSite;
145+
}
146+
};
147+
}
148+
}
149+
```
150+
151+
# API Details
152+
## Win32 C++
153+
```
154+
interface ICoreWebView2NavigationStartingEventArgs_2 : ICoreWebView2NavigationStartingEventArgs
155+
{
156+
157+
/// Get additional allowed frame ancestors set by the host app.
158+
[propget] HRESULT AdditionalAllowedFrameAncestors([out, retval] LPWSTR* value);
159+
160+
/// The host may set this property to allow a frame to be hosted by certain additional ancestors besides what is allowed by
161+
/// http header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options)
162+
/// and [Content-Security-Policy frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors).
163+
/// If set, a frame ancestor is allowed if it is allowed by the additional allowed frame ancestoers or original http header from the site.
164+
/// Whether an ancestor is allowed by the additional allowed frame ancestoers is done the same way as if the site provided
165+
/// it as the source list of the Content-Security-Policy frame-ancestors directive.
166+
/// This property gives the app the ability to use iframe to host sites that otherwise
167+
/// could not be hosted in an iframe in trusted app pages.
168+
/// This could potentially subject the hosted sites to [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking)
169+
/// attack from the code running in the hosting web page. Therefore, you should only
170+
/// set this property with origins of fully trusted hosting page and any intermediate iframes.
171+
/// Whenever possible, you should use the list of specific origins of the top and intermediate
172+
/// frames instead of wildcard characters for this property.
173+
/// This API is to provide limited support for app scenarios that used to be supported by
174+
/// `<webview>` element in other solutions like JavaScript UWP apps and Electron.
175+
/// You should limit the usage of this property to trusted pages, and if possible, to specific iframe and
176+
/// specific navigation target url, by checking the `Source` of the WebView2, the `Name`
177+
/// of the ICoreWebView2Frame and `Uri` of the event args.
178+
/// This property is ignored for top level document navigation.
179+
///
180+
[propput] HRESULT AdditionalAllowedFrameAncestors([in] LPCWSTR value);
181+
182+
}
183+
```
184+
## WinRT and .NET
185+
```c#
186+
namespace Microsoft.Web.WebView2.Core
187+
{
188+
runtimeclass CoreWebView2NavigationStartingEventArgs
189+
{
190+
// ...
191+
192+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2NavigationStartingEventArgs_2")]
193+
{
194+
String AdditionalAllowedFrameAncestors { get; set; };
195+
}
196+
}
197+
}
198+
```

0 commit comments

Comments
 (0)