10

I have an MVC view that is supposed to be iFramed. And several instances of it might be iFramed in the same host page. In my view I have this:

@Html.AntiForgeryToken()

Which I use to try to make sure calls to a web api are only coming from this page. My host page might look something like this:

<iframe src="http://myserver.com/myview?someparameters=0000"></iframe>
<iframe src="http://myserver.com/myview?someparameters=0001"></iframe>
<iframe src="http://myserver.com/myview?someparameters=0002"></iframe>

In my view I grab the token and submit it in a header so that I can check it in my API:

var headers = actionContext.Request.Headers;
var headerToken = headers.Contains("__RequestVerificationToken") ? headers.GetValues("__RequestVerificationToken").FirstOrDefault() : null;
var cookie = headers.GetCookies("__RequestVerificationToken").FirstOrDefault()?["__RequestVerificationToken"]?.Value;

AntiForgery.Validate(cookie,headerToken);

The problem I'm running into is that in my host page, all three views are getting loaded in parallel. As a result they all get their own random token in a hidden field and try to set the cookie. But while there can be three separate independent hidden input tokens, there can be only one cookie. So of the three requests, two will fail and one will succeed. A reload of the page will have all three working again, presumably because they are all getting the same anti-forgery token at this point (because it belongs to the session - if I understand this correctly).

So how can I avoid this? How can I make sure they all get the same token?

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • You can avoid this by not using iframes. So the question is: Can you change the design? Do you need the iframes? And does each iframe contain a form? Because the anti forgery token will only be used to post information. –  Feb 18 '19 at 20:41
  • @RuardvanElburg - not really. The idea is that this is a widget type thing that our customers can drop on to their own pages. The trouble is that they want to drop several on the same page with their own content around it. The iframed page itself has to make some backend calls which are protected by checking the token, but maybe I'll have to rethink that. – Matt Burland Feb 18 '19 at 20:44
  • And a javascript solution? –  Feb 18 '19 at 20:48
  • 1
    @HoomanBahreini - the token isn't the problem. The cookie is. – Matt Burland Feb 21 '19 at 20:43
  • @HoomanBahreini - it's an http only cookie. It isn't accessible from javascript. And besides, checking if it's set already would still cause exactly the same race condition. In fact, that is exactly what happens internally `@Html.AntiForgeryToken()`. It checks if a cookie is already set, and if not, creates a new one and creates a hidden `input` tag in the html. The problem is that two requests come in at the same time and neither of them has a cookie. – Matt Burland Feb 21 '19 at 20:54
  • Couple of questions. 1. Are you an owner of host page? I mean is it the same asp.net project and you can edit the host page as you like? 2. Do host page and iframes have different domains? 3. You making ajax request from iframe, right? – Alexander Feb 22 '19 at 12:56
  • @Alexander 1) No, 2) Yes, 3) Yes – Matt Burland Feb 22 '19 at 13:50

2 Answers2

1

try using partialview. in each partial view you can use your iFrames and all partail views will be added to main .cshtml file having Html.AntiForgeryToken()

@Html.AntiForgeryToken()

<div>    
    @{Html.RenderPartial("_iFrame1");}
</ div>
<div>    
    @{Html.RenderPartial("_iFrame2");}
</ div>
<div>    
<div>
@{Html.RenderPartial("_iFrame3");}
</ div>
Laxmikant
  • 588
  • 4
  • 11
0

You need to create your AntiForgeryToken outside of the iFrames, and somehow pass it into the sub page.

In your outer page, pass the anti-forgery token to the sub-page as a query parameter

@{    
  var aft = @Html.AntiForgeryToken; 
}
<iframe src="http://myserver.com/myview?someparameters=0000&aft=@aft"></iframe>
<iframe src="http://myserver.com/myview?someparameters=0001&aft=@aft"></iframe>
<iframe src="http://myserver.com/myview?someparameters=0002&aft=@aft"></iframe>

Then, on the page that renders the sub-views, check if AFT is set, and if so use it, if not, use @Html.AntiForgeryToken()

AndrewP
  • 1,598
  • 13
  • 24