This commit is contained in:
Ilya Kantor 2017-05-25 11:56:32 +03:00
parent 787d58a83f
commit 455d300d8d
280 changed files with 2 additions and 2 deletions

View file

@ -0,0 +1,193 @@
# Popups and window methods
A popup window is one of the oldest methods to show additional document to user.
Basically, you just run:
```js
window.open('http://javascript.info/')
```
... And it will open a new window with given URL. Most modern browsers are configured to open new tabs instead of separate windows.
[cut]
## Popup blocking
Popups exist from really ancient times. The initial idea was to show another content without closing the main window. As of now, there are other ways to do that: JavaScript is able to send requests for server, so popups are rarely used. But sometimes they are still handy.
In the past evil sites abused popups a lot. A bad page could open tons of popup windows with ads. So now most browsers try to block popups and protect the user.
**Most browsers block popups if they are called outside of user-triggered event handlers like `onclick`.**
If you think about it, that's a bit tricky. If the code is directly in an `onclick` handler, then that's easy. But what is the popup opens in `setTimeout`?
Try this code:
```js run
// open after 3 seconds
setTimeout(() => window.open('http://google.com'), 3000);
```
The popup opens in Chrome, but gets blocked in Firefox.
...And this works in Firefox too:
```js run
// open after 1 seconds
setTimeout(() => window.open('http://google.com'), 1000);
```
The difference is that Firefox treats a timeout of 2000ms or less are acceptable, but after it -- removes the "trust", assuming that now it's "outside of the user action". So the first one is blocked, and the second one is not.
## Modern usage
As of now, we have many methods to load and show data on-page with JavaScript. But there are still situations when a popup works best.
For instance, many shops use online chats for consulting people. A visitor clicks on the button, it runs `window.open` and opens the popup with the chat.
Why a popup is good here, why not in-page?
1. A popup is a separate window with its own independent JavaScript environment. So a chat service doesn't need to integrate with scripts of the main shop site.
2. A popup is very simple to attach to a site, little to no overhead. It's only a small button, without additional scripts.
3. A popup may persist even if the user left the page. For example, a consult advices the user to visit the page of a new "Super-Cooler" goodie. The user goes there in the main window without leaving the chat.
## window.open
The syntax to open a popup is: `window.open(url, name, params)`:
url
: An URL to load into the new window.
name
: A name of the new window. Each window has a `window.name`, and here we can specify which window to use for the popup. If there's already a window with such name -- the given URL opens in it, otherwise a new window is opened.
params
: The configuration string for the new window. It contains settings, delimited by a comma. There must be no spaces in params, for instance: `width:200,height=100`.
Settings for `params`:
- Position:
- `left/top` (numeric) -- coordinates of the window top-left corner on the screen. There is a limitation: a new window cannot be positioned offscreen.
- `width/height` (numeric) -- width and height of a new window. There is a limit on minimal width/height, so it's impossible to create an invisible window.
- Window features:
- `menubar` (yes/no) -- shows or hides the browser menu on the new window.
- `toolbar` (yes/no) -- shows or hides the browser navigation bar (back, forward, reload etc) on the new window.
- `location` (yes/no) -- shows or hides the URL field in the new window. FF and IE don't allow to hide it by default.
- `status` (yes/no) -- shows or hides the status bar. Again, most browsers force it to show.
- `resizable` (yes/no) -- allows to disable the resize for the new window. Not recommended.
- `scrollbars` (yes/no) -- allows to disable the scrollbars for the new window. Not recommended.
There is also a number of less supported browser-specific features, which are usually not used. Check <a href="https://developer.mozilla.org/en/DOM/window.open">window.open in MDN</a> for examples.
## Example: a minimalistic window
Let's open a window with minimal set of features just to see which of them browser allows to disable:
```js run
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=0,height=0,left=-1000,top=-1000`;
open('/', 'test', params);
```
Here most "window features" are disabled and window is positioned offscreen. Run it and see what really happens. Most browsers "fix" odd things like zero `width/height` and offscreen `left/top`. For instance, Chrome open such a window with full width/height, so that it occupies the full screen.
Let's add normal positioning options and reasonable `width`, `height`, `left`, `top` coordinates:
```js run
let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=600,height=300,left=100,top=100`;
open('/', 'test', params);
```
Most browsers show the example above as required.
Rules for omitted settings:
- If there is no 3rd argument in the `open` call, or it is empty, then the default window parameters are used.
- If there is a string of params, but some yes/no features are omitted, then the omitted features are disabled, if the browser allows that. So if you specify params, make sure you explicitly set all required features to yes.
- If there is no `left/top` in params, then the browser tries to open a new window near the last opened window.
- If there is no `width/height`, then the new window will be the same size as the last opened.
## Accessing a popup
The `open` call returns a reference to the new window. It can be used to manipulate it's properties, change location and even more.
In the example below, the contents of the new window is modified after loading.
```js run
let newWindow = open('/', 'example', 'width=300,height=300')
newWindow.focus();
newWindow.onload = function() {
let html = `<div style="font-size:30px">Welcome!</div>`;
*!*
newWindow.document.body.insertAdjacentHTML('afterbegin', html);
*/!*
};
```
Please note that external `document` content is only accessible for windows from the same origin (the same protocol://domain:port).
For windows with URLs from another sites, we are able to change the location by assigning `newWindow.location=...`, but we can't read the location or access the content. That's for user safety, so that an evil page can't open a popup with `http://gmail.com` and read the data. We'll talk more about it later.
## Accessing the opener window
A popup may access the "opener" window as well. A JavaScript in it may use `window.opener` to access the window that opened it. It is `null` for all windows except popups.
So both the main window and the popup have a reference to each other. Thay may modify each other freely assuming that they come from the same origin. If that's not so, then there are still means to communicate, to be covered in the next chapter <info:cross-window-communication>.
## Closing a popup
If we don't need a popup any more, we can call `newWindow.close()` on it.
Technically, the `close()` method is available for any `window`, but `window.close()` is ignored by most browsers if `window` is not created with `window.open()`.
The `newWindow.closed` is `true` if the window is closed. That's useful to check if the popup (or the main window) is still open or not. A user could close it, and our code should take that possibility into account.
This code loads and then closes the window:
```js run
let newWindow = open('/', 'example', 'width=300,height=300')
newWindow.onload = function() {
newWindow.close();
alert(newWindow.closed); // true
};
```
## Focus/blur on a popup
Theoretically, there are `window.focus()` and `window.blur()` methods to focus/unfocus on a window. Also there are `focus/blur` events that allow to focus a window and catch the moment when the visitor switches elsewhere.
In the past evil pages abused those. For instance, look at this code:
```js run
window.onblur = () => window.focus();
```
When a user attempts to switch out of the window (`blur`), it brings it back to focus. The intention is to "lock" the user within the `window`.
So, there are limitations that forbid the code like that. There are many limitations to protect the user from ads and evils pages. They depend on the browser.
For instance, a mobile browser usually ignores that call completely. Also focusing doesn't work when a popup opens in a separate tab rather than a new window.
Still, there are some things that can be done.
For instance:
- When we open a popup, it's might be a good idea to run a `newWindow.focus()` on it. Just in case, for some OS/browser combinations it ensures that the user is in the new window now.
- If we want to track when a visitor actually uses our web-app, we can track `window.onfocus/onblur`. That allows us to suspend/resume in-page activities, animations etc. But please note that the `blur` event means that the visitor switched out from the window, but he still may observe it. The window is in the background, but still may be visible.
## Summary
- A popup can be opened by the `open(url, name, params)` call. It returns the reference to the newly opened window.
- By default, browsers block `open` calls from the code outside of user actions. Usually a notification appears, so that a user may allow them.
- The popup may access the opener window using the `window.opener` property, so the two are connected.
- If the main window and the popup come from the same origin, they can freely read and modify each other. Otherwise, they can change location of each other and communicate using messages (to be covered).
- To close the popup: use `close()` call. Also the user may close them (just like any other windows). The `window.closed` is `true` after that.
- Methods `focus()` and `blur()` allow to focus/unfocus a window. Sometimes.
- Events `focus` and `blur` allow to track switching in and out of the window. But please note that a window may still be visible even in the background state, after `blur`.
Also if we open a popup, a good practice is to notify the user about it. An icon with the opening window can help the visitor to survive the focus shift and keep both windows in mind.

View file

@ -0,0 +1,346 @@
# Cross-window communication
The "Same Origin" (same site) policy limits access of windows and frame to each other.
The idea is that if we have two windows open: one from `john-smith.com`, and another one is `gmail.com`, then we wouldn't want a script from `john-smith.com` to read our mail.
[cut]
## Same Origin [#same-origin]
Two URLs are said to have the "same origin" if they have the same protocol, domain and port.
These URLs have the same origin:
- `http://site.com`
- `http://site.com/`
- `http://site.com/my/page.html`
These ones are not:
- `http://www.site.com` (another domain: `www.` matters)
- `http://site.org` (another domain: `.org` matters)
- `https://site.com` (another protocol: `https`)
- `http://site.com:8080` (another port: `8080`)
If we have a reference to another window (a popup or iframe), and that window comes from the same origin, then we can do everything with it.
Otherwise, we can only change its location. Please note: not *read*, but modify it, redirect it to another place. That's possible, because such action does not reveal any data. Also such windows windows may exchange messages. Soon about that later.
````warn header="Exclusion: subdomains may be same-origin"
There's an important exclusion in the same-origin policy.
If windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com`, and assign to `document.domain` their common second-level domain `site.com`, then limitations are removed.
In other words, all such documents (including the one from `site.com`) should have the code:
```js
document.domain = 'site.com';
```
Then they can interact without limitations.
````
## Managing iframes
An `<iframe>` is a two-faced beast. From one side it's a tag, just like `<script>` or `<img>`. From another side it's a window-in-window.
The embedded window has a separate `window` and `document` objects, scripts and so on.
We can access them:
- `iframe.contentWindow` is a reference to the window inside the `<iframe>`.
- `iframe.contentDocument` is a reference to the document inside it.
When we access an embedded window, the browser checks if the iframe has the same origin. If that's not so then the access to almost everything is denied.
For instance:
```html run
<iframe src="https://example.com" id="iframe"></iframe>
<script>
iframe.onload = function() {
// can get the reference to the inner window
let iframeWindow = iframe.contentWindow;
try {
// ...but not to the document inside it
let doc = iframe.contentDocument;
} catch(e) {
alert(e); // Security Error
}
// also can't read iframe.contentWindow.location:
try {
alert(iframe.contentWindow.location);
} catch(e) {
alert(e); // Security Error
}
// ...but can modify it!
iframe.contentWindow.location = '/'; // works
iframe.onload = null; // run this code only once
};
</script>
```
The code above shows errors for any operations except:
- Getting the reference to the inner window `iframe.contentWindow`
- Changing its `location`.
```smart header="`iframe.onload` vs `iframe.contentWindow.onload`"
The `iframe.onload` event is actually the same as `iframe.contentWindow.onload`. It triggers when the embedded window fully loads with all resources.
...But `iframe.onload` is always available, while `iframe.contentWindow.onload` needs the same origin.
```
And here's an example with the same origin:
```html run
<iframe src="/" id="iframe"></iframe>
<script>
iframe.onload = function() {
// just do anything
iframe.contentDocument.body.prepend("Hello, world!");
};
</script>
```
### Wait until the iframe loads
When an iframe is created, it immediately has a document. But that document is different from the one that finally loads into it!
Here, look:
```html run
<iframe src="/" id="iframe"></iframe>
<script>
let oldDoc = iframe.contentDocument;
iframe.onload = function() {
let newDoc = iframe.contentDocument;
*!*
alert(oldDoc == newDoc); // false
*/!*
};
</script>
```
That's actually a well-known pitfall for novice developers. We shouldn't work with the document immediately, because that's the *wrong document*. If we set any event handlers on it, they will be ignored.
...But the `onload` event triggers when the whole iframe with all resources is loaded. What if we want to act sooner, on `DOMContentLoaded`?
We can try to catch the moment when a new document appears, and then setup necessary handlers, like this:
```html run
<iframe src="/" id="iframe"></iframe>
<script>
let oldDoc = iframe.contentDocument;
// every 100 ms check if the document is the new one
let timer = setInterval(() => {
if (iframe.contentDocument == oldDoc) return;
// yeah, now set handlers and do whatever we want
clearInterval(timer);
iframe.contentDocument.addEventListener('DOMContentLoaded', () => {
iframe.contentDocument.body.prepend('Hello, world!');
});
}, 100);
</script>
```
Let me know in comments if you know a better solution here.
### window.frames
An alternative way to get a window object for `<iframe>` -- is to get it from the named collection `window.frames`:
- By number: `window.frames[0]` -- the window object for the first frame in the document.
- By name: `window.frames.iframeName` -- the window object for the frame with `name="iframeName"`.
For instance:
```html run
<iframe src="/" style="height:80px" name="win" id="iframe"></iframe>
<script>
alert(iframe.contentWindow == frames[0]); // true
alert(iframe.contentWindow == frames.win); // true
</script>
```
An iframe may have other iframes inside. The corresponding `window` objects form a hierarchy.
Navigation links are:
- `window.frames` -- the collection of "children" windows (for nested frames).
- `window.parent` -- the reference to the "parent" (outer) window.
- `window.top` -- the reference to the topmost parent window.
For instance:
```js run
window.frames[0].parent === window; // true
```
We can use the `top` property to check if the current document is open inside a frame or not:
```js run
if (window == top) { // current window == window.top?
alert('The script is in the topmost window, not in a frame');
} else {
alert('The script runs in a frame!');
}
```
### The sandbox attribute
The `sandbox` attribute allows to run untrusted content inside an `<iframe>`. It "sandboxes" the iframe by treating it as coming from another origin and forbidding certain actions.
There are many restrictions. By default, for `<iframe sandbox src="...">` all of them are applied. But if we specify them in a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`, then they are lifted.
In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift:
`allow-same-origin`
: By default `"sandbox"` forces the browser to treat the `iframe` as coming from another origin, even if it's `src` points to the same site. This option removes that feature.
`allow-top-navigation`
: Allows the `iframe` to change `parent.location`.
`allow-forms`
: Allows to submit forms from `iframe`.
`allow-scripts`
: Allows to run scripts from the `iframe`.
`allow-popups`
: Allows to `window.open` popups from the `iframe`
See [the manual](mdn:/HTML/Element/iframe) for more.
The example below demonstrates a sandboxed iframe with some JavaScript and a form. Neither one works:
[codetabs src="sandbox" height=140]
```smart
The purpose of the `"sandbox"` attribute is to add restrictions. It cannot remove them. In particular, it can't relax same-origin restrictions if the iframe comes from another origin.
```
## Cross-window messaging
The `postMessage` interface allows windows to talk to each other no matter which origin they are from.
It has two parts.
### postMessage
The window that wants to send a message calls [postMessage](mdn:api/Window.postMessage) method of the receiving window. In other words, if we want to send the message to `win`, we should call `win.postMessage(data, targetOrigin)`.
Arguments:
`data`
: The data to send. Can be any object, the data is cloned using the "structured cloning algorithm". IE supports only strings, so we can `JSON.stringify` complex objects.
`targetOrigin`
: Allow only a window from the given origin to get the message.
The `targetOrigin` is a safety measure. If the target window comes from another origin, we can't read it's `location`. We can't be sure which site is open in it right now, the user could navigate away.
Specifying `targetOrigin` ensures that the window only receives the data if it's still at that site. Good when the data is secure.
If we don't want that check, we can set `targetOrigin` to `*`.
For instance:
```html no-beautify
<iframe src="http://example.com" name="example">
<script>
let win = window.frames.example;
win.postMessage("message", "http://example.com");
// win.postMessage("message", "*");
</script>
```
### onmessage
To receive a message, the window should have a handler on the `message` event.
The event object has special properties:
`data`
: The data from `postMessage`
`origin`
: The origin of the sender, for instance `http://javascript.info`.
`source`
: The reference to the sender window. We can immediately `postMessage` back if we want.
To assign that handler, we should use `addEventListener`, a short syntax `window.onmessage` does not work.
Here's an example:
```js
window.addEventListener("message", function(event) {
if (event.origin != 'http://javascript.info') {
// something from an unknown domain, let's ignore it
return;
}
alert( "received: " + event.data );
});
```
The full example:
[codetabs src="postmessage" height=120]
```smart header="There's no delay"
There's totally no delay between `postMessage` and the `message` event. That happens synchronously, even faster than `setTimeout(...,0)`.
```
## Summary
To call methods and access the content of another window, we should first have a reference to it.
For popups:
- `window.open` -- opens a new window and returns a reference to it,
- `window.opener` -- a reference to the opener window from a popup
For iframes:
- `window.frames` is a collection of nested window objects,
- `window.parent`, `window.top` are the references to parent and top windows,
- `iframe.contentWindow` is the window inside an `<iframe>` tag.
Then if they come from the same origin (host, port, protocol), then windows can do whatever they want with each other.
Otherwise, only possible actions are:
- Change the location of another window (write-only access).
- Post a message to it.
Exclusions are:
- Windows share the same main domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into "same origin".
- If an iframe has a `sandbox` attribute, it is forcefully put into the "different origin" state, unless the `allow-same-origin` is specified in the attribute value. That can be used to run untrusted code in iframes from the same site.
The `postMessage` interface allows two windows to talk with security checks:
1. The sender calls `targetWin.postMessage(data, targetOrigin)`.
2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the URL from `targetWin` site.
3. If it is so, then `targetWin` triggers the `message` event with special properties:
- `origin` -- the origin of the sender window (like `http://my.site.com`)
- `source` -- the reference to the sender window.
- `data` -- the data, any object in everywhere except IE that supports only strings.
We should use `addEventListener` to set the handler for this event inside the target window.

View file

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
Receiving iframe.
<script>
window.addEventListener('message', function(event) {
alert(`Received ${event.data} from ${event.origin}`);
});
</script>
</body>
</html>

View file

@ -0,0 +1,25 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<form id="form">
<input type="text" placeholder="Enter message" name="message">
<input type="submit" value="Click to send">
</form>
<iframe src="iframe.html" id="iframe" style="display:block;height:60px"></iframe>
<script>
form.onsubmit = function() {
iframe.contentWindow.postMessage(this.message.value, '*');
return false;
};
</script>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div>The iframe below is has <code>sandbox</code> attribute.</div>
<iframe sandbox src="sandboxed.html" style="height:60px;width:90%"></iframe>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button onclick="alert(123)">Click to run a script (doesn't work)</button>
<form action="http://google.com">
<input type="text">
<input type="submit" value="Submit (doesn't work)">
</form>
</body>
</html>

View file

@ -0,0 +1,200 @@
# The clickjacking attack
The "clickjacking" attack allows an evil page to click on a "victim site" *on behalf of the visitor*.
Many sites were hacked this way, including Twitter, Facebook, Paypal and other sites. They are all fixed, of course.
[cut]
## The idea
The idea is very simple.
Here's how clickjacking was done with Facebook:
1. A visitor is lured to the evil page. No matter how.
2. The page has a harmlessly-looking link on it (like "get rich now" or "click here, very funny" and so on).
3. Over that link the evil page positions a transparent `<iframe>` with `src` from facebook.com, in such a way that the "Like" button is right above that link. Usually that's done with `z-index`.
4. Clicking on that link, the visitor in fact presses that button.
## The demo
Here's how the evil page looks like. To make things clear, the `<iframe>` is half-transparent (in real evil pages it's fully transparent):
```html run height=120 no-beautify
<style>
iframe { /* iframe from the victim site */
width: 400px;
height: 100px;
position: absolute;
top:0; left:-20px;
*!*
opacity: 0.5; /* in real opacity:0 */
*/!*
z-index: 1;
}
</style>
<div>Click to get rich now:</div>
<!-- The url from the victim site -->
*!*
<iframe src="facebook.html"></iframe>
<button>Click here!</button>
*/!*
<div>...And you're cool (I'm a cool hacker actually)!</div>
```
The full demo of the attack:
[codetabs src="clickjacking-visible" height=160]
Here we have a half-transparent `<iframe src="facebook.html">`, and in the example we can see it hovering over the button. A click on the button actually clicks on the iframe, but that's not visible to the user, because the iframe is transparent.
As a result if the visitor is authorized on facebook ("remember me" is usually turned on), then it adds a "Like". On Twitter that would be a "Follow" button.
Here's the same example, but closer to reality, with `opacity:0` for `<iframe>`:
[codetabs src="clickjacking" height=160]
All we need to attack -- is to position the `<iframe>` on the evil page in such a way that the button is right over the link. That's usually possible with CSS.
```smart header="Clickjacking is for clicks, not for keyboard"
The attack only affects mouse actions.
Technically, if we have a text field to hack, then we can position an iframe in such a way that text fields overlap each other. So when a visitor tries to focus on the input he sees on the page, he actually focuses on the input inside the iframe.
But then there's a problem. Everything that the visitor types will be hidden, because the iframe is not visible.
So that would look really odd to the user, and he will stop.
```
## Old-school defences (weak)
The oldest defence method is the piece of JavaScript that forbids to open the page in a frame (so-called "framebusting").
Like this:
```js
if (top != window) {
top.location = window.location;
}
```
That is: if the window finds out that it's not on the top, then it automatically makes itself the top.
As of now, that's not reliable, because there are many ways to hack around it. Let's cover a few.
### Blocking top-navigation
We can block the transition caused by changing `top.location` in the [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) event.
The top page (that belongs to the hacker) sets a handler to it, and when the `iframe` tries to change `top.location` the visitor gets a message asking him whether he wants to leave.
Like this:
```js
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Want to leave without learning all the secrets (he-he)?";
};
```
In most cases the visitor would answer negatively, because he doesn't know about the iframe, all he can see is the top page, there's no reason to leave. And so the `top.location` won't change!
In action:
[codetabs src="top-location"]
### Sandbox attribute
One of the things restricted by the `sandbox` attribute is navigation. A sandboxed iframe may not change `top.location`.
So we can add the iframe with `sandbox="allow-scripts allow-forms"`. That would relax the restrictions allowing scripts and forms. But we don't put `allow-top-navigation` in the value so that the navigation is still forbidden. And the change of `top.location` won't work.
Here's the code:
```html
<iframe *!*sandbox="allow-scripts allow-forms"*/!* src="facebook.html"></iframe>
```
There are other ways to work around that simple protection too.
## X-Frame-Options
Server-side header `X-Frame-Options` can allow or forbid showing the page inside a frame.
It must be sent by the server: browser ignore it if found in `<meta>` tags. So `<meta http-equiv="X-Frame-Options"...>` won't do anything.
The header may have 3 values:
`DENY`
: Never ever show the page inside an iframe.
`SAMEORIGIN`
: Allow to show inside an iframe if the parent document comes from the same origin.
`ALLOW-FROM domain`
: Allows to show inside an iframe if the parent document is from the given domain.
For instance, Twitter uses `X-Frame-Options: SAMEORIGIN`. Here's the result:
```html
<iframe src="https://twitter.com"></iframe>
```
<iframe src="https://twitter.com"></iframe>
Depending on the browser, `iframe` above is either empty or it has a message telling that "the browser can't show it".
## Showing with disabled functionality
The protecting `X-Frame-Options` header has a side-effect. Other sites can't show our page in an `iframe`, even if they have "legal" reasons to do so.
So there are other solutions. For instance, we can "cover" the page with a `<div>` with `height:100%;width:100%`, so that it handles all clicks. That `<div>` should disappear if `window == top` or we figure out that we don't need protection.
Like this:
```html
<style>
#protector {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
<div id="protector">
<a href="/" target="_blank">Go to the site</a>
</div>
<script>
// there will be an error if top window is from the different origin
// but that's ok here
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
```
The demo:
[codetabs src="protector"]
## Summary
Clickjacking is a way to "trick" users into a clicking on a victim site without even knowing what happens. That's dangerous if there are important click-activated actions.
A hacker can post a link to his evil page in a message or lure visitors to his page by other means. There are many variants.
From one side -- the attack is "not deep": all a hacker can do is one click. But from another side, if the hacker knows that after the click another control appears, then it may use cunning messages to make the user to click on it as well.
The attack is quite dangerous, because when we engineer the UI we usually don't think that a hacker can click on behalf of the visitor. So vulnerabilities can be found in totally unexpeced places.
- It's recommended to use `X-Frame-Options: SAMEORIGIN` on pages that are totally not meant to be shown inside iframes (or just for the whole site).
- Use a covering `<div>` if we want to allow our pages to be shown in iframes, and still stay safe.

View file

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Like pressed on facebook.html!')" value="I LIKE IT !">
</body>
</html>

View file

@ -0,0 +1,32 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<style>
iframe {
width: 400px;
height: 100px;
position: absolute;
top: 5px;
left: -14px;
opacity: 0.5;
z-index: 1;
}
</style>
<div>Click to get rich now:</div>
<!-- The url from the victim site -->
<iframe src="facebook.html"></iframe>
<button>Click here!</button>
<div>...And you're cool (I'm a cool hacker actually)!</div>
</body>
</html>

