The script tag is often used to bypass the same-origin security policy of browsers, and communicate with sites belonging to other domains. In essence, a web page hosted on www.foo.com could use this technique to send a request to www.bar.com using some JavaScript. Some Ajax frameworks offer transport layers that use this technique for cross-domain requests. The Dojo framework has had one for a long time. There is one for the Prototype framework now. There is nothing wrong with the technique employed, but whenever I see it mentioned as a "pluggable" transport layer in Ajax frameworks, alarm bells start ringing in my mind. While being an interesting technique, it is not safe, it is not HTTP friendly, and it must be used with care.
The technique is quite simple to use. A page on a domain www.foo.com includes a script tag pointing to a URI on www.bar.com.
<script src="http://www.bar.com/path?params"></script>
The resource at www.bar.com processes the request, and responds with some JavaScript that may include code declarations, data, and calls to execute code.
// Executable code
function foo(...) {
...
}
// Data
var data = { ... };
// Execution
foo(data);
Since the script tag is expecting JavaScript back, it will faithfully evaluate the response, in turn processing any executable code. This technique can be used in a number of ways with different kinds of URIs.
| Request URI | Response |
|---|---|
http://www.bar.com/getUser?name="subbu" |
var resp = {status: "OK", response: { firstName: "Subbu", lastName: "Allamaraju", address : { street: "1 Main Street", city: "Some Place"}}}; someFunc(resp); |
http://www.bar.com/updateUser?street="2 Main Street" |
var resp = {status: "OK", response: { firstName: "Subbu", lastName: "Allamaraju", address : { street: "2 Main Street", city: "Some Place"}}}; someFunc(resp); |
http://www.bar.com/deleteUser?name="subbu" |
var resp = {status: "OK"}; someFunc(resp); |
To support this technique, the site at www.bar.com must expose some URIs to operate on some resource. In the above example, www.bar.com exposed a set of URIs to read, modify or delete a user, and returning some JavaScript upon processing each request.
The so-called "pluggable" transports in Ajax frameworks make the process of creating requests and processing response easy while letting the code follow the same syntax as is used for XMLHttpRequest. In addition, these frameworks also support form submissions through this tag, which entails encoding the form into a query string and append it to the URI used with the script tag. See, for instance, introducing a cross site ajax plugin for Prototype for an example.
This technique is unsafe if used in the wild without any consideration towards security. If a page on www.foo.com can send a request to get a user’s data, so can a page on www.mrrouge.com. Once Mr Rogue figures out (which is quite easy) the URI space available on www.bar.com and the request URI parameters, embedding the same script fragments on any site is trivial.

A rogue site can now use this technique to not only read sensitive data, but also modify or even delete data. One way to solve this problem is to let both www.foo.com and www.bar.com rely on a shared secret, and use the shared secret to encode a special token (e.g. a signature) in the URI generated for the script tag. The site at www.bar.com will then verify that the token is indeed generated by www.foo.com and that the request URI has not been tampered with, before processing the request.

This technique will ensure that only sites that share a secret can participate in cross-domain requests. Since www.mrrogue.com does not share the same secret, it will not be able to construct a URI containing the token, and hence www.bar.com will reject the request. To safe-guard against replay attacks, the token generated should include a timestamp so that the token will be valid only for a short-period of time.
The second issue to realize is that this technique is not HTTP friendly. It requires that every request be a GET, and it should only be used for idempotent requests. While it is possible to serialize a form into a query string of a URI, and use that for non-idempotent requests, the HTTP layer will not be aware that the request is non-idempotent, and hence must not be replayed and or cached. This could lead to to some interesting problems. For instance, browsers do not resubmit POST requests without the user’s permission since POST requests are not considered idempotent, and resubmission may have side-effects on the server. On the other hand, a request submitted via the script tag is considered idempotent by browsers, and can resubmit the request without user intervention.
Given these potential issues, the script tag should not be used as a general purpose alternative to XMLHttpRequest without proper analysis and precautions.
