How Genius annotations undermined web security
How Genius annotations undermined web security

  
The primary way Genius annotations are accessed on the web is by adding "genius.it" in front of any URL as a prefix. The genius.it server reads the original content behind the scenes, adds the annotations, and delivers the hybrid content. The Genius version of the page includes a few extra scripts and highlighted passages, but until recently it also eliminated the original page’s Content Security Policy. The Content Security Policy is an optional set of instructions encoded in the header of the HTTP connection which tells browsers exactly which sites and servers should be considered safe — any code which isn’t from one of those sites can then be ignored.
Content Security Policies were first introduced in 2012 and are not yet in widespread use, since they can interfere with scripts used for advertising and social-network functionality, and thus tend to be implemented only by sites with high security standards. Still, the sites that do supply Content Security Policies include PayPal, BuzzFeed, Facebook, Twitter, Airbnb, Pinterest, CNN, and IMDb, among others. Since the web-annotator product is designed to work as a substitute for any webpage on the internet, Genius presented a substantial new attack surface, theoretically usable by any malicious hacker who could lure their victims into clicking on a Genius redirect.

Having a Content Security Policy in place drastically limits the viability of a type of attack called "cross-site scripting," or "XSS," in which an untrusted party gets a trusted site to execute malicious code. In a blog post published on April 12th, one of GitHub’s security experts referred to cross-site scripting as "the most common web vulnerability of the past, present, and foreseeable future." The 2013 post in which Github first announced that they’d added a Content Security Policy said "this is the main protection you’ll want against XSS."
The easiest way to hijack a site with a JavaScript exploit is to run the code as an "inline script," which means the code is simply printed directly on the page as part of the content instead of being called from a separate file. A Content Security Policy typically prohibits inline scripts, instead allowing only scripts hosted on remote servers that have been specifically whitelisted. The vast majority of cross-site scripting attacks rely on getting code to simply appear on pages and then execute, so this added restriction removes their primary publication method.
After we initially disclosed the Content Security Policy issue, Genius correctly pointed out that the risk of cross-site scripting attacks was minimal, because the web annotator doesn’t store any personal information about its users in between successive page loads, let them log in to accounts, or type sensitive information into web forms. But these steps did nothing to protect users from exploits that did not depend on cross-site scripting, such as forcing malware downloads. It proved trivial to bypass the form restrictions and run keyloggers. I submitted several proof-of-concept demos showing that those kinds of actions could all be suppressed by a Content Security Policy but then unlocked again when viewed through the web annotator. On  Tuesday, May 3rd, Genius resolved the issue by changing the web annotator to restore the original Content Security Policy, with a few extra modifications which allow the extra annotation code to run.
To understand why this happened, it’s important to consider what Genius is trying to do and how its web annotator works. Completely discarding security settings was more than just a reckless step toward building its tool: as I continued to explore the issue, I began to realize that the entire service is built on top of a unique approach to overriding the standard security practices of the web — protections that would ordinarily prevent the annotations from being technically possible in the first place.

