Tuesday, October 24, 2006

IE 7 and IFrame APIs Part 2

Microsoft was kind enough to look into the issue, and here is more information.

The "Navigate sub-frames across different domains" preference is designed to address this security report:
http://secunia.com/advisories/11978/

IE 7 has shipped with automatically turning this preference on.

There is a workaround to the issue: the two iframes that do the communication must both have a parent frame from their domain loaded.

So, the original setup I use in the XHR IFrame Proxy situation looks like the following. The dashes indicate the level of nestedness (Top Window contains IFrame 1, IFrame 1 contains IFrame 2):

Top window (Domain A)
--IFrame 1 (Domain A)
----IFrame 2 (Domain B)

Where IFrame 1 and IFrame 2 communicate by setting each other's fragment identifiers (location.hash in JavaScript). When the communication is done, IFrame 1 tells the top window what the result is.

What broke was IFrame 2 (on Domain B) setting IFrame 1's location (via parent.location).

The following iframe structure works in IE7:

Top window (Domain A)
-- IFrame W (Domain B)
---- IFrame 1 (Domain A)
------ IFrame 2 (Domain B)

Notice the introduction of IFrame W. Now, apparently since IFrame W (on Domain B) owns IFrame 1, it is OK for IFrame 2 to change the location of IFrame 1.

Here is a test of this structure (thanks in large part of Microsoft).

This test uses something I personally have not done before: using window.open() to grab named iframes. Interesting technique. I'm curious to see if it is required when I patch IFrame XHR for IE 7.

So, the end result is that *another* IFrame is needed to accomplish the task. I'll be looking at modifying XHR IFrame Proxy code to use this extra frame, but only for IE 7. The down side is that now, in addition to the remote server placing xip_server.html on their server, they will have to place another HTML file too (the file for IFrame W). If the code bloat is not too much, I might consider making xip_server.html smart enough to act as IFrame W content as well as being the content for IFrame 2.

So, while it works, I still ask Microsoft to consider the following:

1) It seems like this change to address the security issue (http://secunia.com/advisories/11978/) seems like it should not affect setting parent/child frames. Firefox seems to have addressed the security issue without breaking subframe communication. I know that is not entirely a fair comparison, but it seems like the code change in IE to protect against a specific issue has too broad of an effect.

2) Even with this preference on, allow changing of fragment identifiers. It is a non-destructive change to the parent frame, and the parent frame can choose to ignore it. It is also a very solid, useful tool for cross-domain communication that works for today's browsers.
If you want to get fancy, consider supporting a security setting on the iframe, that allows Domain A to say it trusts content from Domain B to change its location. Ideally this sort of configuration of an iframe would be some sort of standardized access control scheme that other browser makers could implement if they so desired.

Wednesday, October 11, 2006

IE 7 Breaks IFrame APIs that use parent.location

Update (Nov. 7, 2006): Microsoft provided more information. See Part 2.

Original post (Oct. 11, 2006):

One of my co-workers was recently testing IE 7 RC1 and found that using fragment identifier messaging (FIM) with IFrames did not work. When the content in the nested IFrame tried to set parent.location, IE 7 popped a new window instead. If you have an IFrame-based API that uses parent.location, then you might be affected by this change.

I would like Microsoft to reconsider the preference change in IE 7 that causes this issue, and this post describes why. Feel free to contact Microsoft if you feel likewise. More information below.

When I investigated the issue more, I found that IE 7 only popped a new window if there was a nested IFrame. If there was just an IFrame in the top level document/browser window, it did not pop a window.
We contacted Microsoft through our company's Microsoft channel, and found out the following:
  • There is a preference that was introduced in Windows XP SP2 that controls this behavior. You can find it by looking in the Internet Settings -> Security Tab -> Click on Custom Level button -> "Navigate sub-frames across different domains".
  • With IE 6, this is set to "Enable" by default.
  • When IE 7 installs, IE 7 automatically sets the preference to "Disable" by default.
I would prefer that they do not change the preference for the following reasons:
  1. There is no documentation about the change, and the change was done without checking with the web developer community.
  2. I think the behavior is buggy and could use refinement.
  3. Fragment identifier messaging (FIM) across IFrames is the best thing we have for allowing pure browser, cross-domain web APIs.
