By Amitai B.
Sep 5, 2016
If you want your web application to be bullet proof with no vulnerabilities at all, do not write it.
There is no software that cannot be hacked; it is just a matter of resources and time, as proved many times.
There are, however, a few basic steps that will improve your web application security to a level that attacking it will be much harder and maybe not worthwhile for the hacker.
Even if you think that your application is not under the radar and no one would be interested in attacking it, know this: There are tools that scan the web and search for sites with known vulnerabilities. If your web application cannot defend itself from these common weaknesses, it will likely be attacked.
In this blog post, I will specify common vulnerabilities and ways you can defend yourself by using simple methods and technologies (with Rails).
CSRF – Cross-site request forgery
In this attack, the hacker executes commands on the application from a different place while relying on the fact that the user is logged-in to the application.
Let’s take the following example: You are logged in to your bank website and it lets you transfer money to a different account by sending a post request with the parameters on the amount and the other account number. After logging into the application, the hacker fools you into opening his/her site. He/she then sends you a post request from his/her page. Because the user is logged in, the request is accepted and the money is transferred.
Yikes! What to do?
A common way to handle this problem is by adding a security token to any request. The security token will be included on the real page of the application and will be sent in the request header. If the request header does not contain the right token, the server will reject the request.
In Rails this is done by the framework, the only thing that needs to be done is to add this line to the application_controller.rb:
protect_from_forgery with: :exception
Notice that if the script is running from the page itself (like XSS) it will be able to know the token and use it.
Another important thing is to add an expiration to this token so it will not be valid after a certain amount of time. So, it if is stolen, it will be useless.
XSS – Cross-Site Scripting
This is perhaps the most popular and harmful attack. It is very hard to defend yourself from it, sometimes impossible.
An XSS attack is when the hacker makes his/her code run inside your web application. The attacker has many options to do that. The most common one is when the input of the user is displayed in the output of the application.
For example, take a message in a forum or a user name for the web page.
When the hackers are able to run a script they can achieve a lot, such as stealing cookies, hijack ing the session, using the user’s credentials to perform actions in the application, and many other things, specially if the developer counts only on the security of the client. The reason is that if the attack is within the session many defenses such as CSRF are rendered useless.
There two types of XSS:
- Reflected (non-persistent): using the user input that is displayed back on the screen (like a search phrase) to run the script. E.g. insert the name:
- Persistent – the user input is saved in the database and then displayed (the script runs) to the user (or even other users) every time the user loads the page.
It is very important to know that XSS can happen on the page inputs but also in the tunnel (man in the middle attacks), so it is not enough to handle the inputs.
What to do? The first thing is to escape every input of the user. Escaping is done by removing or replacing any input that contains script characters like ‘<’.
Many frameworks have escaping mechanisms and sometimes they are even built in so the developer does not need to worry about them. If they are not, then it is very important to verify this, and read about the framework vulnerabilities before going to production.
Moreover, sometimes we prefer using other technologies instead of what the framework provides (for various reasons). Therefore, it is crucial to escape the inputs of those cases because no one is watching our backs.
The other side of this equation is to verify the outputs and escape them as well.
The second part of it is in the server. Do not, ever, base your defenses on the client. Use all that you can to add protection in the server:
- Check the user inputs.
- Verify his/her identity
- Verify that the action is reasonable for this user
Do not trust the users. One vicious user is enough to cause massive damage.
Sometimes the attacker can break our defences and get to the user’s table. Storing an encrypted password will prevent hackers from accessing user accounts. There are many tools and methods to do so. Devise is highly recommended.
Moreover, sometimes the attacker takes control of the user’s session, and can change passwords. A very easy way to prevent this is by asking for the current password before changing it.
Another thing is to use technologies like 2FA (two factor authentication), SMS or mail to verify the user’s identity.
SQL injection was very popular in the past, but it has changed because every experienced developer knows how to prevent it. The SQL injection works is when the attacker injects an SQL phrase into his/her input, and then penetrates to the application or collects data from it.
For example, adding to the password the phrase ‘or 1 = 1’, this will make a user authentication query be:
select user from users where user_name = ‘john’ and password = ‘password1’ or 1 =1
The best way to avoid this is to escape SQL in the user inputs and make where queries with parameters rather than integrate them in the query:
User.where(‘user_name = ? and password = ?’, ‘john, ‘password1).first
Many web applications ask for the user’s login name and password and then store it (usually in a cookie). Then they use the password in the session to identify the user for everything he/she does.
The problem is if the attacker seizes the cookie. In that case, he/she can hijack the session and use it because he/she has all the user’s permissions.
The way to prevent this is to use SSL, which will make it very hard for the attacker to eat (get) the cookie.
config.force_ssl = true
The next thing is to encrypt the cookie so that even if the attacker gets it, he/she cannot use it. The encryption in rails uses the key from config/secrets.yml file.
Defending web applications is hard work, but we cannot underestimate the risk to our applications or our customers if we ignore hacking threats. In order to get complete or almost complete defense, we need to invest in a lot of resources and even then we cannot be sure that we are 100% safe. We need to define the level of protection that will match the data we are trying to defend.
The good news is that with a few simple steps and by using frameworks like Rails, we can improve the level of security to the point that our system and applications will be very hard to attack or take down.
Security issues should be handled through the development life cycle in order to improve it, from the design phase to implementation, as well as code review and testing.
Remember, it takes one malicious user to destroy a good reputation.