Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Access is Denied" on contentDocument in IE9 on same domain [duplicate]

Short/Generic Version:

I'm working on an application that (unfortunately, for other reasons), sets document.domain at the top of every page to a substring of the "true" domain: for subdomains like sub.app.local, document.domain = "app.local". I'm also creating an iframe dynamically and adding it to the page. The iframe loads a file residing on the same server as the parent page. Later on, some Javascript needs to access the contentDocument property of the iframe.

This is fine in modern browsers, but causes problems in IE9 due to this bug. (See the top answer for a good explanation of why this is.) AFAIK, every browser newer than IE9 automatically inherits document.domain for programmatically-created iframes, so this is IE9-specific. Because of some of the unique requirements of my scenario (tldr: the iframe src needs to change), the answer in the above post didn't work for me.

Longer/Application-Specific Version:

I'm using FineUploader in an application running on IIS, uploading files to S3. Everything's working just fine in modern browsers, but IE9 support is giving me trouble, even after following the docs (Supporting IE9 and older) to configure the iframeSupport.localBlankPagePath option. I'm a little stumped!

  • I've tried this on FineUploader 4.0.3 and also 5.0.3.
  • Everything works fine in IE11. If I switch Document mode to 9 in the F12 console, it breaks.
  • I don't have CORS support turned on, because I'm not loading anything from a different domain.
  • I have a dummy/blank file on the same domain as my page.
  • I can see (in the Network panel) the HTTP 303 response from AWS that includes the Location key, and a value that points to my blank document. If I paste the entire URL into the address bar, the page loads fine and is blank as expected.
  • I've tried adding X-Frame-Options SAMEORIGIN to the server's response headers, as suggested here, but it didn't help.

The error that I get in the console is:

[Fine Uploader 5.0.3] Error when attempting to access iframe during handling of upload response (Access is denied.
)
[Fine Uploader 5.0.3] Amazon likely rejected the upload request



UPDATE

I've determined that the reason it's not working out-of-the-box is because the application sets document.domain on page load, and it's different than (a subset of) location.host. FineUploader's 303 redirect mechanism to the blank page is working fine, but the document.domain of the iframe differs from the parent (due to IE9 not inheriting the property) and so, access denied.

The actual S3 upload is working. It's just the final step of verifying the upload that fails.

Here's my client-side code:

FineUploader.js

var fu = namespace('App.FineUploader');
fu.DocId;
fu.ClientDeployId;
fu.viewModel;
fu.defaultAttachmentEndpoint = "https://s3.amazonaws.com/App.UploadBucket";
fu.FineUploaderController = "FineUploaderDocAttachment";

fu.delete = function (documentAttatchmentID, attachmentID) {
    var data = documentAttatchmentID;
    $.ajax({
        type: "POST",
        url: App.BaseUrl + "/api/" + fu.FineUploaderController + "/delete",
        data: JSON.stringify(data),
        success: function () {
            $('#fineUploader' + attachmentID).fineUploader('reset');
            $('#fineUploader' + attachmentID).show();
            $('#aDownloadfineUploader' + attachmentID).html('');
            $('#aDownloadfineUploader' + attachmentID).hide();
            $('#aDeletefineUploader' + attachmentID).hide();
        },
        dataType: 'json',
        contentType: 'application/json'
    })
}

fu.lockAll = function () {
    $('.fineUploader').hide();
    $('a[id^="aDeletefineUploader"]').hide();
}

fu.init = function (sID) {
    $('#' + sID).fineUploaderS3({
        request: {
            endpoint: fu.defaultAttachmentEndpoint,
            accessKey: "[key]",
            params: {
                documentid: $('#' + sID).attr('documentid'),
                attachmentid: $('#' + sID).attr('attachmentid')
            }
        },
        signature: {
            endpoint: App.BaseUrl + "/api/" + fu.FineUploaderController + "/signtureHandler"
        },
        uploadSuccess: {
            endpoint: App.BaseUrl + "/api/" + fu.FineUploaderController + "/success"
        },
        iframeSupport: {
            localBlankPagePath: App.BaseUrl + "/Scripts/FineUploader/4.0.3/html/blank.html"
        },
        objectProperties: {
            key: function (fileId) {
                var keyRetrieval = new qq.Promise(),
                    filename = $('#' + sID).fineUploader("getName", fileId);
                var documentid = $('#' + sID).attr('documentid');
                var attachmentid = $('#' + sID).attr('attachmentid');
                var data = { name: filename, documentId: documentid, attachmentId: attachmentid }
                $.ajax({
                    type: "POST",
                    url: App.BaseUrl + "/api/" + fu.FineUploaderController + "/preUpload",
                    data: JSON.stringify(data),
                    success: null,
                    dataType: 'json',
                    contentType: 'application/json'
                }).done(function (data) {
                    keyRetrieval.success(data.key);
                }).fail(function () {
                    keyRetrieval.failure();
                });
                return keyRetrieval;
            }
        },
        validation: {
            itemLimit: 1
        },
        chunking: {
            enabled: true
        }
    }).on('error', function (event, id, name, errorReason, xhrOrXdr) {
        alert(qq.format("Error on file number {} - {}.  Reason: {}", id, name, errorReason));
    }).on('complete', function (event, id, name, response) {
        if (fu.FineUploaderController === "FineUploaderDocLibraryAttachment") {
            $('#' + sID).fineUploader('reset');
            App.Contracts.Create.ReloadImageLibraryList(true);
            App.Contracts.Create.HideLoader();
        } else {
            $('#aDownload' + sID).attr('href', response.url);
            $('#aDownload' + sID).html(response.name);
            $('#aDownload' + sID).show();
            $('#aDelete' + sID).show();
            $('#aDelete' + sID).attr('onclick', 'App.FineUploader.delete(' + response.daId + ',' + sID.replace('fineUploader', '') + ');');
            $('#' + sID).hide();
        }
    }).on('submitted', function () {
        if (fu.FineUploaderController === "FineUploaderDocLibraryAttachment") {
            App.Contracts.Create.ShowLoader();
        }
    });
}
like image 508
Nate Barbettini Avatar asked Jul 21 '14 19:07

Nate Barbettini


2 Answers

(If anyone stumbles across this answer outside of the context of FineUploader, this idea is what I based my solution on.)

To implement this, I made FineUploader's blank.html slightly non-blank:

<head>
<script type="text/javascript">
    // Provide a mechanism to override document.domain
    // inside the iframe via this url syntax: blank.html?[args]#domain.com
    if (location.hash.substring(1).length > 0)
        document.domain = location.hash.substring(1);
</script>
</head><body></body>

This gives me a way to feed the correct document.domain value from the parent page when the iframe is generated. Slight modification to the FineUploader configuration object:

$.fineUploaderS3({
[snip]
    iframeSupport: {
        localBlankPagePath: App.BaseUrl + "/Scripts/FineUploader/4.0.3/html/blank.html#" + document.domain
    },
[/snip]
}

This doesn't seem to interfere with the arguments that are prepended by AWS. We're still using FineUploader 4.0.3 in this application, but this should work with latest as well.

tl,dr; It works! Tested in IE11 Document mode and also native IE9.

like image 58
Nate Barbettini Avatar answered Nov 16 '22 11:11

Nate Barbettini


The error suggests that the page served by the iframe is indeed not the same domain as the page hosting the uploader. Either that, or you have some plug-in/extension causing trouble. According to the error, Fine Uploader is simply not able to access any content in the iframe, which happens when the domain of the iframe doesn't match the domain of the frame/page hosting the uploader.

like image 1
Ray Nicholus Avatar answered Nov 16 '22 10:11

Ray Nicholus