📜 ⬆️ ⬇️

Basic knowledge of site security

Hi, Habr!

Security is a serious matter. And often problems in this area surface unexpectedly and have extremely unpleasant consequences. Therefore, knowledge in this topic is extremely important for each web developer.

I will make a reservation right away - I am far from a pro, but I strive for this. Therefore, I will be glad to criticism, but only objectively. This material is for beginners who want to increase their professionalism and value as a specialist.
')
And yet, I show the most simple implementation of the code. I know about exceptions, I know about ORM, about the provided protection in frameworks. My goal is to show clearly, so that everyone understands.

And so, it's time to finish with the introduction and start the practice.

The path from the implementation of a novice to somehow sane result


I'm not used to working with theory. My soul craves practice. Therefore, speaking of the topic of security, we will consider almost all types of attacks from a practical point of view - how to implement and how to defend. Yes, you can say that teaching hacking is not good, but, not knowing how the attack is going on, we cannot build a competent defense.

Xss


Okay, the first type of attack is XSS. Yes, the good old XSS, about which everyone has heard. XSS (Cross Site Scripting) is a type of attack that targets site visitors. How it happens: through the entry field, the attacker writes malicious code that enters the database and does its work. Usually, cookies are stolen from users in this way, which allows you to log into their accounts without a password and login.

We are implementing a more innocent example.

Our developer made a simple form to add comments:

Index.php file
<?php $opt = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); $query = $pdo->prepare("SELECT * FROM `comments`"); $query->execute(); $comments = $query->fetchAll(); if ($_POST) { $username = trim($_POST['name']); $comment = trim($_POST['comment']); $query = $pdo->prepare("INSERT INTO `comments` (`username`,`message`) VALUES ('$username', '$comment')"); $query->execute(); if ($query) { echo ' !'; header("Location: index.php"); } else { echo ' !'; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>XSS</title> </head> <body> <form method="POST" class="addComment"> <input type="text" name="name" placeholder="Username"> <textarea name="comment"></textarea> <input type="submit" value=" "> </form> <div class="h2"></div> <div class="comments"> <?php if ($comments): foreach ($comments as $comment):?> <div class="comment"> <div class="comment_username"><?php echo $comment['username'];?></div> <div lass="comment_comment"><?php echo $comment['message'];?></div> </div> <?php endforeach;?> <?php else:?> <div class="no_comments"> </div> <?php endif;?> </div> </body> </html> 

The code is very simple and does not need to be explained.

There is an attacker - John. John got bored and he came across the site of our developer.
John writes in the form of a message:

 <script>document.body.style.backgroundColor = "#000";</script> 

And now all users of the site have a black background. John is pleased, and the developer has received experience and reprimand.

What happened at all?

John added a comment with javascript code. When displaying data on the page, the text comment is converted to html code. Html code, seeing the use of the script tag, added it to the markup, and the interpreter has already executed the JavaScript code. That is, John simply added his piece of js code to the existing site code.

How are we going to fix it?

To correct this misunderstanding, the htmlspecialcars function was created. The essence of her work is that she replaces characters such as quotes and brackets with special characters. For example, the character "<" will be replaced with the corresponding character code. With this function, we process the data from the form and now John’s js code can no longer harm our site. Of course, if this is the only form on the site.

Changes in the code will look like this:

Index.php file
 <?php if ($_POST) { $username = htmlspecialchars(trim($_POST['name'])); $comment = htmlspecialchars(trim($_POST['comment'])); /// } 


SQL Injection


Another one of the most common types of attacks, which have already begun to be forgotten. They forget because there are prepared requests and frameworks.

Just about the prepared requests we will talk.

What is the essence of the attack: the attacker enters into the input field part of the SQL query and sends the form. During the execution of the query, the received data is added to the database. But since the code is in the text, when adding records, it modifies the logic of our script.

Ok, let's simulate the situation. Our developer defended the form of XSS attacks. And John continues to use his knowledge, pointing out the flaws to the unfortunate developer.

That is, we will continue to work with the same form for adding comments.
Let's make a few changes:

1) Premoderation of comments.

The bottom line is that the page will display only those comments that are approved by the moderator. Pre-moderation is implemented in the simplest form, so as not to be distracted from the main part of the article.

To implement the idea, add the “is_moderate” field to the table with comments, which will take two values ​​- “1” (display a comment) or “0” (not display). By default, of course, "0".

2) Change the query.

This is needed for greater visibility. Let the request for adding comments will look like this:

 "INSERT INTO `comments` SET `username`='$username', `message`='$comment'" 

Now the form work code looks like this:

Index.php file
 <?php $opt = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); $query = $pdo->prepare("SELECT * FROM `comments` WHERE `is_moderate`='1'"); $query->execute(); $comments = $query->fetchAll(); if ($_POST) { $username = htmlspecialchars(trim($_POST['name'])); $comment = htmlspecialchars(trim($_POST['comment'])); $query = $pdo->prepare("INSERT INTO `comments` SET `username`='$username', `message`='$comment'"); $query->execute(); if ($query) { echo ' !'; } else { echo ' !'; } } ?> 


