Skip to content

Commit 65399a2

Browse files
authored
Update AdditionalAllowedFrameAncestors.md
1 parent 6983aa4 commit 65399a2

1 file changed

Lines changed: 60 additions & 46 deletions

File tree

specs/AdditionalAllowedFrameAncestors.md

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,29 @@ Additional Allowed Frame Ancestors for iframes
22
===
33

44
# Background
5-
Due to potential [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking) attack, a lot of sites only allow themselves to be hosted in certain trusted ancestor iframes and pages. The main way to specify this ancestor requirement for sites are http header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) and [Content-Security-Policy frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors).
5+
Due to potential [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking) attack, a lot of sites only allow themselves to be embedded in certain trusted ancestor iframes and pages. The main way to specify this ancestor requirement for sites are http header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) and [Content-Security-Policy frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors).
66

7-
However, there are application scenarios that require hosting these sites in the app's UI that is authored as an HTML page.
8-
`<webview>` HTML element was provided for these hosting scenarios in previous solutions like Electron and JavaScript UWP apps.
7+
However, there are application scenarios that require embedding these sites in the app's UI that is authored as an HTML page.
8+
`<webview>` HTML element was provided for these embedding scenarios in previous solutions like Electron and JavaScript UWP apps.
99

10-
For WebView2, we are providing a native API for these hosting scenarios. Developers can use it to provide additional allowed frame ancestors as if the site sent these as part of the Content-Security-Policy frame-ancestors directive. The result is that an ancestor is allowed if it is allowed by the site's original policies or by this additional allowed frame ancestors.
10+
For WebView2, we are providing a native API for these embedding scenarios. Developers can use it to provide additional allowed frame ancestors as if the site sent these as part of the Content-Security-Policy frame-ancestors directive. The result is that an ancestor is allowed if it is allowed by the site's original policies or by this additional allowed frame ancestors.
1111

1212
# Conceptual pages (How To)
1313

14-
To host other sites in an trusted page with modified allowed frame ancestors
14+
To embed other sites in an trusted page with modified allowed frame ancestors
1515
- Listen to FrameNavigationStarting event of CoreWebView2.
16-
- Set AdditionalAllowedFrameAncestors property of the NavigationStartingEventArgs to a list additional allowed frame ancestors using the same syntax as [Content-Security-Policy frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors).
16+
- Set AdditionalAllowedFrameAncestors property of the NavigationStartingEventArgs to a list additional allowed frame ancestors using the same syntax for the source list of [Content-Security-Policy frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors). Basically, it is a space delimited list. All source syntax of Content-Security-Policy frame-ancestors directive are supported.
1717

1818
The list should normally only contain the origin of the top page.
19-
If you are hosting other sites through nested iframes and the origins of some of the intermediate iframes are different from the origin of the top page and those origins might not be allowed by the site's original policies, the list should also include those origins.
19+
If you are embedding other sites through nested iframes and the origins of some of the intermediate iframes are different from the origin of the top page and those origins might not be allowed by the site's original policies, the list should also include those origins. As an example, if you owns the content on https://example.com and https://www.example.com and uses them on top page and some intermediate iframes, you should set the list as "https://example.com https://www.example.com".
2020

21-
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 app scenarios.
21+
You should only add an origin to the list if it is fully trusted. When possible, you should try to limit the usage of the API to the targetted app scenarios. For example, we can set a specific name attribute to the iframe that is used to embed sites (something like `<iframe name="my_site_embedding_frame">`) and then detect the embedding scenario when the trusted page is navigated to and the embedding iframe is created.
2222