The cross-site scripting prevented by the Content Security Policy is one of a broad class of security issues collectively known as "code injection." Code injection describes cases in which a malicious person uses a known system input to send unexpected, disruptive, or destructive commands — imagine an individual with database access injecting code that deletes the database. On the web, getting a page to run an untrusted script is a problem because, well, pages contain information. Your bank’s web portal contains sensitive details about your finances. If a malicious script were to gain access to that page, it could read all that information and then send it off to any other server on the internet. Or it might add a "clickjacker," an invisible layer that sits over the entire page, capturing your clicks and making them do unexpected things — like transfer funds.
Content Security Policies are a stricter and more powerful reinvention of an important defense against cross-site scripting called the "same-origin policy," a browser behavior that has been universal practice for the past 20 years. The same-origin policy says that information from a page is only available to a script if that same page also served the script. This means that even if a malicious script somehow gets added to a page and starts running, the browsers will refuse to let it access the page’s content. So to a malicious attacker, there are really two key ingredients to a successful XSS exploit: first, they have to find a way to inject the code and get it to run, and only then can they use it to do something terrible. Once the same-origin policy was in place, the first step actually became harder than the second.
The restrictions of the same-origin policy don’t mean much in many vanilla web-development tasks, because a site and its scripts are often served from the same domain. Sometimes there are legitimate reasons to share data between servers, though, so there are several technically sound workarounds which bypass the same-origin policy, like JSONP and CORS headers. The Genius scripts use something called postMessage, which requires coding a transmitter on one end and a listener on the other. Even though the details differ, the common feature across all these solutions is that they are enabled only when specific pieces of code are present on both servers. No matter the implementation, cooperation and consent from both sides of the exchange are always required.
A less elegant but simpler way to bypass the same-origin policy is called a "proxy," which is a server-side tool that reads content and then just immediately outputs it again. After going through a proxy, the transmitted data appears to be coming from a known friendly server, and a browser’s same-origin security checks won’t block the client-side script’s ability to access it. This is usually done for specific files or data feeds — small targeted bits of information with narrow scope — but it is also essentially how the Genius annotation redirects work: they proxy entire pages, for any URL on the internet, and add the annotations.
Unlike most other proxies, the Genius proxy does not just pass the content through unchanged. Instead, it rewrites the page very slightly, to insert a set of new scripts — which, among other things, start listening to postMessage. Because the proxy server is controlled by Genius, its scripts are now allowed to execute freely in ways that would have been blocked by the same-origin policy if they’d just been loaded on the original page. This is the only reason Genius can modify the original text to insert highlighted links which will reveal the annotations — a key part of the web-annotator product. At this point Genius no longer needs the cooperation of the target site, just a fast proxy that can quickly republish the page on a server where they make the rules. A treasure trove of content for the web annotator is immediately unlocked. The same-origin policy also benefits Genius in the opposite sense: as far as site owners are concerned, the policy is still in place. Since the original site doesn’t control the proxy, scripts from the original page can’t touch the added annotations.

There can be little argument that Genius has one of the most compelling proxy services on the market. The web annotator is best-in-class technology, a powerful and groundbreaking tool wrapped up in a slick user interface. As a result, the company now also has a modest but growing library of exclusive and original user-generated content. But the viability of this entire system relies on their proxy first overriding browser security — otherwise, per the same-origin policy, their annotation code wouldn’t be able to touch the page text in order to highlight it.
This is a strange case where the users and the site owners might disagree about whether the extra code qualifies as unwanted malware. That quickly leads into a tough question of identity for digital content. Based on the server connections, the proxied page is technically now a site served by Genius, and as such, they are now well within their rights to define the security restrictions. But if you were to consider the content — by asking the people reading it, for example — then there is certainly a sense in which you are still looking at the original site. Part of the appeal of Genius is precisely that it makes minimal changes to the look and feel of the page: the goal is to present the original page to the user, with the extra script inserting annotations on top of its content. But that action is also exactly what the same-origin policy tries to forbid.
The conventional definition of XSS includes three specific variants, but there are also newer kinds emerging. For example, there are new ways of saving data in the browser’s memory instead of in a database on a remote server. This makes things faster, but it’s also one more place to store malicious code used for XSS.



        
        
        
      
  



So with all this in mind, is the Genius web annotator actually a form of cross-site scripting?
Well, not in the conventional sense — it doesn’t actually fall into any of those three categories, mostly because the site-crossing happens server-side through the proxy instead of in the browser. Of course, the goal of the web annotator is not to harvest user information for malicious ends. But internet security is a continually evolving field in which maybe "conventional sense" doesn’t mean much. Forget that "cross-site scripting" is an established term. The web annotator runs JavaScript code using two sites simultaneously: its own server, and the original content being annotated. By any sufficiently literate interpretation, this is cross-site scripting. That is simply what those words mean.
We can also take a cue from Genius engineering: the filename of the main annotation script is actually injection.js, and one of the things it injects is explicitly labeled as a "clickjacker." The use of special terminology, which typically refers to exploits, suggests that the decision to model the product after security flaws was deliberate.

