Security of web applications: fight against oneself, or draw a line of adequacy

How secure is the application? For some, this question does not make sense. “As much as possible.” The safer, the better. ” But this is not an exhaustive answer. And it does not help to formulate a security policy in the project. Moreover, if we only adhere to this directive (“the more security, the better”), we can render a disservice to ourselves. Why? The answer is under the cut.

Security conflicts with usability

Excess security checks make the application less convenient. This mainly affects 2 parts of the application: authentication and password recovery.

Multifactor authentication, which involves forcing SMS and entering additional data in addition to the password, makes the life of users a little more safe, but less happy. And if the whole point of your service is that there you can exchange pictures with seals, then the user can not appreciate your eagerness to make his experience safer.

Best Security Practices recommend that in case of authentication errors, give the user as little information as possible about where the error could have occurred, so that the attacker could not collect the user database. According to these recommendations, if the visitor of the site went through 33 authentication steps and made a typo in one of the fields, the best security solution would be to write a neutral message: “Sorry, something went wrong, please try again.” Thanks to developers and sincere admiration for the noble intentions of the site’s security system are not the emotions that the user will have in this case.

It is necessary to clearly understand in what cases and how much the user’s experience deteriorates when implementing certain security practices. And decide whether it is for your particular application.

Security makes the application more difficult to develop and maintain

The more security the application implements, the more complex it is to develop. The length of writing individual functionality can grow several times for the sake of minor security improvements.

Many activities can be spent not on actually protecting against vulnerabilities, but on making the life of cybercriminals more unhappy. As an extreme example of this case, one can result in obfuscation of the name of the REST API methods and their parameters.

Often, developers take a lot of time to ensure that an attacker can not collect a user name database through a login form, registration or password recovery.

You can come across approaches when an application marks a user as an intruder, but does not report it. User requests are simply ignored.

If multifactor authentication includes a “secret question” that is unique to each user, then for an existing user name the application can still display a security question. Moreover, a web application can store in the session or in the database the name of this non-existent user and the question that was first displayed for him to consistently ask for the same information.

There are hundreds of other ways to confuse the intruder. But, of course, they require time for implementation. These mechanisms can be quite complex to understand and support, even with good code and accompanying comments. But the most important thing is that these things do not cover any vulnerabilities, but are done solely to complicate the search for these most vulnerabilities.

It’s not always easy to separate the “competently designed and safe functionality” from “Crazy mind games with an imaginary hacker”. Moreover, this border is floating depending on how much your application attracts potential attackers.

Security makes the application more difficult to test

All our rules and checks should be tested. Unit tests, integration tests, manual testing – we need to decide which approach will be used for each specific security mechanism.

We just can not test. Because errors creep in the code. And even if we originally wrote everything without errors, then in the process of support, modification, refactoring – errors necessarily appear. Nobody immediately writes a legacy code. It becomes a legacy code gradually.

It will not be entirely reasonable if the business functionality undergoes rigorous testing, but security mechanisms are considered inviolable, flawless and absolute.

If this is all tested manually, the question is how often. If we have a more or less complex web application that uses the REST API, then it potentially has dozens, if not hundreds, of places where a broken authentication vulnerability can creep in. A typical example of a vulnerability is the situation where, in an HTTP request, we change some parameter (for example, the user ID) to another, and we receive information that we should not have access to. Checking every such case is a titanic work. Do I have to deal with this before every major release? Is it necessary to allocate a separate person for this? Or a separate team?

These questions are important, because broken authentication is very easy to admit. With any change in the model, any new REST method should always think about whether this vulnerability will appear. There is no universal and simple solution. But there are different approaches that allow the project to combat the problem consistently.

In addition to broken authentication, there are dozens of security issues that are also desirable to check. And, introducing new checks and mechanisms, we need to think about how this will be tested. Things that are not tested, in time, have the property of breaking down. And then we have not enough that there will be no normal security, so there will also be a false belief in what it is.

