Content posted here with the permission of the author, who is currently employed at Josh Software. Original post available here.
While developing web apps, how frequently do you think about securing it?
Just wait for a moment and think!
You do? Really? That’s Awesome!
This blog will act like a checklist for you. Go through it once, to figure out just in case you have missed anything OR maybe I might have missed something that you have taken care of. Either way, please be kind enough to drop a comment.
You don’t? No worries, let’s do it together. Let’s understand:
- What are the various types of attacks?
- How do they work?
- How to secure your app from such attacks?
Alright, without any further ado, let’s get started.
Cross-Site Request Forgery (CSRF)
It’s a kind of attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. These attacks target state-changing request, not theft of data.
Consider a scenario wherein a genuine admin user logs in to your web app (http://www.genuine-domain.com). When login form is submitted, server validates the credentials and sends a cookie in response. Now, for each subsequent request to your domain, browser will automatically send along this cookie. Server will verify the existence and contents of the cookie before considering the request authenticated.
All sounds good? Yeah? Think again!
Your web app is vulnerable to CSRF attacks right now. How?
Let’s say the admin user is authorized to delete some other user’s account. Also assume that URL for such endpoint is accessible over GET request. Now, open a new tab, type this URL and press enter. Voila! you just deleted a user.
Now think, how difficult is it for a malicious user to trick your customer into clicking such a URL? Not very difficult? Right?
Just by following certain basic rules, you can protect your app from such attacks:
- Show a confirmation prompt before every destructive action
- Use GET verb for read-only requests. Use POST/PUT/PATCH/DELETE for write operations.
How do non-get verbs help? Did you notice that only URLs exposed over GET verb can be opened from browser’s URL bar (and hence the links). By using non-get verbs, you are making the hackers job a little more difficult.
The hacker may still trick your customer into submitting a form, which would trigger a POST/PATCH/PUT/DELETE request.
We can step-up our security by including a one-time disposable secure token in every form rendered by the server. Now, if a form is submitted without a token OR with an invalid token, the server would simply reject it. Only our server and genuine client (browser tab) knows about this token. Additionally, verify the Origin and Referrer header whenever possible. Please note that Origin header may not always be present (specially in GET requests). Similarly, Referrer header is not guaranteed to be present in every request.
The attacker can also modify the contents of the cookie OR just copy it. To mitigate this risk, your web app needs to ensure the following while setting the cookie:
- User “session” cookies – they are deleted when browser/client is shut down.
- Turn on the “secure” flag on the cookie – only accessible over HTTPS.
An attacker can always intercept your network requests and get access to sensitive information. To secure your data during transit, we need to encrypt it. This can be done by using HTTPS certificates.
However, using HTTPS alone is not sufficient. HTTPS encrypts data using some ciphers. You encryption is only as strong as your ciphers. Older version of HTTPS are known to have vulnerabilities. Therefore, I urge you to use HTTP Strict Transport Security (HSTS).
CORS (Cross-Origin Resource Sharing)
Similar to HTML requests, we also need to protect our web app from AJAX based security threats. AJAX calls can be triggered either from your domain OR from a different domain. Luckily, most modern browsers block cross-domain AJAX calls by default. But, what if a genuine user wants to share resources across the domains?
Secure inter-domain AJAX based communication is handled by using CORS (Cross Origin Request Sharing). Here is an excellent blog on implementing CORS.
NOTE: CORS restrictions are only applicable on AJAX requests (i.e. not applicable on HTML requests).
Clickjacking (User Interface redress attack OR UI redress attack OR UI redressing) is a malicious technique of tricking a Web user into clicking on something different from what the user perceives they are clicking on, thus potentially revealing confidential information or taking control of their computer while clicking on seemingly harmless web pages.
In order to secure your web app from clickjacking attacks, the server needs to set X-Frame-Options: DENY. This means, your web app cannot be opened inside an iframe.
If you want to allow rendering of your app inside an iframe from your domain only, you can set:
X-Frame-Options: ALLOW-FROM <whitelisted-domain>
A SQL injection attack consists of insertion or “injection” of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data (Insert/Update/Delete), execute administration operations on the database (such as shutdown the DBMS), recover the content of a given file present on the DBMS file system and in some cases issue commands to the operating system.
Bind variables are the best way to prevent SQL injection. When using bind parameters you do not write the actual values but instead insert placeholders into the SQL statement. That way the statements do not change when executing them with different values. The question mark (
?) is the only placeholder character that the SQL standard defines.
Cross-Site Scripting (XSS)
Cross-Site Scripting, commonly known as XSS, is a vulnerability that is often found in web apps. XSS allows attackers to inject client-side scripts into public facing web pages and, in many cases, can be used by attackers to work their way past access controls.
To protect your web app from XSS attacks, you need to:
- Never trust user input. i.e. Always escape/sanitize input before inserting into:
- HTML element’s content. e.g. innerHTML, $(‘#some-id’).html(), etc.
- Values of common attributes. e.g. class, data-*, etc.
- HTML or JS comments
- Style property values
- URL parameters
- Whenever possible use an Auto-Escaping Template System
- Avoid eval() or such dynamic evaluation methods
- Implement CSP
CSP makes it possible for web app developers to specify the domains that the browser should consider to be valid sources of executable scripts. A CSP compatible browser will then only execute scripts loaded in source files received from those whitelisted domains, ignoring all other script (including inline scripts and event-handling HTML attributes).
e.g. The following header instructs the CSP compatible browser to only allow scripts from site’s own origin. This excludes the sub-domains.
Content-Security-Policy: default-src ‘self’
Event after implementing all the above mentioned security measures, your web app is still not 100% secure. That’s when I say: Security is a myth.
Despite that, I recommend following ALL the above mentioned precautions to protect your web app. That’s because something is better than nothing and this is certainly more than something ;). If nothing else, at least this will discourage attackers from choosing you as a target.
Finally, a sincere appeal to all:
Implement all these security measures together. Don’t pick n choose.