Okay, John comes to the site and, seeing that the comments have started to pass moderation, decided to make fun of the developer. Moreover, the XSS attack form now successfully reflects and the possibility of having fun, John has already deprived. He leaves a comment of this type: "LOL ', is_moderate =' 1" and bypasses moderation.

Why?

When John's comment is inserted into our query, the quotes are broken. That is, as mentioned above, John was able to execute arbitrary SQL code.

When executing a query with a John comment, the query looks like this:

 "INSERT INTO `comments` SET `username`='John', `message`='LOL', `is_moderate`='1'" 

At what SQL Injection can be implemented not only when sending the form. This can also occur when retrieving records by their identifier, processing the search form and other not so obvious situations.

How to fix?

The method of solving the problem has been known for quite some time - prepared queries. Prepared requests are requests that are specially processed before execution. Handling is shielding extra quotes. You must have heard of such a function. In PHP, it is implemented like this: "\ '".

The most popular solution is PDO. PDO is an interface for working with a database. With that quite a convenient interface. Only need to use them correctly.

PDO provides the ability to use masks and placeholders to implement prepared queries.

Then our query when using masks will look like this:

Index.php file
 <?php $query = $pdo->prepare("INSERT INTO `comments` SET `username`=:username, `message`=:comment"); $params = ['username' => $username,'comment' => $comment]; $query->execute($params); 


And when using placeholders like this:

Index.php file
 <?php $query = $pdo->prepare("INSERT INTO `comments` SET `username`=?, `message`=?"); $params = [$username,$comment]; $query->execute($params); 


Now the attack of John ceases to be relevant. At least for this form.

By the way, the moderation implemented by us, even in this form, already protects against another type of attack - SPAM. We all heard about him. SPAM is the sending of any messages in which the attackers use their knowledge of social engineering. Now the only one who will be attacked is a moderator. And even then, if he is not so stupid, he will either remove the garbage from the database, or, if he is lazy, rejects the publication and that's it.

CSRF attack


CSRF - Cross-Site Request Forgery. Dangerous because very few people know about him. Although it is quite simple to do this.

How does it happen: an attacker from another site forges a form and forces the victim to go on this form. That is, a POST request is sent. This way the HTTP request is faked and a malicious action is performed on the victim's website.

For example, an attacker could send your friend a letter to VC on your behalf, and you will not know about it.

Sounds slightly confused. I propose to consider in practice.

Our developer has made a form, he is good. She already knows how to defend herself against XSS, SQL injection and keeps up the pressure of Spam. She looks like this:

File index.php on the developer’s site
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> </head> <body> <form action="action.php" method="POST"> <input type="text" name="username"> <textarea name="message"></textarea> <input type="submit" value=" "> </form> </body> </html> 


But John is not that simple. He gets the form code (just from the source code of the site in the browser) and adds the form to his site.