Special problems are delivered by 2 categories of protective mechanisms: those that are included only for production, and those that represent the protection of the second (third, fourth) level.

Protection, which is implemented only on production. Let’s say a session token cookie should have a tick https. But, if the test environment uses http, it means that there are separate configurations for testing and production. Therefore, not exactly what will be used is tested. And, if during migration or some changes this tick will cease to be shown, we can not notice it at once. How to solve this problem? Introducing another environment for pre-production? If so, which part of the functionality is tested on it?

Multi-level protection. People who are experienced in safety, like to create a defense, which can be checked only if the first barrier broke. The idea is reasonable. Even if an attacker found a vulnerability in the first echelon, he would be stuck on the 2nd. But how is it tested? A typical example is the use of different db users for different users of the application. Even if our REST contains broken authentication, the attacker will not be able to edit / delete the data, because his db user will not be right. Of course, these configurations become obsolete, break if they are not supported and not tested.

A lot of security can make the application less secure

The more security checks, the greater the complexity. The more complexity, the more likely to make a mistake. The more likely it is to make a mistake, the security is worse.

Take for example the login form again. Login with two fields (username and password) is easy to implement. It is only necessary to check whether there is such a user in the system and whether the password is correctly entered. Well, still it is necessary to check that the login / password goes in the body of the request, and not in the URL parameters. It is advisable to make sure that the application does not prompt where the error in the login or password is, so that it is impossible to compile a database with a list of users (this item can be ignored for some applications in the name of a better user experience). And we must make sure that the anti-bruteforce mechanism is implemented. Which, of course, must be resistant to fail-open vulnerability. Even better, if we do not show the attacker that we marked him as a “cracker” and start simply ignoring his requests – let him continue to think he is hacking the login. Yes, and do not forget to make sure that we do not write down the password sent anywhere in the logs. Well, and a few more subtleties, which must be remembered. In general, what could be simpler than the usual login form with two fields?

Another thing, multifactor authentication. Where to us send any token on mail or by SMS. Or where you need to enter some additional data. Or you have to go through a number of steps, gradually introducing information about yourself. All this is complicated. In theory, all these additional checks should reduce the likelihood of hacking. If everything is implemented correctly, then it really is. The probability of hacking remains (neither SMS, nor e-mail messages, nor anything else does not give 100% guarantee against hacking), but it goes down. But the already complicated logic of authentication becomes even more complicated, and it’s easier to make a mistake somewhere. The presence of at least one error or one incorrect assumption makes the whole model less secular than a simple form with 2 fields.

Moreover, intrusive and inconvenient security measures can force users to store their information less than secrets. For example, if you need to change the password every month on the corporate network, users who are completely unclear about these strange requirements can simply start writing their password on the sticker and glue it to the monitor. “This is a user problem if they allow such a silly mistake” – you can object. But no. This is our problem with you. After all, is it not for these very users that we make our applications?

Great. What do you suggest?

I suggest from the very beginning of the development to determine how far we are ready to go to deceive the attacker. Are we ready to optimize our login form so that the response time to the login request will never return, is there a user with that name or not? Are you ready to implement such authentication checks that even a friend of the victim of hacking with her phone in her hands and from her computer is not able to enter our application? Are we ready to complicate development at times, increase significantly the budget and development time, sacrifice a good user experience in order to make the life of an attacker a little more difficult?

You can endlessly work on security, raising new levels of protection, improving monitoring and analysis of user behavior, making it difficult to obtain information. There must be a line that separates us from what we need to do and what not to do. Of course, as the project progresses, this trait can be rethought and changed.

 

In the worst case, the project can spend a lot of resources building an impenetrable wall against one type of attack, when the application in another location has a huge security breach.

