Vulnerability in EPiServer.Forms
Hope you are staying safe.
I'll provide some background to what I'm trying to do, just incase I'm focusing on solving the wrong problem!
We recently implemented a caching strategy with the help of EPiServer's own Johan Antila. It's a full page cache based on David Knipe's caching with Visitor Groups, in which Johan modified the solution to also work with A/B testing. And it's awesome, we saw our TTFB drop from around 1 - 2s down to 200 - 500ms when the cached version of a page is requested. We are super happy, but then we discovered we had overlooked Forms! (how could we!?!) Antiforgery tokens and caching don't mix :/
I quickly modified the implementation to not cache a page if there was a form present, but a significant portion of our pages have a form on and it's ruining the good vibes. On top of that, we know that this is only a temporary solution until we can work out how to handle forms. Maybe some kind of donut hole caching mixed in with what we have? Or forms in an iframe?
My journey so far:
Given how close we are with the caching implementation, I've shied away from any donuts and I guess I fear that I'll end up going down that road eventually. But not before I've exhausted the iframe possibility.
We like a thank you page! When users complete a form they are generally taken to a thank you page and we use that hit for conversion tracking etc. But no one wants to see the thank you page loaded just inside the iframe... I can of course write some js to use the MutationObserver api to detect when the iframe changes url and use that to redirect the parent page of the iframe, but this of course results in two hits to the thank you page and that just won't do.
For seemingly obvious security reasons, you can't use a window's onbeforeload event to grab the intended destination page and stop the iframe from requesting the thank you page before redirecting the parent page.
I thought perhaps I could hook into FormSubmissionFinalized event from Forms and somehow see the final destination url there, but I can't even see that firing even though the submission data in the CMS shows as finalized. But given the data submission stuff is all handled on the server, that was probablly a silly idea in the first place.
So I began wondering about custom actors. Could I write a custom actor which would optionally overwrite the redirecturl functionality of the forms and somehow hook that into the iframe to post a message to the parent and force the parent page to redirect instead?
So finally to my questions. Is that possible? How would that even work?
I've been looking at the decompiled code from Forms and from Forms.Demo and I can't quite see yet how that is implemented. I'm guessing that there'll be some code doing something like RedirectToAction(RedirectUrl? + FormSessionData) and I'd need an actor to overwrite that method. But then what? a custom action method which passes the data to a script I can then use in the iframe to assign window.top.location.href ?? I'm not really sure.
Am I barking up the completely wrong tree? Is the approach I am trying to take just not possible?
If anyone can help, I would be so so so grateful!
All the best,Alex
... it's setting target="_parent" to the <form> isn't it...? no it's not. not setting it to _top either. lol once again, I thought I had it.
I also found where RedirectToUrl is in forms. It's buried deep in EPiServer.Forms.Core.Internal.DataSubmissionService redirectUrl used as argumenment for this.BuildReturnResultsForSubmitAction() so without forking EPiServer.Forms and rewriting the internals I don't see how I'm going to do that ha ha ha even if I could!!!
First of all thanks for participating in the form and sharing your solution - great to hear the solution works for you (forms aside...).
As for solving the forms challenge you could make the "thank you" page thats redirected to in the iframe simply be an empty/dumb page that does nothing but break out of the frame and redirect to the actual thank you page using some JS? Something site site.com/thankyou.html?redirect?/actual-thank-you-page/ where the JS reads the redirect parameter, breaks out of the frame and redirects to /actual-thank-you-page/
This could solve your challenge and avoid double tracking?