Traveller Map in Second Life

Okay, this is pretty sweet.

About a year ago I took advantage of Second Life's "Parcel Media" support to build an in-world map browser of TravellerMap.com in SL. This basically just sets the media URL of a parcel (a chunk of land) to a map URL, and built an object that is textured with that media URL, along with controls to pan and scale the map. Here's what I came up with:

(I'm that attractive sea creature, by the way.)

Today I got a link from Corro Moseley pointing me at another in-world build by Daden Limited:

I believe it supports multiple map sources, not just TravellerMap.com, and wow is that integration nice. Look, look! I'm standing on Utoland! Look at the text board in the upper left corner - it's showing the RSS feed from this blog, and you can click to read articles! Ω



Allen Varney asks:
...doesn't this functionality leave the map site open to -- at the very least -- cross-site scripting (XSS) hacks? The whole procedure sounds quite insecure.
Short answer: no (but read on)

But it's an excellent question, so let's analyze the potential security vulnerability.

First off, Wikipedia has a reasonable Cross Site Scripting (XSS) overview.

The short version is that a vulnerability in some site allows an hacker to add content that, when viewed by a victim, executes the hacker's code using the victim's data.

For example, let's say VulnerableForum.com allows evil hacker Eve to post arbitrary script code. When victim Alice visits the site, the web page loads and Eve's code can execute within the context of VulnerableForum.com. That means it could do things like make forum posts in Alice's name, change Alice's password, modify Alice's profile (perhaps to include additional copies of Eve's code, turning this into a viral worm), and so forth.

The Wikipedia article lists several variations of this beyond simple persistent content modification, e.g. Eve sends Alice a URL to VunerableForum.com that includes parameters which induce bugs in the forum software, allowing code injection.

The "classic" form of XSS was what led to the Same Origin Policy (SOP). In the old days, Eve's web site (EvilHacker.com) could load up AlicesBank.com in an IFRAME. If Alice visited EvilHacker.com, the IFRAME would load AlicesBank.com - and possibly log in automatically. The scripts from EvilHacker.com could then reach into the IFRAME and manipulate the now-authenticated bank pages to steal money. In response to this, browser makers implemented the SOP, which means that the scripts can't reach into the IFRAME if the content is from a different site.

This SOP has been extended to all web technology - Flash, AJAX calls, and so forth. Unfortunately, this means that sites that want to cooperate - say, your site and TravellerMap.com - don't have a way to share data. Flash exposes a cross-domain mechanism (where receiving second site can explicitly allow requests from requesting first site). For JavaScript there are some cross-domain-enabling proposals. But for now, JSONP is a workaround. JSONP requires that both sites cooperate – the requesting site must specify a JSONP parameter in a URL request, and the receiving site must understand the JSONP parameter and create an appropriate response.

So…. back to the question of security.

  • Does accepting JSONP requests expose TravellerMap.com to XSS attacks? No – the site does not interpret the content of the JSONP parameter beyond %-decoding. It is simply pre-pended (along with "(" prefix and ");" suffix) to the data being returned.

  • Does using JSONP requests expose your site to XSS attacks? Ah, there’s the rub. By making JSONP requests you are allowing TravellerMap.com to inject script and data into the execution context of your web pages. Let’s analyze how this could be exploited:

(1) TravellerMap.com could THEORETICALLY add malicious script to the JSONP results. In that case, instead of emitting "callback(…);" the site emits "make_forum_post(…);" or something. Since the script is executed automatically (via the JSONP mechanism) and the data can’t be filtered (it appears as a script tag which executes the content, not data you can deserialize with paranoid JSON parsing code), there is no simple defense against this. If TravellerMap.com is compromised (or I turn to the dark side…) then your site is vulnerable.

Note that this vulnerability is restricted to XSS behavior, that is, users of the site would have code executed in the context of the web site. This doesn’t allow arbitrary code execution on the victim’s machine (it’s still in the web page sandbox), or on the web server (it’s all client-side). So this isn't a "pwn'd your machine" exploit, but it is a (theoretical) "pwn'd your account" exploit.

Also note that sites like Flickr expose JSONP APIs with the same risks, so it’s not a flaw in the design of the TravellerMap.com API. Either you trust the site you’re calling into with JSONP or you don’t.

For the paranoid (i.e. you want to use the TravellerMap.com JSONP APIs, but don’t trust the site to remain exploit-free), one mitigation would be to sandbox any use of the JSONP APIs. For example, your main site might be mytravellerforum.com, and create a sandbox.mytravellerforum.com subdomain that doesn’t do anything with users' forum accounts. And of course, if you are implementing server-side functionality, you can call directly into the non-JSONP versions of the APIs, in which case you’re parsing the data yourself.

(2) TravellerMap.com could THEORETICALLY expose a vulnerability that allows JSONP requests to include arbitrary content.

Here’s how a hacker might exploit that:
  • Your site exposes a "make a map for sector X" form. The user can type the sector name into a form, which causes a JSONP request to TravellerMap.com
  • A hacker crafts a URL which makes your site make that request automatically (due to the architecture of your site, possibly a redirect). The URL includes the sector name as: "); do_evil(…); void("
  • Your page turns that into a JSONP request
  • The TravellerMap.com site THEORETICALLY blithely outputs that as part of a valid response, un-escaped
  • Your site executes that script with the do_evil() content (which, say, makes a forum post)
  • The hacker posts this nasty URL to a forum. Any user that clicks on the link ends up with do_evil() running within their authenticated context.