View file

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<body style="margin:10px;padding:10px">
<input type="button" onclick="alert('Like pressed on facebook.html!')" value="I LIKE IT !">
</body>
</html>

View file

@ -0,0 +1,32 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<style>
iframe {
width: 400px;
height: 100px;
position: absolute;
top: 5px;
left: -14px;
opacity: 0;
z-index: 1;
}
</style>
<div>Click to get rich now:</div>
<!-- The url from the victim site -->
<iframe src="facebook.html"></iframe>
<button>Click here!</button>
<div>...And you're cool (I'm a cool hacker actually)!</div>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
#protector {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 99999999;
}
</style>
</head>
<body>
<div id="protector">
<a href="/" target="_blank">Go to the site</a>
</div>
<script>
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
This text is always visible.
But if the page is shown inside a document from another domain, the div over it prevents any actions.
<button onclick="alert(1)">Click doesn't work in that case</button>
</body>
</html>

View file

@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe src="iframe.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div>Changes top.location to javascript.info</div>
<script>
top.location = 'https://javascript.info';
</script>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
iframe {
width: 400px;
height: 100px;
position: absolute;
top: 0;
left: -20px;
opacity: 0;
z-index: 1;
}
</style>
<script>
function attack() {
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Want to leave without learning all the secrets (he-he)?";
};
document.body.insertAdjacentHTML('beforeend', '<iframe src="iframe.html">');
}
</script>
</head>
<body>
<p>After a click on the button the visitor gets a "strange" question about whether he wants to leave.</p>
<p>Probably he would respond "No", and the iframe protection is hacked.</p>
<button onclick="attack()">Add a "protected" iframe</button>
</body>
</html>

View file

@ -0,0 +1 @@
# Frames and windows