Web security: the must-know for developers
Leaving a huge security hole is one of the worst things you can do. More than ever, developers know almost nothing about it. Yet they are on the front line against gifted, reactive, and surgical hackers.
Even with the automatic protections of modern browsers and frameworks, sites are constantly being hacked. It is very easy to produce a security flaw.
And it’s not the tool’s fault when you don’t know what you’re doing.
This article is addressed to you, web developer of the World Wide Web. We are not going to deal with the security part of the web server. This part is more for SysAdmins and DevOps (or DevSecOps if you prefer).
Today, we are talking about securing your web application.
We want to make it extremely difficult for hackers to get too close to you and your site.
And I emphasize the expression “make it extremely difficult“. In the world of cybersecurity, anything – or almost anything – can be hacked. Provided you put the means to do so.
That said, unless you run a government site, understanding the main attack vectors will protect you against 99% of the threats that concern you.
Despite the fact that the main attacks have not changed for years, they are still deadly. The statistics are depressing. Developers are still making the same mistakes.
Let’s make sure you’re not part of the statistics.
One of my first missions during my first internship as a developer was the construction of a small admin area. A very small PHP page to manipulate the database. Perfect to start smoothly.
So I’m doing it. I quickly made a functional admin that I immediately send to my superior. In this company, everyone hosted their work on a web server on their machine. With the right local IP, you could access everyone’s local work.
My superior then calls me to his office. He tells me that my admin isn’t working. I look at his screen and i’m horrified. Not only updates does not work anymore, but apparently the database is empty!
When my superior asked me for an explanation, I didn’t know what to tell him.
When he saw my face, he immediately started laughing like a seal. He had just made a SQL injection attack via the form of my admin.
But what is it and how does it work?
The reason I’m starting with this is that despite the fact that almost everyone has heard about it, it’s still the most exploited. It’s this security hole that developers leave the most! It’s by far the most dangerous even today.
And it’s not me saying it, but the very serious OWASP.
I show you in PHP, but the problem is the same for all languages. Typically what you don’t want to do looks like this.
<?php $mysqli = new mysqli("localhost", "username", "username", "dbname"); $sql = "SELECT * FROM users WHERE email='" . $email . "' AND encrypted_password='" . $password . "'"; $result = $mysqli->query($sql); $mysqli->close(); ?>
This is evil because the variables (line 4) are passed directly into the query without escaping the SQL control characters. To better understand this sentence, let’s take a closer look at what happens during the attack.
A normal user will enter his login and password in your form this way:
And it will generate this SQL query.
-- SELECT * FROM users WHERE email='[email protected]' AND encrypted_password='password' --
And in this scenario, everything is fine. The user is authenticated if the login and password are correct. That’s what we want.
A user who wants to hurt you is going to do things differently.
In the login form he will try to modify your SQL query.
And without protection he will easily succeed.
Generating this SQL query.
-- SELECT * FROM users WHERE email='[email protected]'--' AND encrypted_password='password' --
The control character ‘ closes the where condition. The control character — ignores the rest of the query. And since control characters are not escaped, SQL interprets them as valid commands!
The attacker has just logged on to your site – instead of someone else – without needing a password.
In general, it doesn’t stop there.
-- SELECT * FROM users WHERE email='[email protected]';DROP TABLE users;--' AND encrypted_password='password' --
This will nicely delete your user database.
To avoid this hell, you just have to “escape” the SQL control characters of the variables. Concretely this means that SQL will consider them as strings, not as SQL control characters. And to do that, you have to use the functions built into your language.
<?php $mysqli = new mysqli("localhost", "username", "username", "dbname"); $email = $mysqli->real_escape_string($email); $password = $mysqli->real_escape_string($password); $sql = "SELECT * FROM users WHERE email='" . $email . "' AND encrypted_password='" . $password . "'"; $result = $mysqli->query($sql); $mysqli->close(); ?>
Again here it’s PHP, but you have integrated character escape systems in all languages/frameworks possible!
This injection is the most common, but there are other types of code injection.
Cross-site scripting (XSS)
During the night of Saturday 22nd to Sunday 23rd July 2015, the site jeuxvideo.com was not at all in its normal state. Most of the pages were filled with parodic, even pornographic images. This huge French video game site was the victim of a simple and common attack.
At that time, anyone posting a comment on the site could trigger it. Everyone was having fun. The devs in charge of closing the issue must have had a bad time.
To understand what happened, let’s put ourselves in a situation.
We are talking about an XSS attack. And more precisely, for the case of jeuxvideo.com, a stored XSS attack.
If you give the power to users of your site to post content, for example via a comment section, you expose yourself to this vulnerability. As before, let’s look at the attack in action to better understand it.
Let’s take the example of Marc. Marc wants to express his love for your site. He will enter a comment in your comment form and trigger a simple and safe flow of action:
This will store it in the site’s database. The page is refreshed and his comment -now stored in the database- will be displayed for everyone on the page.
Again, a user who wants to hurt you is going to do things differently.
This is the case of Darlene. She wants to test whether or not you have protected yourself against XSS.
Darlene realizes that you didn’t put any protection on. She is very disappointed in your work.
No need to explain the devastating potential here. From session stealing to redirecting to fake sites for phishing. It’s a door open to every window.
This is what one of the attacks made on the site jeuxvideos.com during this famous party looked like.
For the reflected XSS attack, it is the same principle. Except that the script is not stored in the database. It is passed directly into the URL.
Imagine that your site is searchable. This search passes an HTTP GET request and a URL parameter. In this page you display the result of the search, but also the search term.
In an ideal world, it happens this way on your site.
During a deliberate XSS attack, the attacker will attempt to inject site directly into the request this way.
The attacker only has to send the link -with script injection in the url- to someone. The person clicks on it and gets attacked via your site. Usually, the attacker will use url minification sites like bit.ly to hide his attack a little bit in the base link.
And to give you an idea of the creativity of hackers around this attack, here is a list of XSS attacks listed by OWASP. They have a lot of imagination. We need a global solution.
To protect against this attack, the main solution is to escape any HTML tags that might come from the client. See to remove all tags altogether when possible.
Everything that comes from the client must be treated as text, otherwise it’s a disaster.
You can also look at Content-Security-Policy to prohibit any inline script that doesn’t come from your own domain. But this is an extra measure. It’s not a replacement for the first one.
The more protection you have, the better.
There is a last type of XSS injection via the DOM. I decided not to tell you about it here because it is not on the podium. On the other hand, I have a recommendation for you at the end of the article with all the possible attacks in it.
Before that, let’s move on to the third most common attack on Internets.
We finish on the famous cross-site request forgery (CRSF) attack.
This time the principle is not to inject code but to make HTTP requests instead of the victim. By going through a third party site, the attacker can force a victim to make requests on your site. And the victim in question is not even aware!
Last little drawing to explain all this.
- 1 : An Internet user visits a site held by a hacker. On this site, HTTP requests made by the attacker are in place. It can be any HTTP verb. The most common with this attack are GET, POST and PUT.
- 2 : These requests are then launched on your site, the target of the attack. Involuntarily, it is the visitor -via the hacker’s website- who makes requests to you.
- 3 : These requests then arrive on your site, and without protection, they will cause actions. The seriousness of these actions will depend on what it is possible to do on your site with HTTP requests.
You can trust the hacker in question to find interesting things to do. They’re pros at hacking where it hurts. They do it with a lot of discretion.
The first thing to do is to follow the REST principles. Under no circumstances should a GET changes data. It’s a hell of a thing to do. You’ll avoid a whole lot of CRSF attacks not doing it.
For other HTTP verbs, it requires a little more work.
First, you need to implement anti-CRSF cookies. You’ve probably seen this kind of thing before when inspecting form code on websites. This is what it looks like.
<form action="/update_profile" method="POST"> <input type="text" name="name" /> <input type="submit" value="Submit" /> <!-- Anti-CRSF --> <input type="hidden" name="token_csrf" value="48rtyu9962dd4s3assa" /> </form>
The CRSF token you see is randomly generated by the web server and embedded in the form at the time of its creation. When the form is submitted, if the web server does not recognize the token, the request is denied. This is effective in blocking most attacks. But it’s not enough. We need to add a last protection.
Make sure that all your queries have the SameSite: Strict or SameSite: Lax attribute in the Set-Cookie. Most recent browsers are in basic Lax, but not all of them. And especially older browsers are not protected at all. This cookie setting will allow you to restrict that it can make queries on your domain.
- In Lax, only GET queries from other domains can make queries to you. And it’s not a problem if you respect the REST principles as seen above.
- In Strict, no queries that do not come from your own domain can make queries to you.
You can imagine that there is more threats than thaton the Internet. And if security is an important topic for you and your client, you need to be prepared for anything.
This is where my recommendation of the day comes in: Web Security for Developers: Real Threats, Practical Defense.
From the introduction, the author gets you to attack your own site. He explains you how to do it with a “penetration test”. It’s incredibly simple and quick to do.
I was surprised to see that the blog you’re reading right now had a small hole in it that I closed in a minute.
Then the first part explains how the Internet, queries, headers, cookies, packets, browser and web servers work. Indispensable so you don’t get lost if all this is new. A necessary memory refresh for the most experienced ones.
The second part is dense. In this article, I talk about the three biggest attacks on the internet. It’s really just the indispensable part I’m talking about.
This book explains in detail twelve more possible attacks against your site.
I knew there was a lot of danger. But not that much.
There’s even a more organizational – almost psychological – part that explains how you yourself can be a tool for some hackers. One of the most interesting parts of this book in my opinion.
It is intended for all web developers, regardless of their level. It starts from the base and will progressively explain everything to you. Obviously, it will be more useful for developers with a need for security in their applications. But don’t all applications have a security need?
It’s up to you to answer this question.
You will never be able to protect yourself against 100% attacks. Hackers are far too talented. But making life difficult for them will greatly limit the danger. The longer and more complex it takes to compromise your site, the less trouble you’ll have to worry about.