If you feel the same, or if you feel that your own IFrame-based web API that sets parent.location might be affected, then send feedback to Microsoft. I will try to find out where we can send feedback and update this post when I have the info.

More information on why I think IE 7 should keep the preference as "Enabled":

No public documentation of discussion

Our Microsoft contact did not provide any public info indicating this was documented or discussed publicly. When I asked for a description of the change, there was no documentation that was ready for public consumption yet. There might have been some public disclosure about it, but I am not aware of any. I apologize if there was something posted. I have been subscribed to the IEBlog for a while now, but none of the previous posts triggered any warning bells for me. But I could very well have missed it.

This page describes that security preference, but reading does not seem to be very helpful (and seems to discuss talking to sub-frames and not parent frames).

I do not mean to chastise Microsoft on this point. I can appreciate with such a large project as a browser, it might not always be evident what changes can have an adverse effect on people using your product. And even if they did note it somewhere, there are lots of changes, and it could very easily get lost. It has happened to me on my software projects.

I can also see that their answer could be "why don't you just test with the IE 7 betas and release candidates? If there is an issue, let us know". That is a fair and valid position to take. But just as it might be hard to document and message all the changes for a large product like IE 7, it can be hard for web developers to stay on top of everything. I can see many not taking IE 7 testing seriously until RC 1.

And given that the parent.location works as long as you are communicating with a browser window and not an IFrame, I can see for some developers using IFrame APIs, there might not appear to be an issue.

For my case, with the XHR IFrame Proxy, I just worked out that solution July 31st. I haven't done IE 7 testing because I have been busy, and I thought the big things for that release were CSS-related changes (from a web developer POV). There did not seem to be that many JavaScript changes (besides an apparent increase in the JS engine performance). But still, I feel bad getting around to this issue this late in the IE 7 cycle.

So, ideally both of us could have caught the issue sooner and talked about it, but it is understandable that it did not happen. And I hope that since it is a preference change, that changing the IE 7 behavior back to "Enable" is hopefully an easy thing to do even this late in the IE 7 cycle. This would give more time to discuss the issue.

Behavior is buggy and could use refinement

I am sure there is a good use case to justify having this preference, and perhaps it even describes the current behavior. However, from my outsider's point of view, it seems buggy.

Why is Test Case 1 allowed (setting parent.location on the top browser window), but setting the parent.location on an IFrame is not allowed? They both seem to be "cross frame" communication, and violate whatever security issue the preference tries to address.

In addition to addressing the apparent bug with the behavior, I think setting parent.location should be allowed if the new value for parent.location is the same protocol, domain, port and path, but just differ by fragment identifier. In those cases, the parent document is not destroyed or modified, so perhaps whatever security issue is being addressed with this preference does not apply? Allowing fragment identifier changes is important for the next point...

FIM is the best thing we have for allowing pure browser, cross-domain web APIs

There are other ways to do cross-domain communication in a browser, but they all suffer from some limitation that does not make them a good general purpose, pure browser solution.

I also believe Fragement Identifier Messaging (FIM) that is used as part of the XHR IFrame Proxy is the most secure cross-domain communication option, since it requires explicit action for a server to allow the cross-domain action, and it has very fine grain control on the types
of requests that are allowed. FIM also does not automatically "execute" the response as using dynamic script tags might. The receiving frame can choose to just ignore messages if it wants.

And with the exception of the current behavior in IE 7 RC 1, FIM works in all other (major) browsers: IE 6, Firefox 1.5 and 2.0, Safari 2.0, and Opera 9.

The real solution would be a native XMLHttpRequest that can do cross-domain requests, but even when that becomes available, it will take a couple of years at the least to be widespread enough that it is actually be usable by web developers for cross-browser solutions.

In Conclusion

Microsoft, please do not change the preference to "Disable" with IE 7. Keep the default "Enable" behavior of IE 6. If at some point you want to change the preference to "Disable" by default, consider a public discourse of the implementation issues discussed here and the impact on FIM.