The index.php file on John’s site
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSRF</title> <style> form input[type=submit]{ padding: 15px; font-size: 20px; color: #fff; background: #f00; cursor: pointer; } </style> </head> <body> <form action="localhost/note/action.php" method="POST"> <input type="hidden" name="username" value="lol"> <input type="hidden" name="message" value="  !   !"> <input type="submit" value=" !"> </form> </body> </html> 


Please note that on John’s site the form processing location is a file from our developer’s site. So now any user who clicks on the button will send not good comments.

This is the CSRF attack. In the simplest version, of course.

The developer has problems again ...

How to fix the vulnerability?

At one point, the developer will nugglit what csrf is and the protection logic will be as follows: to protect, you need to create a csrf token (a set of letters and numbers) and hang it on a form. You also need to secure the same token to the user (for example, through a session). And then, when processing the form, compare these tokens. If you match - we can add a comment.

We realize this:

File index.php on the developer’s site
 <?php session_start(); $token = ''; if (function_exists('mcrypt_create_iv')) { $token = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); } else { $token = bin2hex(openssl_random_pseudo_bytes(32)); } $_SESSION['token'] = $token; ?> ... <form action="action.php" method="POST"> <input type="text" name="username"> <textarea name="message"></textarea> <input type="hidden" name="csrf_token" value="<?php echo $token;?>"> <input type="submit" value=" "> </form> 


Action.php file
 <?php session_start(); if ($_POST) { if ($_SESSION['token'] == $_POST['csrf_token']) { echo ' !'; } else { echo '!'; } } 


Brute Force and Publick Passwords


Perhaps the most famous type of attack. He heard him almost every first of the movies about hackers and the like.

What is the essence: there is a form for authorization in the admin panel of the site. I need a username and password that John does not know. But he has a file with popular usernames and passwords. And he happily runs to try them on our website.

What can a developer oppose? For example, the limit on the number of authorization attempts in a certain period of time.

Let the initial version of the authorization form look like this:

Index.php file
 <?php $opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); if (isset($_POST['autoriz'])) { $username = htmlspecialchars(trim($_POST['login'])); $password = htmlspecialchars(trim($_POST['password'])); $query = $pdo->prepare("SELECT * FROM `users` WHERE `username`=:username AND `password`=:password"); $query->execute(['username' => $username,'password' => $password]); $find_user = $query->fetchAll(); if ($find_user) { echo ' !'; } else { echo '  !'; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Brute Force  Public Passwords</title> </head> <body> <form method="POST"> <input type="text" name="login" placeholder="Login"> <input type="password" name="password" placeholder="Password"> <input type="submit" value="" name="autorize"> </form> </body> </html> 


How are we going to fix: the simplest option, as already mentioned, is a limit on the number of authorization attempts over a period of time.

To do this, we will add the user the current time value in cookies when trying to log in. And now, when trying to log in, we will look at it so that the user can log in no more than once every 5 seconds. Moreover, if you enter the password or login incorrectly, we will increase the timeout to the next attempt by 5 seconds.

Index.php file
 <?php $count_next_minit = $_COOKIE['count_try'] ? $_COOKIE['count_try'] : 1; $seconds_to_new_try = 5; $opt = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]; $pdo = new PDO("mysql:host=localhost;dbname=".$db,$user,$pass,$opt); $pdo->exec("SET CHARSET utf8"); if (isset($_POST['autorize'])) { if ($_COOKIE['last_try']) { if ($_COOKIE['last_try'] < time() - $seconds_to_new_try * $count_next_minit) { $username = htmlspecialchars(trim($_POST['login'])); $password = htmlspecialchars(trim($_POST['password'])); $query = $pdo->prepare("SELECT * FROM `users` WHERE `username`=:username AND `password`=:password"); $query->execute(['username'=>$username,'password'=>$password]); $find_user = $query->fetchAll(); setcookie('last_try', time(), time() + 3600); if ($_COOKIE['count_try']) { $old_value = (int)$_COOKIE['count_try']; setcookie('count_try', $old_value + 1, time() + 3600); } else { setcookie('count_try', 1, time() + 3600); } if ($find_user) { var_dump(' !'); } else { var_dump('  !'); } }else{ var_dump('   !    ' . $seconds_to_new_try * $count_next_minit . ' '); } }else{ setcookie('last_try', time(), time() + 3600); } } ?> 


Backtrace


Backtrace is a method of attack through displayed system errors. This is both MySQL and PHP.

For example, John entered a wrong url and brought out an error that there is no record in the database with such an id (if the record is received via id from the address bar - site.ru/article?id=12). There are even the so-called "Dorks" - certain patterns of addresses of sites, when entering at which the user sees errors. And this opens up an opportunity for John to use the bot to go through this list of addresses and try to find this vulnerability on your website.

How to fix? In this case, we can do without examples, because the problem is solved simply by closing the error output. Usually this implements hosting, providing you in the admin panel to write logs to a separate file, but it will not be superfluous to limit the output of errors yourself.

This can be done with the error_reporting () function.

Among the arguments that it takes are: E_ERROR, E_WARNING, E_PARSE, E_NOTICE, E_ALL. The names speak for themselves.

For example, if you use error_reporting (E_NOTICE), then all errors will be hidden except for errors like Notice (warnings, for example, that there is no data in the $ _POST array).
To disable the output of all errors (which we actually need), you need to use this function as follows: error_reporting (0)

Logical errors


Logical errors are one of the worst. Because it is a mistake due to inattention. They float unexpectedly and, at times, we do not even guess where the root of the problem lies. These are errors of the site’s logic.

Well, for example, you could forget to put a check on the availability of authorization data in the session and cookies for one of the pages of the admin panel. So they opened access to this page for any user.

In this case, only one will save you - while writing the program code, think about how it can be hacked.

DDOS


DOS - type of attacks on equipment, in particular a computer. The attack is aimed at disabling the machine due to overload. DDOS differs only in the fact that more computers are involved in the attack.

That is, John calls his friends and they begin to send inquiries to the site. What does the botnet be appropriate for. A botnet is a set of infected computers, the peculiarity is that an attacker can control their work to some extent (launch processes, etc.). They send so many requests that the server does not withstand the load and, at best, starts to work very slowly, or refuses for an unspecified amount of time.

Protection against such attacks is provided either by the hosting companies themselves or by special services like Cloudflare.

How protection works: Cloudflare service provides you with its own DNS servers through which traffic will pass. There it is filtered, passing through algorithms known only to the owners and developers of the service. After that, the user is already on your site. Yes, of course, there is server operation, page generation and so on, but this is not about us now.
Moreover, speaking of DDOS, it is impossible not to mention the blocking of IP addresses, which Cloudflare also provides.

Yes, all this will not give a 100% guarantee of protection, but at times will increase the chances of your site to stay afloat at the time of the attack.

Mitm


Man In The Middle is a type of attack when an attacker intercepts your packets and substitutes them. We all heard that data is transmitted over the network in packets. So, when using the http protocol, the data is transmitted in the usual, unencrypted form.

For example, you write a friend "hello", and he gets "send me money, here's a wallet."

The https protocol was created to solve this problem. When it is used, the data will be encrypted and John will not be able to do anything with the received traffic.
And to get the https protocol for the site, you need to get an SSL certificate. Well, or TLS. In general, TLS is essentially an SSL receiver, because it is based on SSL 3.0. In their work there are no significant differences.

SSL certificate provides the same Cloudflare, and for free.

Backdoor


A little more theory. For this type of attack can be implemented in many ways and you just need to grasp the point. A backdoor is a type of hidden attack in which the script itself does something in the “background”. Most often these are plugins or WordPress themes downloaded from a torrent. The plugin / theme itself will work quite adequately, but a certain piece of the script written in the plugin / theme code will do something in secret. The same SPAM, for example. This is the reason for all cautions about the undesirability of downloading files from the torrent.

Finally


Yes, of course, this is not the whole range of attacks. But this knowledge will already increase the safety of your projects.

Source: https://habr.com/ru/post/451420/


All Articles