Handling Web Application Security
What is security?¶
In general, application security refers to protective steps and rules taken or implemented by developers to protect the application from cyber-criminals and threats that can exploit private information (data). Security is a critical part to ensure business continuity and protecting user data, organizations from risk.
Even with understanding the importance of security, organizations take security hits and we can point out some of the observable reasons from this as follows:
- Feature & Deadline Vs Security: As the application grows developers always trying to fix bugs and add new features. So naturally, security becomes a secondary concern.
- Awareness: Particularly web developers are fallen behind in terms of security awareness and lack of knowledge as compared to backend or DevOps counterparts.
- Wide range: As the software industry continues to grow every day, so as its threats. It becomes difficult to maintain updates on new potential security threats.
All these things above compound into us as developers needing to take responsibility for our code, follow standard practices that will ensure the safety of the application.
What are common Web application attacks?¶
Most common form of web attacks includes:
Img 1: Rough outline of web application system
- Cross Site Scripting (XSS)
- Cross Site Request Forgery (CSRF)
- Clickjacking
- Third party assets
Cross site scripting (XSS)¶
What is XSS?¶
XSS is a client-side code injection attack. In this threat, an attacker can trick our system to treat content as code and execute it. This allows the attacker to compromise the interaction that users have with the application. For example,
// Intended code
<h1>Welcome, ${name}</h1>
name = ‘Mike’;
<h1>Welcome, Mike</h1>
// Injected code from a hacker
name = ‘Mike<script>terrible()</script>’;
<h1>Welcome, Mike<script>terrible()</script></h1>
- JavaScript has access to the whole web page which also includes cookies. Most commonly cookies are used to keep the session cookies. Once the attackers are able to access these cookies then, they can impersonate the user, use/access sensitive data on behalf of the user.
- Using JS attackers can access DOM and make changes to it.
- Attackers can use the XMLHttpRequest object to send an HTTP request with the help of JS.
How does XSS work?¶
For a typical XSS attack to work there are two steps:
- The attacker must find ways to inject code into the victim's web page that the victim frequently visits.
- After that, the attacker can do some social engineering to target particular victims. Otherwise, the victim just has to visit the web page with malicious code.
Img 2: Sample XSS attack
Types of XSS attacks¶
Stored XSS
Code that executes attacker’s script persisted (malicious script comes from database). Here is a simple example of a stored XSS vulnerability. A message board application lets users submit messages, which are displayed to other users:
The application doesn't perform any other processing of the data, so an attacker can easily send a message that attacks other users:Reflected XSS
Temporary response from server causes the script to execute (i.e. validation error).
If the application doesn't perform any other processing of the data, so an attacker can easily construct an attack like this:DOM Based XSS
In this attack no server involvement is required, the vulnerability exists in the client-side code. In the following example, an application uses some JavaScript to read the value from an input field and write that value to an element within the HTML:
If the attacker can control the value of the input field, they can easily construct a malicious value that causes their own script to execute:Where to look for XSS vulnerability?¶
- User generated rich texts.
- Embedded content.
- Anywhere users have control over URL.
- Anywhere users input reflected back.
- Query parameters rendered into DOM.
element.innerHTML = ?
How to defend XSS?¶
To protect our web application from XSS one of the most important thing is never ever trust the user data (raw user data). In particularly there are really terrible places to put untrusted raw user data, listed as below:
- Directly in script,
<script>${userData} </script>
- In a HTML comment,
<!-- ${userData} —>
- In a attribute name
<iframe ${userData}=”myValue” —>
- In a tag name
<${userData} class=”myElement” >
- Directly in style block
<style>${userData}</style>
So, now the question is how to we can work around this? if some use cases need this implementation, for that we have to do two things. First sanitize data before storing and secondly sanitize data before rendering it onto the screen. Typically it is good to do both because there are loopholes in sanitization methods. Also having two layers of defense will have less chance of getting something exploitable. Ex:
<script>alert(‘Hi’) </script>
//Escaping data before putting it on HTML
<script>alert('Hi')</script>
Content security policy (CSP)¶
Browsers directly can’t tell the difference between the scripts downloaded from your origin vs another. It is a single execution context. Here comes CSP, it is a browser’s security mechanism and it allows us to tell the modern browsers which sources they should trust, and for what type of resources. We can use the "Content-Security-Policy" HTTP header to specify your policy, like this:
Content-Security-Policy: <policy-directive>; <policy-directive>
Content-Security-Policy: script-scr ‘self’ 'https://test.com';
font-src 'https://fonts.googleapis.com'
XSS handling in web framework React?¶
React has good handling of XSS attacks, due to the use of JSX. ReactDOM escapes any values embedded in JSX before rendering it onto the DOM. Ex,
function App() {
const [text, setText] = useState(‘’);
const handleClick = () => {
setText(‘Hi there!!’);
}
return (
<div>
<button onClick={handleClick}>Click me</>
<p>{text}</p>
</div>
)
}
/*
The output of the above function on the DOM after clicking button is:
"Hi there!!"
*/
Even after changing the string as, setText(‘Hi there!!<script>doSomethingTerrible();</script>’)
output will be:
Hi there!!<script>doSomethingTerrible();</script>
// here script will not execute it will just render as text.
Even with all this automation React will not help in all cases, for that we only have to follow some standard coding practices while writing React code:
- We should always avoid the use of a prop called dangerouslySetInnerHTML provided by React.
-
Avoid accessing and manipulating DOM elements directly in React from escape hatches such as
findDOMNode
andcreateRef
ex,divRef.current.innerText="After rendering, this will display"
Cross Site Request Forgery (CSRF)¶
What is CSRF attack?¶
CSRF takes advantage of the fact that cookies (or Basic Authentication Credentials) are passed along with requests. Here an attacker tricks a victim into performing an action on their behalf. The severity of this attack can depend on the victim's permission levels like admin, user, guest.
Img 3: Sample CSRF attack
How does CSRF work?¶
Imagine an attacker has an image tag like above and the attacker puts that IMG tag into HTML email and sends it to the customer. If they happened to be logged in (their cookie works) they will make a request to the link in ‘src’, their credentials will be passed along, and by the time the request finishes they will have basically asked the bank to transfer funds to some other account.
How do we know if we’re vulnerable to CSRF attacks?¶
- If the server looks at cookies (basic authentication credentials) sent along with the request in order to authenticate or authorize a user is a major problem Except for client-side cookies which are not susceptible to CSRF.
- Using read-only cookies also can be a good solution to CSRF, i.e. HttpOnly header.
- localStorage/sessionStorage - don’t have this problem
How to defend CSRF?¶
The most popular way to prevent CSRF is to use an unpredictable token that is associated with a particular user. This token is called an anti-CSRF token. The basic principle behind anti-CSRF tokens is to provide the user browser with a piece of information (a token) and check if the web browser sends it back. The token must be unique and impossible to guess by a third party.
These tokens are generated cryptographically in an unpredictable way with each page load. A simplistic example could be, it generates numeric keys that should meet a certain condition like they are divisible by 67, 5, 812. Only if this condition is met server can verify the request as a valid request. Moreover, ani-CSRF tokens are disposable meaning, once a token is used that token value is not usable again.
There are multiple ways we can setup anti-CSRF tokens like,
- We can set up anti-CSRF tokens upon login and then verified by every form in the secession cookie.
- For more protection than the above way we can use a separate token for each request, to do so we have to just invalidate the token after it is verified. Although this method will impact negatively for overall application performance as the number of requests to the server will become more.
- Using non-persistent CSRF tokens.
Non-Persistent CSRF Tokens¶
If the web application is very busy and server storage is limited then it is better to avoid storing tokens on the server-side. There are two ways we can tackle this issue. These techniques are easy to implement and stateless:
- Double submit cookie: When a user visits (even before authenticating to prevent login CSRF), the site should generate a (cryptographically strong) pseudorandom value and set it as a cookie on the user's machine separate from the session identifier. The site then requires that every transaction request include this pseudorandom value as a hidden form value (or another request parameter/header). If both of them match at server, the server accepts it as a legitimate request and if they don't, it would reject the request.
- HMAC Based CSRF Tokens: In this method, the token doesn't have to be hidden from the client, because it can only be created (signed) with a secret key on the server. Only the server can cryptographically validate the token so that an attacker cannot forge or tamper with it. Thanks to this validation, the server doesn't need to store the token anywhere. When the browser makes a request, it includes the CSRF token just like usual. The server doesn't have to look anything up; it can use its secret key to confirm the token is valid. HMAC is used to create the signed token.
Validating Request Origin¶
Modern web browsers send an Origin header, which can not be altered by client-side code, with each request. In some cases, if there is no Origin header, there’s almost always Referer header. In either case, we have to verify that its value matches the target origin to protect the attack against CSRF.
Cross origin resource sharing (CORS)¶
The third best practice is to set up CORS headers properly in our web application to tackle CSRF attacks. CORS is what basically permits browsers to allow us to send requests from one domain to another. Sample flow on how things happen within browsers internally:
// Preflight REQUEST to server
Origin: http: '//foo.example'
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, content-type
// Preflight RESPONSE from server
Access-Control-Allow-Origin: 'http://foo.example'
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
Access-Control-Request-Headers: Authorization, content-type
Access-Control-Allow-Max-Age: 86400
In the code above, the server is responding, Yes I see you’re coming from the origin, http://foo.example. And I am allowing not only POST requests but some others too (PUT, GET, OPTIONS). It is absolutely fine to send me the authorization headers that you send be before (in the request call), you can send me a request after some time (max-age).
// Main Request to server
Origin: http: '//foo.example'
Access-Control-Allow-Methods: POST
Access-Control-Request-Headers: Authorization, content-type
Authorization: Bearer QjrxQEdvanj0mv5Wx3xSAnO0mv5Wx3
Content-type: application/json
CSRF handling in web framework React?¶
React as a framework doesn't provide any inbuilt CSRF protection security. We can use any one of the approaches to handle CSRF attacks. But, React provides good protection for XSS. This becomes an additional layer in the CSRF case as well than a direct web application created in vanilla js application.
Clickjacking¶
What is Clickjacking attack?¶
This attack falls under the category of UI redress attack, For example, a user may be attracted by a website that promises them an exceptional prize. When the user clicks a button to accept the prize, their click is instead used to purchase an item on an e-commerce website. Typically this attack is performed by hiding the target website's UI and arranging the visible UI so that the user isn't aware of clicking the target website.
The clickjacking and CSRF attacks mechanism might look the same but they are different. In the CSRF case attacker builds an HTTP request and exploits the user session to send it to the server. On the other side in clickjacking user is directly interacting with the target website.
How does clickjacking works?¶
Clickjacking attacks use CSS to create and manipulate layers. The attacker incorporates the target website as an iframe layer overlaid on the decoy website. An example using the style tag and parameters is as follows:
<head>
<style>
#target {
position:relative;
width:128px;
height:128px;
opacity:0.00001;
z-index:2;
}
#decoy {
position:absolute;
width:300px;
height:400px;
z-index:1;
}
</style>
</head>
...
<body>
<div id="decoy">
...decoy web content here...
</div>
<iframe id="target" src="https://vulnerable-website.com">
</iframe>
</body>
How to defend Clickjacking?¶
A good approach to prevent clickjacking attacks is to ask the browser to block any attempt to load the web application within an iframe. We can do this using the HTTP response header X-Frame-Options
. With this header set while downloading the document browser will know it is not allowed to put an iframe inside the webpage. This header only applies to the top level frame. Ex, type of options available for the header:
Clickjacking handling in web framework React?¶
The combination of solutions discussed in clickjacking and also in CSRF will be enough to handle this problem in React-based web applications.
Third party assets¶
Above are the examples of the third-party code that we usually include in our web application and these could bring some possible threats to the application. Because we have to understand that people who manage these dependencies can also make mistakes even if the library/code comes from a big tech company, we should not blindly trust these. We can deal with this problem in the following ways:
- Reproducible builds, with a lockfile
- Use LTS versions where you care less about bleeding-edge features
- Tests that assert only expected requests are sent out
CDNS¶
If you must use a CDN like this, be aware you’re trusting the owners a lot. There could be a possibility of mistakes/bugs in their code.
Using Subsource Integrity attributes on <script>
and <link>
tags should be a better practice while including CDN into our application. Ex:
The above SHA code is like a checksum of the contents of that file. If the contents of that file change the browser will refuse to load the file.
Conclusion¶
Ultimately, web application security is a vast topic, and as technology continues to grow the possibility of potential attacks also increases. Being said that, XSS and CSRF are the major contributing attacks in web applications and other large sets of attacks need dependency of XSS and CSRF on a small or large scale. To minimize these security risks we should always follow the right steps in terms of architecture, coding practices, continuous code review, etc. These key factors only determine the secure state of our web application.