2323
# Examples
2424
## Win32 C++
2525
```cpp
2626
const std::wstring myTrustedSite = L"https://example.com/";
27-
const std::wstring siteToHost = L"https://www.microsoft.com/";
27+
const std::wstring siteToEmbed = L"https://www.microsoft.com/";
2828

2929
bool AreSitesSame(PCWSTR url1, PCWSTR url2)
3030
{
@@ -53,14 +53,19 @@ bool IsAppContentUri(PCWSTR pageUrl)
5353
return AreSitesSame(pageUrl, myTrustedSite);
5454
}
5555

56-
// App specific logic to decide whether a site is the one it wants to host.
56+
// App specific logic to decide whether a site is the one it wants to embed.
5757
bool IsTargetSite(PCWSTR siteUrl)
5858
{
59-
return AreSitesSame(siteUrl, siteToHost);
59+
return AreSitesSame(siteUrl, siteToEmbed);
6060
}
6161

62-
void MyApp::HandleHostedSites()
62+
void MyApp::HandleEmbeddedSites()
6363
{
64+
// Set up the event listeners. The code will take effect when the site embedding page is navigated to
65+
// and the embedding iframe navigates to the site that we want to embed.
66+
67+
// This part is trying to scope the API usage to the specific scenario where we are embedding a site.
68+
// The result is recorded in m_embeddingSite.
6469
CHECK_FAILURE(m_webview->add_FrameCreated(
6570
Callback<ICoreWebView2FrameCreatedEventHandler>(
6671
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args)
@@ -72,22 +77,24 @@ void MyApp::HandleHostedSites()
7277
if (IsAppContentUri(pageUrl.get()))
7378
{
7479
// We are on trusted pages. Now check whether it is the iframe we plan
75-
// to host other sites.
76-
const std::wstring siteHostingFrameName = L"my_site_hosting_frame";
80+
// to embed other sites.
81+
// We are know that on the page, we are using an
82+
// <iframe name="my_site_embedding_frame"> to embed other sites.
83+
const std::wstring siteEmbeddingFrameName = L"my_site_embedding_frame";
7784
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
7885
CHECK_FAILURE(args->get_Frame(&webviewFrame));
7986
wil::unique_cotaskmem_string frameName;
8087
CHECK_FAILURE(webviewFrame->get_Name(&frameName));
81-
if (siteHostingFrameName == frameName.get())
88+
if (siteEmbeddingFrameName == frameName.get())
8289
{
83-
// We are hosting sites.
84-
m_hostingSite = true;
90+
// We are embedding sites.
91+
m_embeddingSite = true;
8592
CHECK_FAILURE(webviewFrame->add_Destroyed(
8693
Microsoft::WRL::Callback<
8794
ICoreWebView2FrameDestroyedEventHandler>(
8895
[this](ICoreWebView2Frame* sender,
8996
IUnknown* args) -> HRESULT {
90-
m_hostingSite = false;
97+
m_embeddingSite = false;
9198
return S_OK;
9299
})
93100
.Get(),
@@ -99,25 +106,25 @@ void MyApp::HandleHostedSites()
99106
.Get(),
100107
nullptr));
101108
// Using FrameNavigationStarting event instead of NavigationStarting event of CoreWebViewFrame
102-
// to cover all possible nested iframes inside the hosted site as CoreWebViewFrame
109+
// to cover all possible nested iframes inside the embedded site as CoreWebViewFrame
103110
// object currently only support first level iframes in the top page.
104111
CHECK_FAILURE(m_webview->add_FrameNavigationStarting(
105112
Microsoft::WRL::Callback<ICoreWebView2NavigationStartingEventHandler>(
106113
[this](
107114
ICoreWebView2* sender,
108115
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
109116
{
110-
if (m_hostingSite)
117+
if (m_embeddingSite)
111118
{
112119
wil::unique_cotaskmem_string navigationTargetUri;
113120
CHECK_FAILURE(args->get_Uri(&navigationTargetUri));
114-
wil::com_ptr<
115-
ICoreWebView2NavigationStartingEventArgs_2>
116-
navigationStartArgs;
117-
if (SUCCEEDED(args->QueryInterface(
118-
IID_PPV_ARGS(&navigationStartArgs))) &&
119-
IsTargetSite(navigationTargetUri.get()))
121+
if (IsTargetSite(navigationTargetUri.get()))
120122
{
123+
wil::com_ptr<
124+
ICoreWebView2NavigationStartingEventArgs2>
125+
navigationStartArgs;
126+
if (SUCCEEDED(args->QueryInterface(
127+
IID_PPV_ARGS(&navigationStartArgs)))
121128
navigationStartArgs
122129
->put_AdditionalAllowedFrameAncestors(
123130
myTrustedSite);
@@ -132,7 +139,7 @@ void MyApp::HandleHostedSites()
132139
## WinRT and .NET
133140
```c#
134141
const string myTrustedSite = "https://example.com/";
135-
const string siteToHost = "https://www.microsoft.com";
142+
const string siteToEmbed = "https://www.microsoft.com";
136143
private bool AreSitesSame(string url1, string url2)
137144
{
138145
auto uri1 = new Uri(url1);
@@ -147,38 +154,42 @@ void MyApp::HandleHostedSites()
147154
148155
private bool IsTargetSite(string siteUrl)
149156
{
150-
// App specific logic to decide whether the site is the one it wants to host.
151-
return AreSitesSame(siteUrl, siteToHost);
157+
// App specific logic to decide whether the site is the one it wants to embed.
158+
return AreSitesSame(siteUrl, siteToEmbed);
152159
}
153160
161+
// This part is trying to scope the API usage to the specific scenario where we are embedding a site.
162+
// The result is recorded in m_embeddingSite.
154163
private void CoreWebView2_FrameCreated(CoreWebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2FrameCreatedEventArgs args)
155164
{
156-
// my_site_hosting_frame is the name attribute on the iframe element that we used in the web page to host the site.
157-
const string siteHostingFrameName = "my_site_hosting_frame";
158-
if (IsAppContentUri(sender.Source) && (args.Frame.Name == siteHostingFrameName))
165+
// We are know that our trusted page is using <iframe name="my_site_embedding_frame"> element to embed other sites.
166+
// We are embedding sites when we are on trusted pages and the embedding iframe is created.
167+
const string siteEmbeddingFrameName = "my_site_embedding_frame";
168+
if (IsAppContentUri(sender.Source) && (args.Frame.Name == siteEmbeddingFrameName))
159169
{
160-
m_hostingSite = true;
170+
m_embeddingSite = true;
161171
args.Frame.Destroyed += CoreWebView2_SiteHostingFrameDestroyed;
162172
}
163173
}
164-
165174
private void CoreWebView2_SiteHostingFrameDestroyed(CoreWebView2Frame sender, Object args)
166175
{
167-
m_hostingSite = false;
176+
m_embeddingSite = false;
168177
}
169178
170179
// Using FrameNavigationStarting event instead of NavigationStarting event of CoreWebViewFrame
171-
// to cover all possible nested iframes inside the hosted site as CoreWebViewFrame
180+
// to cover all possible nested iframes inside the embedded site as CoreWebViewFrame
172181
// object currently only support first level iframes in the top page.
173182
private void CoreWebView2_FrameNavigationStarting(CoreWebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs args)
174183
{
175-
if (IsTargetSite(args.Uri))
184+
if (m_embeddingSite && IsTargetSite(args.Uri))
176185
{
177186
args.AdditionalAllowedFrameAncestors = myTrustedSite;
178187
}
179188
}
180-
private void HandleHostedSites()
189+
private void HandleEmbeddedSites()
181190
{
191+
// Set up the event listeners. The code will take effect when the site embedding page is navigated to
192+
// and the embedding iframe navigates to the site that we want to embed.
182193
webView.FrameCreated += CoreWebView2_FrameCreated;
183194
webView.FrameNavigationStarting += CoreWebView2_FrameNavigationStarting;
184195
}
@@ -190,20 +201,23 @@ void MyApp::HandleHostedSites()
190201
interface ICoreWebView2NavigationStartingEventArgs_2 : ICoreWebView2NavigationStartingEventArgs
191202
{
192203
193-
/// Get additional allowed frame ancestors set by the host app.
204+
/// Get additional allowed frame ancestors set by the app.
194205
[propget] HRESULT AdditionalAllowedFrameAncestors([out, retval] LPWSTR* value);
195206
196-
/// The host may set this property to allow a frame to be hosted by certain additional ancestors besides what is allowed by
207+
/// The app may set this property to allow a frame to be embedded by certain additional ancestors besides what is allowed by
197208
/// http header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options)
198209
/// and [Content-Security-Policy frame-ancestors directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors).
199210
/// If set, a frame ancestor is allowed if it is allowed by the additional allowed frame ancestoers or original http header from the site.
200211
/// Whether an ancestor is allowed by the additional allowed frame ancestoers is done the same way as if the site provided
201212
/// it as the source list of the Content-Security-Policy frame-ancestors directive.
202-
/// This property gives the app the ability to use iframe to host sites that otherwise
203-
/// could not be hosted in an iframe in trusted app pages.
204-
/// This could potentially subject the hosted sites to [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking)
205-
/// attack from the code running in the hosting web page. Therefore, you should only
206-
/// set this property with origins of fully trusted hosting page and any intermediate iframes.
213+
/// For example, if https://example.com and https://www.example.com are the origins of the top
214+
/// page and intemediate iframes for a nested iframe that is embedding a site, and you fully trust
215+
/// those origins, you should set thus property to "https://example.com https://www.example.com".
216+
/// This property gives the app the ability to use iframe to embed sites that otherwise
217+
/// could not be embedded in an iframe in trusted app pages.
218+
/// This could potentially subject the embedded sites to [Clickjacking](https://en.wikipedia.org/wiki/Clickjacking)
219+
/// attack from the code running in the embedding web page. Therefore, you should only
220+
/// set this property with origins of fully trusted embedding page and any intermediate iframes.
207221
/// Whenever possible, you should use the list of specific origins of the top and intermediate
208222
/// frames instead of wildcard characters for this property.
209223
/// This API is to provide limited support for app scenarios that used to be supported by
@@ -225,7 +239,7 @@ namespace Microsoft.Web.WebView2.Core
225239
{
226240
// ...
227241
228-
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2NavigationStartingEventArgs_2")]
242+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2NavigationStartingEventArgs2")]
229243
{
230244
String AdditionalAllowedFrameAncestors { get; set; };
231245
}

0 commit comments

Comments
 (0)