Same Origin Policy
In the context of web security there’re two main vectors of attack: XSS and CSRF. Before describing them, we have to understand the default browser policy about cross-tab interactions (or interactions between scripts with different origins, more precisely speaking). This policy is called Same Origin Policy (SOP), and “origin” is the central concept of it. Origin is determined by combination of these: protocol (http/https, etc.), host (my-website.com) and port (as an example, 443 for https and 80 for http).
Here’re the examples which will be interpreted by the browser as scripts of the same origin (if an open tab has three scripts with these origins, then the browser’s gonna perceive it as scripts with the same origin).
And these origins are gonna be counted as different:
An important feature of SOP is that the script with the same origin can send requests on another origin, but it won’t be able to read the response (reading the response would be blocked by the browser). If it’s necessary to block the very ability to send a request (say, we don’t want scripts downloaded on our web page from other origins to be able to make requests on our origin), then there’s such a possibility too (moreover, it’s advised) by specifying Content Security Policy.
XSS / JS-Injections
The most common attack is Cross-Site Scripting or as a more appropriate name in my opinion - JS-injections (in analogy with SQL-injections). If there’s a string inserted into an html document and it contains elements similar to html tags, then the browser is going to interpret these elements as a valid html code (and executes it). For instance, we can write <img> with empty src attribute and add onerror=“alert(‘hacked’)” (if src is empty, then the browser will execute onerror), inside of which any JS code could be executed (having access to localStorage, etc.). Also malicious script could be inside of query params in the following form:
In modern frameworks (like Angular or React) sanitisation is enabled by default, which could be disabled in case we’re sure the script is not malicious. In React it can be done by using dangerouslySetInnerHTML attribute, highlighting the danger of inline-script insertions.
In Angular we may inject a sanitizer and choose the level of sanitisation appropriate for our conditions (for instance, it’s possible to read styles, shielding any scripts). It looks like this: this.safeString = this.domSanitizer. bypassSecurityTrustHtml().
Apart from sanitisation there’s Content Security Policy Header. By this header it’s possible to explicitly set which scripts can be executed on the current page based on its origin. By default CSP contains * value, meaning that the browser is allowed to execute scripts with any origin (including inline-scripts without any origin). The setting is considered highly insecure, and it’s recommended always to explicitly specify the allowed scripts (explicit indication of allowed origins also prevents execution of inline-scripts).
Here’s an example how CSP could look like:
default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com
These settings indicate that images can be loaded from any source (*), media only from two sources (media1.com, media2.com), and only these scripts that have one particular origin (userscripts.example.com) are allowed on current page. “default-src ’self’” allows to run scripts from current origin.
CSP gives fine-grained control over scripts execution on a website.
CSP Header and access-control-allow-origin quite similar in a sense that CSP allows to bypass SOP (default browser policy concerning scripts execution based on its origin), while access-control-allow-origin allows to get permission for CORS (to access responses from requests made by scripts with different origins). In the beginning of this article it was mentioned that SOP allows scripts with one origin make requests on another origin, but the SOP blocks the response. By specifying access-control-allow-origin we can access the response in this case (if the server explicitly indicates the origin allowed to access responses).
Long story short, CSP is about SOP and frontend part of a system and access-control-allow-origin is about CORS and backend part of a system.
Second most common type of vulnerability is Cross-Site Request Forgery. This vector of attack abuses a browser inclination to add cookies to any requests from the client, regardless of a particular tab. For instance, we’ve authorised on a website (let’s suppose it’s a bank) using cookie, and at some point a malicious script makes a request on the bank’s origin (we’ve opened a mail client a clicks on a suspicious link). In this case a browser automatically adds cookies to the request, even though the query comes from another open tab. And, yes, because of SOP this script doesn’t have access to a response, but it still can make queries with authorised user’s rights (as an example, making requests for money transfer or deleting user information).
For CSRF prevention first and foremost it’s recommended to use explicit system of authentications (session token / jwt in header or session token in body param). Also if a site uses some form, we can use anti-csrf tokens, which implies that a server gives two random token, one for cookies, and the other is meant for hidden field in the form. When a client submits a form, it’s expected to add these tokens back to a server which compares these tokens (sent and received) and decided whether to block the request or not.
In the context of information security there’s a concept of CIA triad, combining three ideas: confidentiality (access to information should be granted only to those whom it’s meant), availability (information should be accessible in the necessary amount and at the right time) and integrity. The last implies that parts of a system should be able to send reliable/trustable messages to each other and no one should be able to intervene in the transmission and compromise the integrity of data. In a web world it can be expressed in a form of relations between frontend and backend, and html has a special attribute called “integrity” that ensures a script execution only if its structure wasn’t compromised (isn’t different from what developers initially builded).
Suppose we use cdn (Content Delivery Network) for load optimisation of our application, however we want to ensure that the application we build locally ourselves and the one client gets from cdn are going to be the same. In this case during the application’s build process it’s possible to generate a unique integrity hash specified in the following way:
<script src="https://example.com/example-framework.js" integrity=“sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC”>
When a browser tries to load a script from cdn, it generates its own hash of the script (using the algorithm name, it gets from integrity attribute, in our case it’s sha384), then the browser compares these hashes and based on the comparison decides whether the script is allowed to be executed.
In Angular we can enable hash generation by using special flag called “--subresource-integrity”.