DOM Exception 18 in Awesomium - angularjs

I have a webpage being loaded from the local file system and rendered using awesomium, the page is using AngularJSto render part of the page. However, I have a problem with part of my AngularJS controller generating a Dom Exception 18:
angular-1.0.0rc10.js # line 5349 Error: SECURITY_ERR: DOM Exception 18
It seems this exception is caused by the presence of this code at the end of my AngularJS controller:
$http({method: 'GET', url: 'http://placeholdermetaserver.appspot.com/list?format=json&game=Heist'})
.success(function(data)
{
//Do stuff with data
});
Oddly enough everything is just fine if I use a straight XMLHttpRequest instead of the AngularJS $http object:
var request = new XMLHttpRequest();
request.onload = function() {
//Do stuff with data
};
request.open("GET", "http://placeholdermetaserver.appspot.com/list?format=json&game=Heist", true);
This exception is not generated when I simply load this page in chrome (off the local file system, same as awesomium).
What could cause this and how can I fix it?

The $http service includes some Cross Site Request Forgery (XSRF) countermeasures. I'm not especially familiar with Awesomium, so I'm not sure what security features it implements, but I'd consult the documentation (both of Awesomium and AngularJS) for more info.
http://docs.angularjs.org/api/angular.module.ng.$http
From the perspective of your server, this is prone to the textbook XSRF img tag attack if you ever send a GET request like:
"http://myapp.com/doSomething/somegame/12345"
From the perspective of your client, let's say you make a request like:
"http://myapp.com/doSomething/somegame/" + someId
A clever hacker might coax someId to be:
"#123.45.56.689/myEvilFakeJson.json"
In which case the request isn't made to your server, but instead some
other one. If you hard code the location of the request or are careful with sanitizing input, it probably won't be that much of a risk.

Related

Blocked a frame with origin "https://example.com" from accessing a frame with origin "https://www.herokucdn.com". Protocols, domains, and ports [duplicate]