Making choices, whether we will work on this or that security mechanism, or whether we will set up another level of protection, we must take into account a number of factors:

  • How easy is vulnerability to exploit? Broken authentication is very easy to use and does not require technical background for this. Therefore, we must give considerable attention to this problem.
  • How critical is the vulnerability? If an attacker accesses information from other users or can change the information of other users, then obviously this is a very important problem. If an attacker can collect the ID of certain products in our system, but does not have the ability to do anything with these IDs – then the importance of the problem is several times lower.
  • How much will the security of the site increase if we implement it? If we are not talking about the first level of protection (for example, checking XSS problems on the intuition, when a good mechanism for sanitization of the outpost is already implemented), or it is just that to confuse the attacker more (instead of blocking his actions, we begin to ignore his requests, “pretending “that we did not recognize the attack), – the priority of these changes is not very high, if they should be realized at all.
  • How much will it take / will it cost?
  • How much will the user experience experience as a result of this change?
  • How difficult is it to support and test? It is common practice not to use code 403 when trying to access a denied resource, but always return 404 to make it difficult to collect information about existing identifiers. This solution, although it makes sense and does make the life of an attacker more difficult, at the same time can complicate testing, analysis of production errors and even affect the positive experience of users when, instead of telling them that they do not have enough rights to get it resource, we say that there is no such resource in principle.

Yes. In your particular case, complex multifactor authentication may be needed. But you should be aware of how much this hinders the support and development and how much this makes your application less enjoyable for users.

You justify disregard for safety

No way. There are security-sensitive applications where additional protection may be needed even to the detriment of user experience and development costs.

Well, in any case, there are a number of vulnerabilities that are unacceptable to have, regardless of what the application is intended for. CSRF is an example of such a vulnerability. The fight against it clearly does not make user experience worse and does not greatly complicate the development. Many server-side (for example, Spring MVC) and client-side (for example, Angular) frameworks make it easy to support CSRF tokens from the box. Further, using the same Spring MVC to add a few necessary header (Access-Control- * header, Content-Security-Policy) is also a fairly simple task.

Broken authentication, XSS, SQL injections and some other vulnerabilities can not be hosted in applications. Protection from them is easy to understand and is sorted out in numerous books and articles. The transmission of sensitive information in URL parameters or the storage of weakly cached passwords are here.

Ideally, the project should have a manifest that regulates security policy: what and how to protect, the description of the password policy that is tested, etc. This manifesto will be different for different projects. Projects where there is a insertion of user input into the operating system command will contain a description of the protection against injection OS commands. Projects where the user can upload their files to the server (including the avatar) require a description of the underlying vulnerabilities that may appear in this case.

Of course, it is not an easy task to write and maintain such a manifesto. But to hope that every member of the team (including testing and support teams) will remember what practices to adhere to in the project, and consistently and consistently implement them, is naive. In addition, the problem is that for many vulnerabilities, there are several options for prevention. And in the absence of a clearly defined policy, a situation may arise whereby one practice (for example, validation of input parameters) is applied to parts of the site, and in the other – the opposite (for example, the output parameter validation). In some places, sanitization will be used, and in part – the excitation of the validation error. And even if these different mechanisms are implemented very well, then the lack of consistency is the perfect ground for the appearance of bugs,

If the team is small and the technical leader is unchanged and has a developed policy regarding security, a permanent code review can be sufficient to avoid such problems even without the presence of a manifest.

Conclusions:

  • When working on security, you need to consider how sensitive the site is to security. Applications for banks and an application for the publication of anecdotes require different approaches.
  • When working on security, you need to consider how much the user experience will deteriorate as a result of this or that change.
  • When working on security, you need to consider how much this decision will make the code more difficult and support more painful.
  • Security mechanisms need to be tested.
  • In the team, it is desirable to introduce educational activities on security issues and / or to subject all work to a thorough security review.
  • There are vulnerabilities from which it is desirable to get rid of for web applications of any level: xss, xsrf, injections (including SQL), broken authentication, etc.

Leave a Reply