In order to solve Cross Domain security woes of JavaScript, I am implementing the following method
On Domain [ abc.com ]
On domain abc.com I have a page called main_page.html. Its code is as follows -
<script>
function SendMsg(id)
{
frames["invisible_iframe"].location = "http://xyz.com/invisible_iframe.html#"+id;
}
</script>
<body>
<input type="button" id="Test" value="Call iFrame" onclick="SendMsg(this.id);">
<iframe src="ttp://xyz.com/visible_iframe.html" name="visible_iframe" height="250" width="500"></iframe>
<iframe name="invisible_iframe" height="0" width="0" style="display:none;"></iframe>
</body>
On Domain [ xyz.com ]
On domain xyz.com I have a page called visible_iframe.html. Its code is as follows -
<script>
function Hi()
{
alert("Hi there!");
}
</script>
<body>
<h1>Visible iFrame on xyz.com
<iframe name="d2_invisible_iframe" id="d2_invisible_iframe" class="iFrame" src="http://xyz.com/invisible_iframe.html" height="310" width="520"></iframe>
</body>
Now I want to access the function Hi() from invisible_iframe.html (which is on the same domain)
The code of invisible_iframe.html is as follows
<script>
var sActionText = "";
function CheckForMessages()
{
if(location.hash != sActionText)
{
sActionText = location.hash;
var sAction = "";
var oSplitActionText = sActionText.split("#");
sAction = oSplitActionText[1];
if (sAction == "Test")
{
parent.Hi();
}
}
}
setInterval(CheckForMessages, 200);
</script>
<body>
<h1>Invisible iFrame on xyz.com</h1>
</body>
I am using hidden iFrame in visible_iframe.html because I don't want the URL of visible_iframe.html to change.
Now I expect when the button on main_page.html is clicked, I'd get alert message. But that doesn't happen. In firefox it says - Permission denied to access property 'Hi'
The strange thing is when I put parent.Hi(); outside CheckForMessages() function, the Hi() function can be accessed and alert box is shown.
What should I do to resolve this ?
Why not using easyXDM? This library should make your life really easy and help to avoid the security limitations when dealling with cross domain issues. Specially if you have control over the two domains.
easyXDM is a Javascript library that enables you as a developer to
easily work around the limitation set in place by the Same Origin
Policy, in turn making it easy to communicate and expose javascript
API’s across domain boundaries.
[This is one of the best and easy to use APIs] available for Cross Domain Communication between web applications.
easyXDM is easy to use, light weight, flexible, writing good quality code etc. I strongly think if you are going to continue with cross domain scenario, then you should adapt a robust cross domain apis such as easyXDM.
[easyXDM vs PostMessage Transport?]
easyXDM will use the PostMessageTransport method if this feature is enabled by the browser such as (IE8+, Opera 9+, Firefox 3+, Safari 4+,Chrome 2+) on the other side it will use different transport methods for the un supported browsers such as (Firefox 1-2 - using the FrameElementTransport) other transport methods will be used as needed such as FlashTransport, NameTransport, and HashTransport).
This clearly makes easyXDM superior in terms on browser support specially the old browsers.
To demonstrate cross-domain access using easyXDM (Domain1 [abc.com] calls a method on a remote domain [xyz.com]):
*On Domain [ abc.com ] - main domain*
<script type="text/javascript">
/**
* Request the use of the JSON object
*/
easyXDM.DomHelper.requiresJSON("../json2.js");
</script>
<script type="text/javascript">
var remote;
window.onload = function(){
/**
* When the window is finished loading start setting up the interface
*/
remote = new easyXDM.Interface(/** The channel configuration */{
/**
* Register the url to hash.html, this must be an absolute path
* or a path relative to the root.
* #field
*/
local: "/hash.html",
/**
* Register the url to the remote interface
* #field
*/
remote: "http://YOUR.OTHER.DOMAIN/YOUR_APPLICATION/YourRemoteApplication.html",
/**
* Register the DOMElement that the generated IFrame should be inserted into
*/
container: document.getElementById("embedded")
}, /** The interface configuration */ {
remote: {
remoteApplicationMethod: {},
noOp: {
isVoid: true
}
},
local: {
alertMessage: {
method: function(msg){
alert(msg);
},
isVoid: true
}
}
},/**The onReady handler*/ function(){
/**
* Call a method on the other side
*/
remote.noOp();
});
}
function callRemoteApplicationMethod(Value1, Value2){
remote.remoteApplicationMethod(Value1, Value2, function(result){
alert("Results from remote application" + result);
});
}
</script>
In the body
<input type="button" onclick="callRemoteApplicationMethod(3,5)" value="call remoteApplicationMethod on remote domain"/>
Now on your remote domain side you need to define your remote client as follows
*On Domain [ xyz.com ] - Remote domain*
This should go into the page YOUR_APPLICATION/YourRemoteApplication.html
<script type="text/javascript">
/**
* Request the use of the JSON object
*/
easyXDM.DomHelper.requiresJSON("../json2.js");
</script>
<script type="text/javascript">
var channel, remote;
/**
* When the window is finished loading start setting up the channel
*/
window.onload = function(){
/**
* When the channel is ready we create the interface
*/
remote = new easyXDM.Interface(/** The channel configuration*/{}, /** The configuration */ {
remote: {
alertMessage: {
isVoid: true
}
},
local: {
remoteApplicationMethod: {
method: doSomething(value1, value2){
// do somethigs with values
return "i'm return value from remote domain";
}
},
noOp: {
isVoid: true,
method: function(){
alert("Method not returning any data");
}
}
}
});
}
</script>
I suppose there is no need to suport old browsers, right?
You can use window.postMessage in modern browsers to support cross-origin communication.
You won't believe the cause. On main_page.html (abc.com) you define two iframes but you don't close them (missing </iframe>). Now there are two cases:
Case 1:
iframes are independent so your main_page.html code should be
<iframe src="http://xyz.com/visible_iframe.html" ...></iframe>
<iframe src="http://xyz.com/invisible_iframe.html" ...></iframe>
and the javascript call from invisible_iframe.html should be
parent.frames["visible_iframe"].Hi();
Case 2:
iframes are parent and child so main_page.html code should be
<iframe src="http://xyz.com/visible_iframe.html" ...></iframe>
visible_iframe.html code sould include
<iframe src="http://xyz.com/invisible_iframe.html" ...></iframe>
and the javascript call from invisible_iframe.html should be
parent.Hi();
That's all. The choice is yours.
Assuming your onlcick works on your input (just a typo there): from invisible_frame, you should address visible_frame's Hi() with parent.frames['visible_frame'].Hi().
For now, parent.Hi() tries to access a Hi() located on abc.com's page (as it is the frame's parent), so you're poked with the hard stick of Same Origin Policy.
Hope this helps and works ;)
Try creating these three files:
http://abc.com/main.html:
<!DOCTYPE html>
<html>
<head>
<script>
function SendMsg(id) {
window.frames.invisible.location = 'http://xyz.com/invisible.html#' + id;
}
</script>
</head>
<body>
<input type="button" id="Test" value="Call iFrame" onclick="SendMsg(this.id);">
<iframe src="http://xyz.com/visible.html" name="visible" height="250" width="500"></iframe>
<iframe name="invisible" height="0" width="0" style="display:none;"></iframe>
</body>
</html>
http://xyz.com/visible.html:
<!DOCTYPE html>
<html>
<head>
<script>
function Hi() {
alert('Hi there!');
}
</script>
</head>
<body>
<h1>Visible on xyz.com</h1>
</body>
</html>
http://xyz.com/invisible.html:
<!DOCTYPE html>
<html>
<head>
<script>
var sActionText = "";
function CheckForMessages() {
if (location.hash != sActionText) {
sActionText = location.hash;
var sAction = "";
var oSplitActionText = sActionText.split("#");
sAction = oSplitActionText[1];
if (sAction == "Test") {
parent.frames.visible.Hi();
}
}
}
setInterval(CheckForMessages, 200);
</script>
</head>
<body>
<h1>Invisible on xyz.com</h1>
</body>
</html>
Notice that the frame structure is:
main
/ \
/ \
visible invisible
And so if you need to access visible from invisible, you need to go:
parent.frames.visible.Hi()
Moving forward, I suggest using easyXDM or the jQuery postMessage plugin for cross-domain communication instead of reinventing the wheel.
There is a reason it is not letting you access the function because of the same origin policy. It would cause real security issues if you could.
CORS (Cross-Origin Resource Sharing) or window.postMessage (ref: Docs on MDN or another simple postMessage example) are definitely worth investigating if you don't care about IE7 support; you could also use a postMessage shim for older IE browsers.
However, I'll stick with an iframe solution in an attempt to more precisely answer your question.
Unfortunately you should not be able to access the parent (window scope) properties/methods from a cross domain iframe. However you can access window scopes via iframes on the same domain ...at any depth. Thus, one solution is to leverage vertical iframe tunnels.
If you create three html documents:
Top level - abc.com
<script>
function sendMsg( id ) {
frames["invisible_iframe"].location = "//xyz.com/mid_level.html#" + id;
}
window.ACTIONS = {
Test: function() {
alert("hello");
// This action could also start an interaction with another
// visible iframe by setting a hash, etc
}
};
</script>
<input type="button" id="Test" value="Call iFrame" onclick="sendMsg(this.id);">
<iframe name="invisible_iframe" height="0" width="0" frameborder="0" style="visibility:hidden;" src="//xyz.com/mid_level.html"></iframe>
Mid Level - xyz.com
<script>
function sendMsg( id ) {
frames["invisible_iframe"].location = "//abc.com/low_level.html#" + id;
}
var sActionText = "";
function checkForMessages() {
if(location.hash != sActionText) {
sActionText = location.hash.replace(/^#/, "");
location.hash = "";
sendMsg(sActionText);
}
}
setInterval(checkForMessages, 20);
</script>
<iframe name="invisible_iframe" height="0" width="0" frameborder="0" style="visibility:hidden;" src="//abc.com/low_level.html"></iframe>
Low Level - abc.com
<script>
function runActionInTop( action ) {
try {
window.top.ACTIONS[action]();
} catch ( err ) {
// Bad action!
}
}
var sActionText = "";
function checkForMessages() {
if(location.hash != sActionText) {
sActionText = location.hash.replace(/^#/, "");
location.hash = "";
runActionInTop(sActionText);
}
}
setInterval(checkForMessages, 20);
</script>
These three iframes can communicate - the first and third can communicate directly and the second indirectly (via hash in this example).
I know this is a variation from what you were originally trying to show with the "visible iframe". I wanted to focus on illustrating the layering necessary for the cross-site communication. If you still need the visible iframe, you could add communication hooks at the top level and also send additional information (in the hash) back from the middle frame to the lowest frame (the lowest frame could then communicate the information directly back to the top page).
Yes, I know this is convoluted - that is one of the reasons why CORS and postMessage exist :)
I've also changed a few minor aspects of your original code that I think are worth pointing out:
Using // instead of http:// means that this system will work on either http or https.
Some browsers (I believe I first ran into this with Safari 3) may not actually load the iframe with display:none; setting the frameborder attribute and visibility:hidden will keep the iframe from being visible.
Some browsers will load about:blank (or a similar page) if you do not specify the iframe src attribute in the markup. On https pages, this can cause insecure content warnings to be thrown in IE.
An alternative, Proxying.
In server-side grab the external file and echo it back. Now it is on your domain so no issues.
for example in php
<?php
$external-site = file_get_contents('http://xyz.com/');
echo $external-site;
?>
I think I would delete any iframes and use jsonp maybe with a library.
1. No Cross-domain errors
2. You could get data as objects (easy to play with 'em
3. You could even get JS code that u could parse.
4. Would be even mobile ready ( iframes are last century solutions... )
But then well maybe you are not able to take away the iframes for any reasons, so I advice to use a ready-made library solution.
Goodluck
Related
I have created a login/signup system which submits values to a php file and connects the database to login. I'm concerned about the security of my login system from the client side, so security in the login.php and script.js. What are the things I need to take into consideration to make a secure login system. As in the security in the XMLHttpRequest or in the input fields of the client side.
login.php
<?php
session_start();
?>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LOGIN</title>
</head>
<body>
<?php
if (isset($_SESSION['user_id'])) {
header ("Location: index.html");
exit();
}
?>
<input id="inserted_id" type="text" placeholder="Account Name/e-mail" />
<p>
<input id="inserted_password" type="password" placeholder="Password" />
<p>
<button id="login_submit" type="submit">Login</button>
<script src="script.js"></script>
</body>
</html>
script.js:
document.getElementById("login_submit").onclick = function () {
var inserted_id = document.getElementById("inserted_id").value;
var inserted_password = document.getElementById("inserted_password").value;
http_request("web_includes/login.inc.php?inserted_id=" + inserted_id + "&inserted_password=" + inserted_password);
}
function http_request(url) {
var xml = new XMLHttpRequest();
xml.open("GET", url);
xml.onload = function () {
if (xml.responseText == "SUCCESS") {
window.open("index.html", "_self", "", false);
} else {
console.log("FAIL");
}
}
xml.send(null);
}
Unsure about XMLHttpRequest's, but research;
CSP: Content Security Policy, Which is specified
in the HTML Meta Tags.
..(to Block or negate Cross-site-Scripting aswell as other threats). ..................................https://www.youtube.com/watch?v=JbfNWg6JS4U
CORS: Cross Origin resource Sharing.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Sha256 & other versions.
Hashing+Salt+pepper are a similar concept. many videos on youtube about ................this....
Also Research. Nonce's (Not the Jail kind).
it stands for a Number which is used once,
Similar to a key. That only your website & Database will have access to or the full number atleast..
(Change XMLHttpRequest to XMLHttpsRequest with the S meaning secure.)
Not viable apparently. or Automatically chosen.
& Could possibly put your whole Login form inside of an Iframe Which you can then specify individual Security attributes on. through the 'Sandboxing' feature.
(Not sure this is necessary if you implement the others or good practice, but it might add an extra layer of frustration to any potential attacker.)
for the PHP side aswell as any Javascript files use RegEx functions to block any attempt to add code; Blocking;
{Z}()[]^></#..etc.....
Just thought of another thing that came to mind. There are websites such as File2hd where you can download websites entire js/css/php/json/ajax etc....
There is a way to block these requests but thats something you'd have to research... it is a very handy site though for analyzing & learning other peoples code...
.php file extension cannot generally be viewed or altered from client side,
unless they are very skilled.. But CSP is your best bet with hash+salt+pepper + RegEx...
A combination of any of the above should make it farely secure... Kind Regards Tao.
Hope this helps :)..
I'm having a bit of a weird problem with my javascript code...
It basically consists of a script that accesses the exif of a photo and then shows it on an HTML page, more specifically the latitude and longitude of it.
The idea is to then use both the latitude and longitude on a Google maps iframe to then show the location that photo was taken...
That's all working but, until now, I've been using a picture that's stored on the cloud to make the testing...
If I try to make it work with the same exact picture stored locally, no EXIF info will appear on the page...
(I've also tried with some of my own pictures that have exif info and it still doesn't work...)
Why does it seem like Exif-js only works with images stored on a server?
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>EXIF</title>
<style>
img{
width: 500px;
max-height: auto;
}
</style>
</head>
<body>
<!-- If I use this it works: -->
<img src="https://c1.staticflickr.com/5/4867/30883801817_bf122bc498_o.jpg" id="img1" />
<!-- If I use this it DOESN'T work: -->
<img src="image3.jpg" id="img1"/> <!-- IT'S THE SAME IMAGE AND IT DOES HAVE EXIF-->
<iframe id="mapa_google" src="" width="640" height="480"></iframe>
<h1>Latitude Exif</h1>
<p id="local_lat"></p>
<h1>Longitude Exif</h1>
<p id="local_lon"></p>
<h1>Latitude Final</h1>
<p id="local_lat_final"></p>
<h1>Longitude Final</h1>
<p id="local_lon_final"></p>
<script src="exif.js"></script>
<script>
var toDecimal = function (number) {
var d = Math.floor(number[0]);
var m = Math.floor(number[1]);
var s = ((number[1]%1)*60);
var dms= d+(m/60)+(s/3600);
return dms
};
window.onload=getExif;
function getExif() {
img1 = document.getElementById("img1");
EXIF.getData(img1, function() {
latitude = EXIF.getTag(this, "GPSLatitude");
longitude = EXIF.getTag(this, "GPSLongitude");
local_lat = document.getElementById("local_lat");
local_lon = document.getElementById("local_lon");
local_lat.innerHTML = `${latitude}`;
local_lon.innerHTML = `${longitude}`;
latitude_final = toDecimal(latitude);
local_lat_final = document.getElementById("local_lat_final");
local_lat_final.innerHTML = `${latitude_final}`;
longitude_final = toDecimal(longitude);
local_lon_final = document.getElementById("local_lon_final");
local_lon_final.innerHTML = `${longitude_final}`;
document.getElementById("mapa_google").src = "https://www.google.com/maps/embed/v1/place?key=AIzaSyDQSbRMCIv1gDsT2qRsY8HvLyZP11hte_Y&q="+latitude_final+"+"+longitude_final;
});
}
getExif();
</script>
</body>
</html>
This is a CORS problem.
Exif-js uses an ajax request to retrieve the image. Ajax calls require CORS access. Pages loaded via file:// URIs don't contain the headers that CORS depends on, and the same-origin policy for these files is implementation-dependent but the safest assumption is to treat every file as having its own unique origin -- which means that loading files without a webserver isn't a good enough way to test anything that involves XHR calls.
Similarly, the site you mentioned in comments as also having this problem is denying CORS access:
[Error] Origin null is not allowed by Access-Control-Allow-Origin.
[Error] XMLHttpRequest cannot load
https://myriad-online.com/images/forum/IMG_4692.jpg due to access
control checks.
as can be seen by watching the developer console (always a good idea when testing js code...) while running this code snippet:
function getExif() {
img1 = document.getElementById("img1");
EXIF.getData(img1, function() {
// won't be reached in this example
console.log(this);
});
}
getExif();
<script src="https://cdn.jsdelivr.net/npm/exif-js"></script>
<img src="http://myriad-online.com/images/forum/IMG_4692.jpg" id="img1" />
You won't be able to run exif-js on files that are hosted by sites which explicitly deny CORS access, but for local testing the simplest solution is going to be to spin up a local webserver and test your files via http:// instead of file:// URIs.
Here's a situation. My customers would be having their own web pages. On that page they might have an iFrame in which they can show a page located on my server. Outside the iFrame they would have simple buttons, which when clicked should execute javascript functions in iFrame.
So basically the code of customer's web page on customer's domain would be something like this
<input type="button" value="Say Hi" id="TestButton">
<iframe src="myserver.com/some_html_page.htm" width="800" height="550"></iframe>
And code of myserver.com/some_html_page.htm would be
$("#TestButton").click(function(){
alert("Hi");
});
I did my reserach and I am aware of the Browser Security issues, but I want to know is there any way to handle this, may be with json or something ?
As you can already tell (given the parent and child are on different domains), you definitely cannot reach up from the child iFrame into the parent to listen for events.
One way around this is to pass messages between the pages. This will require your clients to include additional javascript in their page as well as the iFrame which points to your server. This is supported in native javascript with postMessage, but including the library #Mark Price suggests will make your life much easier.
So here goes an example:
Clients Page:
...
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.postMessage.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#TestButton").click(function(){
jQuery.postMessage("say_hi", "myserver.com/some_html_page.htm");
});
});
</script>
</head>
<input type="button" value="Say Hi" id="TestButton">
<iframe src="myserver.com/some_html_page.htm"></iframe>
Code on myserver.com/some_html_page.htm:
...
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.postMessage.min.js"></script>
<script type="text/javascript">
// you will need to set this dynamically, perhaps by having your
// clients pass it into the URL of the iFrame,
// e.g. <iframe src="myserver.com/some_html_page.htm?source_url=..
var source_origin = "clients_page.com/index.html";
var messageHandler = function (data) {
// process 'data' to decide what action to take...
alert("Hi");
};
$.receiveMessage(messageHandler, source_origin);
</script>
</head>
Probably it would be nice to bundle the client code up into a single library that they could include, so your clients aren't burdened with writing their own javascript.
As a caveat, I wrote this code off the top of my head and it is likely be rife with typos. I have used this library before to accomplish similar goals, and I hope this answer is a useful jumping off point for you (along with the plugin documentation).
Let me know if I can clarify anything, and best of luck! :)
You could try this jquery plugin from Ben Alman, providing you can have the plugin running on both yours, and your clients servers - see the examples for ways to execute js cross domain :
http://benalman.com/code/projects/jquery-postmessage/docs/files/jquery-ba-postmessage-js.html
Lets consider if you have a function called test() which loads under Iframe, then you can access that test() function as below
document.getElementsByName("name of iframe")[0].contentWindow.functionName()
e.g.
document.getElementsByName("iframe1")[0].contentWindow.test()
One of the common patterns of doing cross-domain requests, is using JSONP.
I am trying to call a javascript function defined in a parent from a child window. I have two files like this:
Parent:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function foo () {
alert ("Hello from parent!");
}
function doStuff () {
var w = window.open("testa.html");
}
</script>
</head>
<body>
<input type="button" value="open" onClick="doStuff();" />
</body>
</html>
And child:
<html>
<head>
<title>Test A</title>
<script type="text/javascript">
function get() {
window.opener.foo();
}
</script>
</head>
<body>
<input type="button" value="Call Parent" onClick="get();" />
</body>
</html>
I can not, for the life of me, call the function foo from the child process. I thought this should be possible with the window.opener object, but I can not seem to make this work. Any suggestions?
Ensure you are accessing this via http:// so the Same origin policy passes and you can access opener from the child. It won't work if you're just using file://.
Answering Rahul's question:
Every browser can load pages from server or from local filesystem. To load file from local filesystem you should put to the browser the address like this file://[path], where [path] is the absolute path to the file in filesystem (including drive letter on Windows, see http://blogs.msdn.com/b/ie/archive/2006/12/06/file-uris-in-windows.aspx for details).
To load file from local HTTP server (if you have one) you should put to address something like this http://localhost:[port]/[path], where [port] is the port where your server is running (default is 80) and [path] is the path of the file relative to the server's document root folder. Document root folder depends on the server configuration.
So, as you see, the same local file can be loaded to the browser in two ways. There is however big difference between these two ways. In the first case the browser doesn't use HTTP protocol to load the file and therefore is missing many things necessary for different mechanisms to work properly. For example AJAX doesn't work with local files, as HTTP response status is not 200, etc.
In this particular example the browser security mechanism didn't get the origin information and was preventing from accessing the parent window.
I am using below mentioned script to get href of new opened window, but its not working
Browser FF 7, IE 9, WIN 7
Can someone please let me know what's wrong in the script
Thanks in Advance
Akhil
<html>
<head>
<script type="text/javascript">
function open_win()
{
var mywindow=window.open("http://thesun.co.uk");
var clbutton=document.createElement("button");
var par=document.getElementById("main");
clbutton.setAttribute("id","focus");
clbutton.setAttribute("value","CLICK");
clbutton.innerHTML="CLICK";
par.insertBefore(clbutton,null);
clbutton.onclick=function() {alertMsg(mywindow);}
}
function alertMsg(mywindow)
{
mywindow.focus();
var t=mywindow.location.href;
alert(t);
//mywindow.alert("testing");
mywindow.close();
}
</script>
</head>
<body onload="open_win()">
<p id="main"></p>
</body>
</html>
You can't get the URL of sites that are in a different domain or protocol as the script that you are running. It is due to cross-domain restrictions. Chrome gives you the following error:
Unsafe JavaScript attempt to access frame with URL http://www.thesun.co.uk/sol/homepage/ from frame with URL http://fiddle.jshell.net/_display/. Domains, protocols and ports must match.
I think that it might be because the variable is not set yet. I think you might need a callback to say that the dom is ready for the new window.