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.

Genius presented a substantial new attack surface

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.

Genius is built on top of a unique approach to overriding the standard security practices of the web

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.

Genius has one of the most compelling proxy services on the market

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.