JSONP, Google Spreadsheet security


Simon Willison has a pretty good overview of things to remember (or to make sure your framework remembers for you) when building a secure web site; one of the things mentioned is JSONP.

The obvious risk for the hosting site that's using JSONP to receive remote data is if the JSONP remote site changes the code that's returned: they can then control the page on which the code is embedded (a similar risk to embedding Flash, except there you can set allowScriptAccess to 'never').

The other risk of using JSONP is to the user whose data is being served. One risk, mentioned in Simon's slides, is that if a user's private data is served at a predictable URL—even with some forms of authentication—any page the user visits is able to fetch that data and read it.

For this reason, Google requires authentication using ClientLogin (username + password) or AuthSub (token) when requesting sensitive data, such as contacts, using JSONP.

However, this doesn't apply to the Spreadsheets API, perhaps because it needs to be able to access the data in order to draw visualisations and use the data in gadgets. This means that if someone has published a spreadsheet that an attacker doesn't have access to, and the attacker can make them visit a malicious page, the attacker can fetch the spreadsheet data in the background using JSONP and access it.

I reported this to Google and, to their credit, they provided a satisfyingly detailed response - that this was known behaviour, that they had already ensured that spreadsheet URLs contain unpredictable keys and aren't leaked in referer headers, and that it was a "low risk"/"no practical real world risk" vulnerability - but they're looking to add further defence mechanisms to the JSONP URLs in the future.

They said it was ok to write about this, so: if you have sensitive data in Google Spreadsheets, just make sure no-one knows the URL.

var google = { visualization: { Query: { setResponse: function(data){
<script src="http://spreadsheets.google.com/tq?key=SPREADSHEET_KEY"></script>