I am loading an <iframe> in my HTML page and trying to access the elements within it using JavaScript, but when I try to execute my code, I get the following error:
SecurityError: Blocked a frame with origin "http://www.example.com" from accessing a cross-origin frame.
How can I access the elements in the frame?
I am using this code for testing, but in vain:
$(document).ready(function() {
var iframeWindow = document.getElementById("my-iframe-id").contentWindow;
iframeWindow.addEventListener("load", function() {
var doc = iframe.contentDocument || iframe.contentWindow.document;
var target = doc.getElementById("my-target-id");
target.innerHTML = "Found it!";
});
});
Same-origin policy
You can't access an <iframe> with different origin using JavaScript, it would be a huge security flaw if you could do it. For the same-origin policy browsers block scripts trying to access a frame with a different origin.
Origin is considered different if at least one of the following parts of the address isn't maintained:
protocol://hostname:port/...
Protocol, hostname and port must be the same of your domain if you want to access a frame.
NOTE: Internet Explorer is known to not strictly follow this rule, see here for details.
Examples
Here's what would happen trying to access the following URLs from http://www.example.com/home/index.html
URL RESULT
http://www.example.com/home/other.html -> Success
http://www.example.com/dir/inner/another.php -> Success
http://www.example.com:80 -> Success (default port for HTTP)
http://www.example.com:2251 -> Failure: different port
http://data.example.com/dir/other.html -> Failure: different hostname
https://www.example.com/home/index.html:80 -> Failure: different protocol
ftp://www.example.com:21 -> Failure: different protocol & port
https://google.com/search?q=james+bond -> Failure: different protocol, port & hostname
Workaround
Even though same-origin policy blocks scripts from accessing the content of sites with a different origin, if you own both the pages, you can work around this problem using window.postMessage and its relative message event to send messages between the two pages, like this:
In your main page:
const frame = document.getElementById('your-frame-id');
frame.contentWindow.postMessage(/*any variable or object here*/, 'https://your-second-site.example');
The second argument to postMessage() can be '*' to indicate no preference about the origin of the destination. A target origin should always be provided when possible, to avoid disclosing the data you send to any other site.
In your <iframe> (contained in the main page):
window.addEventListener('message', event => {
// IMPORTANT: check the origin of the data!
if (event.origin === 'https://your-first-site.example') {
// The data was sent from your site.
// Data sent with postMessage is stored in event.data:
console.log(event.data);
} else {
// The data was NOT sent from your site!
// Be careful! Do not use it. This else branch is
// here just for clarity, you usually shouldn't need it.
return;
}
});
This method can be applied in both directions, creating a listener in the main page too, and receiving responses from the frame. The same logic can also be implemented in pop-ups and basically any new window generated by the main page (e.g. using window.open()) as well, without any difference.
Disabling same-origin policy in your browser
There already are some good answers about this topic (I just found them googling), so, for the browsers where this is possible, I'll link the relative answer. However, please remember that disabling the same-origin policy will only affect your browser. Also, running a browser with same-origin security settings disabled grants any website access to cross-origin resources, so it's very unsafe and should NEVER be done if you do not know exactly what you are doing (e.g. development purposes).
Google Chrome
Mozilla Firefox
Safari
Opera: same as Chrome
Microsoft Edge: same as Chrome
Brave: same as Chrome
Microsoft Edge (old non-Chromium version): not possible
Microsoft Internet Explorer
Complementing Marco Bonelli's answer: the best current way of interacting between frames/iframes is using window.postMessage, supported by all browsers
Check the domain's web server for http://www.example.com configuration for X-Frame-Options
It is a security feature designed to prevent clickJacking attacks,
How Does clickJacking work?
The evil page looks exactly like the victim page.
Then it tricked users to enter their username and password.
Technically the evil has an iframe with the source to the victim page.
<html>
<iframe src='victim-domain.example'/>
<input id="username" type="text" style="display: none;"/>
<input id="password" type="text" style="display: none;"/>
<script>
//some JS code that click jacking the user username and input from inside the iframe...
<script/>
<html>
How the security feature work
If you want to prevent web server request to be rendered within an iframe add the x-frame-options
X-Frame-Options DENY
The options are:
SAMEORIGIN: allow only to my own domain render my HTML inside an iframe.
DENY: do not allow my HTML to be rendered inside any iframe
ALLOW-FROM https://example.com/: allow specific domain to render my HTML inside an iframe
This is IIS config example:
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
The solution to the question
If the web server activated the security feature it may cause a client-side SecurityError as it should.
For me i wanted to implement a 2-way handshake, meaning:
- the parent window will load faster then the iframe
- the iframe should talk to the parent window as soon as its ready
- the parent is ready to receive the iframe message and replay
this code is used to set white label in the iframe using [CSS custom property]
code:
iframe
$(function() {
window.onload = function() {
// create listener
function receiveMessage(e) {
document.documentElement.style.setProperty('--header_bg', e.data.wl.header_bg);
document.documentElement.style.setProperty('--header_text', e.data.wl.header_text);
document.documentElement.style.setProperty('--button_bg', e.data.wl.button_bg);
//alert(e.data.data.header_bg);
}
window.addEventListener('message', receiveMessage);
// call parent
parent.postMessage("GetWhiteLabel","*");
}
});
parent
$(function() {
// create listener
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
// replay to child (iframe)
document.getElementById('wrapper-iframe').contentWindow.postMessage(
{
event_id: 'white_label_message',
wl: {
header_bg: $('#Header').css('background-color'),
header_text: $('#Header .HoverMenu a').css('color'),
button_bg: $('#Header .HoverMenu a').css('background-color')
}
},
'*'
);
}, false);
});
naturally you can limit the origins and the text, this is easy-to-work-with code
i found this examlpe to be helpful:
[Cross-Domain Messaging With postMessage]
There is a workaround, actually, for specific scenarios.
If you have two processes running on the same domain but different ports, the two Windows can interact without limitations. (i.e. localhost:3000 & localhost:2000). To make this work, each window needs to change their domain to the shared origin:
document.domain = 'localhost'
This also works in the scenario that you are working with different subdomains on the same second-level domain, i.e. you are on john.site.example trying to access peter.site.example or just site.example
document.domain = 'site.example'
By explicitily setting document.domain; the browser will ignore the hostname difference and the Windows can be treated as coming from the 'same-origin'. Now, in a parent window, you can reach into the iframe: frame.contentWindow.document.body.classList.add('happyDev')
If you have control over the content of the iframe - that is, if it is merely loaded in a cross-origin setup such as on Amazon Mechanical Turk - you can circumvent this problem with the <body onload='my_func(my_arg)'> attribute for the inner html.
For example, for the inner html, use the this html parameter (yes - this is defined and it refers to the parent window of the inner body element):
<body onload='changeForm(this)'>
In the inner html :
function changeForm(window) {
console.log('inner window loaded: do whatever you want with the inner html');
window.document.getElementById('mturk_form').style.display = 'none';
</script>
I experienced this error when trying to embed an iframe and then opening the site with Brave. The error went away when I changed to "Shields Down" for the site in question. Obviously, this is not a full solution, since anyone else visiting the site with Brave will run into the same issue. To actually resolve it I would need to do one of the other things listed on this page. But at least I now know where the problem lies.
I would like to add Java Spring specific configuration that can effect on this.
In Web site or Gateway application there is a contentSecurityPolicy setting
in Spring you can find implementation of WebSecurityConfigurerAdapter sub class
contentSecurityPolicy("
script-src 'self' [URLDomain]/scripts ;
style-src 'self' [URLDomain]/styles;
frame-src 'self' [URLDomain]/frameUrl...
...
.referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
Browser will be blocked if you have not define safe external contenet here.
Open the start menu
Type windows+R or open "Run
Execute the following command.
chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security

Spring + Angular / IE gets 403 on PUT (others don't)

I have a spring webapp with spring security(3.2.3, so no CSRF protection) and angular.
In a controller i have a method like this one to update the users pw:
#RequestMapping("/accountinfo/password", method = arrayOf(RequestMethod.PUT))
#ResponseBody
#Secured("ROLE_USER")
open fun updateOwnPassword(user: User, #RequestBody password: String) {
val editedUser = user
editedUser.password = encoder.encode(password)
userRepository.save(editedUser)
}
The request is done via angular Service:
function changeOwnPassword(newPassword) {
return $http
.put('accountinfo/password', newPassword)
.then(function (response) {
return response.data
});
}
This works fine in every browser i tested with. Except if using IE 11.0.35 in a Citrix environment (Works outside of it,but can't see any specific configuration).
In that case i get 403 on the Request. When i change the method to POST it works fine again. I could do that for every function where i got this problem of course, but that doesn't seem like a clean solution.
As far as my research goes, i think it's something wrong with the way the browser writes the Request, but that's were i can't find out what to do.
EDIT:
I compared the request headers of both IE 11.0.35 inside and outside of Citrix and they seem exactly the same. The only difference is that the working version uses DNT=1 and the non-working version as WOW64 in the User-Agent attributes?
UPDATE:
I found out that it happens with DELETE too
Found the problem: The client sends the Requests through an additional Proxy that doesn't like PUT and DELETE and just cuts the session cookies off of it. We are adressing that problem with putting the tokens in the header in the future.

AngularJS: Mock specific $http calls [duplicate]

Due to some infrastructure changes (namely servers & VPNs) there are times I want to run our application in an offline mode. I've been able to implement this with ngMockE2E however it seems to be an all or nothing approach, meaning you have to explicitly set every single HTTP request out of the app.
Is there a way to have it assume that unless you have explicitly set a route/url to be handled that it will automatically call a generic passThrough() operation?
Currently I am doing this:
noSrvc = $location.search().hasOwnProperty 'nosrvc'
#
# templates
#
$httpBackend.whenGET /(\.htm|\.html)$/
.passThrough();
#
# session
#
rqst = $httpBackend.whenGET /(api\/users\/current)$/
if !noSrvc then rqst.passThrough() else rqst.respond {"user":{
# doing something similar for every single service call in the app... gets tedious after about 3-4
Most everything I've read on the subject deals with unit testing and doesn't really address the implied passthrough unless otherwise stated.
That's the recipe I've used for whitelisting
app.run(function ($httpBackend) {
// mocked requests, should come first
$httpBackend.when('GET', 'mock').respond(200, {});
// whitelisted real requests, should come last
angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD', 'PUT', 'POST', 'PATCH'], function (method) {
$httpBackend.when(method).passThrough();
});
});
And I'm quite sure that precedence matters here.

Is there an easy way to whitelist HTTP requests with ngMockE2E

Due to some infrastructure changes (namely servers & VPNs) there are times I want to run our application in an offline mode. I've been able to implement this with ngMockE2E however it seems to be an all or nothing approach, meaning you have to explicitly set every single HTTP request out of the app.
Is there a way to have it assume that unless you have explicitly set a route/url to be handled that it will automatically call a generic passThrough() operation?
Currently I am doing this:
noSrvc = $location.search().hasOwnProperty 'nosrvc'
#
# templates
#
$httpBackend.whenGET /(\.htm|\.html)$/
.passThrough();
#
# session
#
rqst = $httpBackend.whenGET /(api\/users\/current)$/
if !noSrvc then rqst.passThrough() else rqst.respond {"user":{
# doing something similar for every single service call in the app... gets tedious after about 3-4
Most everything I've read on the subject deals with unit testing and doesn't really address the implied passthrough unless otherwise stated.
That's the recipe I've used for whitelisting
app.run(function ($httpBackend) {
// mocked requests, should come first
$httpBackend.when('GET', 'mock').respond(200, {});
// whitelisted real requests, should come last
angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD', 'PUT', 'POST', 'PATCH'], function (method) {
$httpBackend.when(method).passThrough();
});
});
And I'm quite sure that precedence matters here.

Is it possible to upload files to S3 from browser in IE8?

Now I have this code in javascript.
var file_object = $('#PHOTO').get(0).files[0];
the_form = new FormData();
the_form.append("AWSAccessKeyId", "TESTING");
the_form.append("acl", "authenticated-read");
the_form.append("policy", policy);
the_form.append("signature", signature);
the_form.append("Content-Type", "image/jpeg");
the_form.append("key", "test.jpg");
the_form.append("file", file_object);
$.ajax({
url: "http://S3BUCKET.s3.amazonaws.com",
type: "POST",
data: the_form,
processData: false,
contentType: false
})
It works sweetly, in Chrome, Firefox, except IE6,7,8,9.
The reason is that file object is not supported until IE10!
https://developer.mozilla.org/en-US/docs/Web/API/File
Is there any work-around solution for browsers before IE10?
PS: Code example would be nice!!
Without Flash many things are definitely a no-go. I believe the lib you reference has some Flash fallbacks, but I'm unclear as to whether they can handle all the issues involved. This is something I'm currently dealing with myself, and here are the issues in brief:
Content-Type header in response. IE (without Flash intermediary) will try to download a JSON content type, no way around this that I know without a proxy middleman to fudge headers.
hostname mapping. If you don't map to origin hostname, IE iframe (which is the non-Flash fallback) will not allow you to read the contents of it from the containing window. Fire and forget may be possible, but consuming the response/detecting errors from s3 may not.
I will update this answer as I uncover more in the coming days. This is a large project so we have some pretty significant requirements and I imagine I'll learn a lot in the next week or so.
This is covered in a lot more detail here (not my company/project/post): http://blog.fineuploader.com/2013/08/16/fine-uploader-s3-upload-directly-to-amazon-s3-from-your-browser/

Resources