It’s a bit convoluted, but that’s how these XSS attacks happen. A further THEORETICAL vulnerability would be if the TravellerMap.com site allowed users to post arbitrary data which could then be retreived by a third-party site, but it does not. (No submitted data is retained by the site beyond the lifetime of the request.)

So how to mitigate it?
  • Sanitize user input on your site – don’t make arbitrary requests to external sites
  • Ensure JSONP logic exists in a sub-domain sandbox (see above)
  • Perform your own security audit – try the TravellerMap.com JSONP-enabled APIs and send test data. To the best of my knowledge, all data from successful requests are properly escaped (user data would appear as strings within JSON data) and errors are returned as 404s (the scripts should not execute).
Again, the same issues exist with any JSONP API - you must trust the site you're calling both to not return malicious scripts intentionally and to be hardened against vulnerabilities that would allow someone to cause malicious script to be returned.

So... an excellent question with a non-trivial answer. Feedback, as always, appreciated! Ω



You can now slap a JSONP parameter to SEC and MSEC calls to get data in cross-domain-friendly format.

Add something like this to the code in your page, triggered (for example) after the user clicks a link or fills in a form field:

   var sector = "Spinward Marches";
var url = 'http://www.travellermap.com/SEC.aspx' +
'?q=' + encodeURIComponent(sector) +
var script_tag = document.createElement('script');

This adds a new <script> element on the fly, filled in with "source code" by TravellerMap.com. The script it generates will look like this:

    myfunction("# Spinward Marches\r\n# blah blah\r\nRegina    1910 ...";

The script will run immediately when it is done loading. So you add a function called "myfunction" (or whatever) to your code and it will be called with a whopping huge string containing the sector data, ready for your parsing needs. Ω


JSONP - It's what's for breakfast

Good news, everybody!

I've added JSONP support to the data APIs (the image APIs don't need it). JSONP allows web pages living on your sites to call APIs on the TravellerMap.com site without any server-side work - it's all in the JavaScript.

More details: JSONP in the TravellerMap.com API Documentation

Here's a short introduction to using JSONP from jQuery which is my favorite JavaScript framework. (You'll note that I don't use any frameworks on the TravellerMap.com site; I was loathe to touch them when I started, and the site was a grand experiment in building everything from scratch. But I digress...) Ω


Spinward Marches Data Update

Despite numerous updates to the bulk of the Imperial data (thanks to the CotI cleanup crew), the Domain of Deneb has suffered in comparison. There has been so much attention paid to this area that there are multiple updates and contradictory canonical and quasi-canonical publications (Supplement 3: The Spinward Marches, The Spinward Marches Campaign, MegaTraveller Journal, Regency Sourcebook, etc). Of course, between the Second Survey (1065) and Rebellion (1116-) there was also the Fifth Frontier War (1107-1110) that redrew borders. So even if you set your campaign on the eve of the Rebellion (not coincidentally the end of the Classic Traveller era and the branch point with the GURPS Traveller alternate timeline) - which is ostensibly the era of my map - it's tricky to get the data just right.

A few weeks ago I sent email to Jerry R. Mapes asking if I could include his cleaned up Spinward Marches data in my site. He granted permission, with a warning that there was additional cleanup to do and that his failing health precluded spending time on it, but that he was optimistic for the future. Sadly, reality intervened, and Jerry passed away a few days ago. (

I've taken his SM 1105 file and made some further corrections by comparing it with other extant data files to turn up anomalies, and set the allegiances to Post-5FW which match the borders on my map.

Here are the specific changes:

  • 0605 name "Algeblaster" --> "Algebaster" (per S3:SM, RSB) - this is consistently mis-typed in SEC files, but is consistently "Algebaster" in print.
  • 3228 name "Fen's Gren" --> "Fenl's Gren" (per S3:SM text) - the alternate "Fen's Gren" name appears on the S3 map and in SMC and RSB. I have no particular attachment, but I saw no big reason to deviate from S3:SM
  • 2125 pbg "725" --> "723" (per RSB) - probably just a typo
  • 2519 name "Plannet" --> "Pannet" (per S3:SM, RSB) - another consistently mis-typed name in SEC files, but consistently "Pannet" in print
  • 0234 name "Andory" --> "Andor" (per SMC, RSB) - this is contraversial. In S3:SM this "Andory" in both the data and map but later sources use "Andor". As this is a fairly well-known world (one of the few multi-world Droyne states in Charted Space) I kept "Andor".
  • 1110,1209,1210,1311 alleg --> "Fa" - the Federation of Arden worlds, post-5FW
  • 1324,1325,1522,1523,1524,1525,1526,1529,1626,1627,1628 alleg --> "Bw" - the Border Worlds, post-5FW
  • 0236,0336 alleg "Im" --> "Dr" - S3:SM left these as Imperial, but later sources annotate these as trade code "Dw" (Droyne world) or allegiance "Dr" (Droyne). Again, since these are Significant in Charted Space, I decided to make them stand out on the map.
As always, comments appreciated. Ω