All this not to say that we shouldn’t have proxy servers like the one Genius uses, nor services based on them. But for the most part, other proxy services are just passing packets of data along, not altering their content. A proxy where the primary value is not the routing of packets, but rather their substance, is a new twist. A proxy that exists specifically to override security so it can inject that content is weirder still. The Genius web annotator clearly had a security flaw in the form of widespread Content Security Policy suppression, but there’s a strange sense in which at its core, it also is a security flaw: a complete reimagining of how cross-site scripting can work in a world of lightning-fast servers and endless bandwidth. This is a whole new class of technology product, and a weird new world.
I was first prompted to take a close look at how Genius works on March 25th, when sexual health blogger Ella Dawson published a searing complaint alleging that the web annotator was tantamount to the forcible creation of comments for sites that don’t want them. I sympathized with her concerns somewhat, especially since Genius has thus far neglected to provide an opt-out mechanism for site owners. I immediately began working on a WordPress plugin called Genius Defender, which renders the service unusable for pages where it is running.
I released the code on GitHub, but it was also accompanied by a fairly long explanation on my personal website about how it works and why it was necessary. That post was picked up by a number of outlets (including The Verge’s sister sites Vox and Recode). Sure enough, most of those articles had soon been annotated. I didn’t really participate in those discussions myself, but I did follow along as a reader, curious how Genius users might respond. As I was idly clicking through the annotations, something new and surreal happened, and for a split second I could see the future I suspect Genius is pursuing.
One of the features of the Genius proxy, which has been largely ignored throughout the controversy sparked by Dawson’s post, is that it also rewrites all the links on the page so that they all point to other Genius proxy versions of the original URLs. This is the primary reason Genius was undermining the Content Security Policy. The only programming language a webpage can execute is JavaScript, and when JavaScript is executed in the form of a remotely hosted script — that’s the the safer kind, less likely to be an XSS exploit — the language simply doesn’t know anything about the page that called it. So in order to rewrite all the links correctly in relation to the URL of the current page, the code needs to instead run as an inline script which knows the page’s address. That means the script needs to be injected, and the same-origin policy and Content Security Policy need to be defeated. Hence the proxy, where Genius takes control, and can do all of the above.

Genius removed the Content Security Policy primarily in order to let their inline script rewrite the links; that change wasn’t technically required by the annotations themselves. After we pointed out the problem, Genius responded by changing the service to use a bleeding-edge Content Security Policy feature which isn’t yet supported in all browsers. Now, the links are rewritten only in cases where the browser can safely run the Genius inline scripts while prohibiting other security risks. In browsers that don’t yet support that new Content Security Policy feature, the links are not rewritten, and still point to the original unannotated pages. User security is now prioritized over the product proliferation strategy. Until two weeks ago, it was always the other way around, which is why the web annotator had evolved into a more dangerous way to view the web than the original pages.
Eventually, all browsers will be updated to support that new Content Security Policy feature, at which point the link rewriting behavior of the web annotator will once again be as universal as it was previously, before Genius fixed the bug we reported. With the rewritten links in place, once you start browsing Genius there’s no natural way to stop — every link just points to more Genius content. To get back to the regular unannotated web, you have to close the tab, or manually type something new into the URL bar. Seemingly by intention, annotated pages have no visible "show the original" style attribution link. And in a sense, sure, why should there be? The original site is already right there, almost exactly the same but for a few highlighted passages, and Genius is just adding more content that they can be fairly certain the reader is interested in.
This means that if the company is successful, there could be a future where it starts to make sense to view large swaths of the web through Genius, with its additional content ready to go. If the annotations are almost always relevant, and you’re still free to just scroll right past them when you don’t care, then maybe we’ll all start to let the Genius proxy site mediate the real internet more often — much as AOL keywords did in the ‘90s and Facebook vaguely threatens to do even today. This could be extremely lucrative, especially considering that the company just launched a new advertising arm. Getting to that level of ubiquity won’t be easy, though: the vast majority of pages on the internet do not have any annotations, and annotated documents that are connected by the rewritten links are even rarer, so those key moments of transitioning from one annotated source to a second without ever touching the originals are still few and far between.
This is in part why there is no opt-out mechanism for site owners. If you squint, this tactic looks vaguely reminiscent of Uber’s consistently ruthless expansion into new cities, often with little regard for the relevant local ordinances. Even though the Content Security Policy bug is now fixed, Genius still has another problem: they need to add as much user-generated content as possible right away — currently the vast majority of redirects don’t contain any annotations. But spurring user and content growth is only part of the reason why they’re scraping all these pages without asking permission. They are also doing it because, technically, due to the same-origin policy, they must. For Uber, expanding the service’s boundaries is mostly about staking a claim on additional market share in a hypothetical future. But for the Genius web annotator, which was developed only after the browsers of the world had already become too secure for code injection, aggressively colonizing the rest of the internet is the only way forward.



Vijith Assar is a writer and software developer based on New York. He is the creator of Genius Defender, a tool for websites to block the Genius annotator.


