During the pandemic, we are witnessing an unprecedented surge in cybercrime, so you need to take the right measures to make your web application secure. Hence, this article is a reminder for you about the mistakes you may make when building web apps, and some of the fallacies that you may fall victim to when thinking about security. It describes web application security best practices you can implement at different stages of the development process. And most importantly, they will be valid regardless of the language and framework you will be using.
When to think about web application security?
It may sound obvious, but the security of a web application doesn’t start somewhere in the middle of the development phase when we have the basic functionalities developed. It doesn’t even start with the beginning of coding. It starts much earlier, in the design or even at the estimation and bidding phase. Bear in mind that the potential consequences of negligence at this stage can be very serious. You can harm your brand’s reputation or, in the worst-case scenario, face financial penalties or legal action.
Let’s start with asking the right questions related to app security, as they will give you the answers you need.
What are your client requirements when it comes to app security?
Your client may already know what they need when it comes to application security. They may already have some tools in their ecosystem that you’ll need to integrate with. However, most often they have neither. Sometimes, they are not even aware that data and app security is something they need.
In such cases, it is your role to educate your clients and build their awareness. You can explain to them that it is not a question of whether a cyberattack occurs, but rather when. So, it is better to assume that your app will have to face this sometime during its lifecycle and implement appropriate security policies beforehand.
Additionally, there is plenty of evidence showing that cybercriminals have become even more active during the pandemic. In April 2020, the FBI was receiving between 3,000 and 4,000 cybercrime complaints daily compared to 1,000 before the outbreak of the COVID-19 pandemic.. And in July 2021, we witnessed a new kind of ransomware attack that hit many companies.
How will the application be used?
Will it be publicly accessible or intranet-based? Who are its end-users? Will it be under heavy load or not? Does the framework or application platform you are using implement any security concepts? Having answered all of this, you will be able to design the new app properly.
Which web application security tools will be used?
Are they free or do they require licensing? Are they easy to use or will you need to hire or train a dedicated developer? Will they integrate well with the client’s ecosystem? All of these questions will impact budget and team composition, so it’s good to answer them before the project begins.
Once all the above is defined, you need to include application security aspects in your budget. You need to emphasize to your potential clients how important app security is and that extra initial cost will help them avoid more serious problems, e.g., user data leakage that will harm the company’s credibility and can end up with financial penalties and legal action.
Now, let’s go through all the stages of app development and see what best practices a business analyst/UX designer, solution architect, tester, developer, and DevOps engineer should follow, and what should definitely be avoided.
I am a business analyst or a UX designer
There are plenty of ways in which a business analyst or UX designer can go wrong when (not) thinking about security. And not necessarily due to the lack of knowledge, but also because the clients themselves do not have awareness of how serious the problem may be.
Of course, an application needs to be easy to use and manage. But it does not mean that you need to compromise its security to meet these requirements. Do not be tempted to go for a shortcut, or at least make your client aware that it will make the app more vulnerable to security threats. Your clients may not be very tech-savvy, but it is your responsibility, as a tech person, to explain to them all the risks.
Here are a few real-life examples of the practices you should definitely avoid unless your client is fully aware of the risks involved and accepts them:
- Logging one user in the context of another one
- Permission elevation
- Accessing protected functionalities by an unauthorized (public) URL, even if it is obfuscated
- Accessing other users’ data by an unauthorized (public) URL, even if it is obfuscated
- Using passwords that are easy to guess (like birthdays) to “secure” data shared with users
- Combining high-responsibility administrative functions with an account for everyday use
- No protection against bots (captcha or similar), especially in public-facing applications
- No strong authentication mechanisms, preferably some sort of multi-factor authentication
- No intrusion detection mechanisms (IDS)
- No mechanisms for detecting or handling suspicious user behavior (multiple login errors, ID iteration in URLs, blocking known malicious IP access, etc.)
- Error messages revealing too much information, especially around authentication or password reset (e.g., having two separate messages for “the account with this email does not exist” and “password is incorrect” reveals the fact that the user is registered and can be a target of an attack, for example by spear phishing)
So, what are the best practices that you as a business analyst or UX designer should follow?
- Suggest an external authentication provider. There are many of them now providing enterprise solutions such as ADFS, RSA Link, Evidian, or One Identity, as well as public ones: OAuth, OpenID, Google, Facebook, Twitter, Amazon, or Microsoft authenticators.
- Suggest some sort of multi-factor authentication. And again, there are plenty of solutions available on the market.
- If you are not a technically skilled solution architect, find one and chat with them about what to suggest to the clients in terms of security.
- Be assertive in pointing out to your client that there cannot be compromises when it comes to security.
I am a solution architect
In this case, you have the privilege to be arguably the most important person in the process of security design. During the design phase, a business analyst/UX designer asks you about your opinion on what to suggest to the client (or they should have done so if they read this article). Now, it is your turn to choose the best tool or framework for the job. Here is a short list of best practices based on my experience.
Apart from my list, take a look at OWASP’s Top 10 threats, which is a regularly updated list of most common application vulnerabilities on the market, and consider if your design is bulletproof against them.
Talk to the client’s IT department
It is possible that you will learn many interesting things that will either make your decisions easier or harder, but for sure more informed. By the way, this not only applies to the security aspects. Maybe there is some infrastructure that your application needs to be compatible with (e.g., load balancing, proxies, ESBs). Maybe there are systems that you need to integrate with to conform to the enterprise standards of the client. Maybe there is already an SSO or another authentication solution in place. Make sure you gather all this information before you start designing your app.
Use encryption, authentication, and authorization mechanisms already in place
I am not going to tell you to use SSL in your app. This much is obvious. What is more important here, is to configure your development and testing environments with all the encryption, authentication, and authorization mechanisms already in place. It is true that it may cost you some time to configure them all. And yes, debugging may be a little bit trickier. Still, all this will usually save you time and labor when deploying your app to the target environments. Additionally, it will protect your lab environment from data leaks or ransomware attacks.
Design and implement authentication and authorization between the internal components of the application
Whether it is microservices, some kind of a distributed system, or a monolithic app with only database access, it is always a bad idea to use anonymous access, unencrypted endpoints, or leave no audit trail of the component’s usage. Regardless of whether these pieces are exposed to the Internet or available only in the corporate network. For example, think about X.509 certificates.
Design proper error handling
This is especially important when the application is facing a public network. However ugly it may look, it’s always better to show an “oops, internal error happened” page in Times New Roman than to leak data, configuration, source code, or stack trace.
Log your errors properly
Give the user some correlation token that you can later use to reference in your log files (or wherever your logs are stored). Give yourself and your power users proper tools to browse logs and monitor for unexpected patterns. As before, there are some available on the market (e.g., Graylog, Nagios, Elastic Stack).
Hide the error details from the user
If possible, predict what can go wrong, give them a meaningful message that they can do something with, and cover the rest with “unexpected error”.
Send meaningful and clear notifications to the power users or administrators if something goes wrong
If you configure your Serilog to send an email with every exception dump, expect this email to be added to the spam rules on the first day of the app’s life. Use logging levels properly to segregate the messages and make the important ones stand out.
Try to crack into your app
Ask yourself or others, how would one go about cracking into your application? Often the first entry point will be your login form, as it usually is the only one available anonymously. So, keep your error messages less elaborate, detect and block multiple invalid logins (not only per user but also IP, time window, or other attack signatures), protect yourself against timing attacks, etc.
Other best practices for you as a solution architect:
- Do not store passwords or sensitive data unless necessary. And if you do, do it well!
- Encrypt your configuration files.
- Automate your deployments to lower the risk of human error.
- Never agree to implement last-minute changes without the testing and release process being completed.
- Do not rely on the “it’s an intranet application, so no need for security” argument.
- Cheat! Take a look at OWASP’s cheat sheets for your technology.
I am a tester
Are you a penetration tester? If yes, you can skip this paragraph. If no, become one! At least on a small, basic scale. In the software development world, there is a common misconception that testers should focus on the functionalities whilst developers will be taking care of security. With this approach, you are either a very lucky tester who works with a very talented development team or you are just about to witness a break-in to the application you signed off as QA-approved. Ask yourself which is more probable.
Talking about penetration testing is well beyond the scope of this article, but just remember to schedule, plan, and perform the security tests even on a basic level:
- Test authentication mechanisms on the front-end and API
- Test roles and permissions (authorization) within the application on the front-end and API
- Do not test everything on a god-mode-enabled user.
- Master the testing tools, both manual (browser console and add-ons, Fiddler, Postman) and automatic (OWASP ZAP, Metasploit, W3af); the automatic ones will be harder to learn, but they are much more powerful, and you will find vulnerabilities you never thought possible.
- Schedule a meeting with your architect or developer and perform some tests together.
- Try to log into the app using invalid credentials and see what happens.
- Generate an exception in some way and see whether the information was stored in the logs and is accurate enough for the tech team to diagnose the problem.
- Try to break any integrations you may have; turn off one of the components, mess with the data interchange file, shut down the application in the middle of an important background job—all this to see how and if the application recovers.
I am a developer
You received your tasks, you are about to start coding. Everything is planned and designed properly. Or at least it should be. If you see any potential security holes, shout! In the worst-case scenario, someone will tell you that this vulnerability you are seeing is taken care of by some other component. In other cases, you will at least make someone else aware of the problem.
Do not trust your framework security features
You may also think that your framework has it all covered, and you will be fine. In most cases, this is true. But what if one tiny thing goes wrong? So, just to be on the safe side, here are a few hints to follow if you want to be sure that you’re doing things right in the area of security (and you do want that).
Never implement your own encryption or authentication
There are only very specific and rare exceptions to this rule. By doing so, you will inevitably make a mistake. Just don’t do it. There are too many libraries and tools available to write your own encryption.
Additionally, each framework includes its own set, used and battle-proven in thousands of applications before. Yes, do research on which is the best one, which gives the most options, which is the most secure, but never code your own.
Validate everything and everywhere
Whether the data comes from the user, your front-end, a microservice, or an external system, validate the hell out of it. Do it as soon as you get your hands on the data, and everywhere later, especially if you cannot 100% trust the source (and you never should). The user can be malicious but also careless or neglectful. It doesn’t matter from the point of view of the app security.
If they input something in the form, validate in the front-end (apart from security, it will give the user quick feedback), validate in the API, validate in any web service that the data is forwarded to. And at the very end, escape any input before it’s stored into the database. Remember, better safe than sorry. Don’t be that school from xkcd.
What security mistakes should an app developer avoid?
Here are a few mistakes you can make. This is by no means a complete list. For compactness, I’m focusing on a simple 3-tier application with a frontend, a backend, and a database.
No validation on the backend level or the validation is not thorough enough
Remember that the validations should either be the same or become stricter the further the data sinks into the application. Never do precise validation in the frontend and only superficial in the backend. Also, use whitelists, not blacklists.
No authentication or authorization on the backend level
This is a crime against programming. Note that even with good intentions from the user this may be a problem, for instance, when they share links or forward notification emails between each other.
Trusting blindly whatever comes from the frontend or any other component under your control
The data can be modified, the requests tampered with, the senders spoofed. URL parameters (both GET and POST/PUT), hidden field values, cookies, local storage, HTTP headers—all this can be compromised pretty easily, not even with complicated software. Here’s a screen from regular Firefox with no add-ons (see Figure 1):
Integers or other easily iterable identifiers in URLs
If there is proper authorization behind the scenes, it’s sometimes acceptable but still better to avoid tempting the user to see what’s behind ?id={n+1}.
Using “secret links” to share information or files with the user (i.e., using a long and random link to a resource on the server).
It’s steganography, which is lots of fun and sometimes useful, but it’s not cryptography. Nobody will guess the link, that’s almost certain, but there are numerous tools to find such URLs, they can leak, the users can share them online, the algorithm for their construction can be easy to crack, etc. If something’s available publicly on the Internet, you have to assume that it’s already leaked into the wrong hands.
Not updating the packages, libraries, and frameworks used by the application.
Maybe your app is finished, rolled out, and working, maybe the development lasts for so long that new versions of your dependencies have been released. They will very often contain security patches that you do want to have in your code. Yes, verify whether there are breaking changes. If not, keep the versions up to date.
Finally, some obvious mistakes that I’m not going to elaborate on, as they are widely known and the majority of them can be found in OWASP Top 10. Avoiding them is the basics of web app development. So, just an honorable mention to: SQL/XML/JSON injection, XSS, CSRF, insecure cookies, Access-Control-Allow-Origin: *, not using HSTS, CSP, x-xss-*, x-frame-options.
I am a DevOps engineer
If you monitor networks and environments, you are much smarter than me and I will not tell you how to do your job. However, based on my experience, I have prepared a list of things you should avoid.
- Development machines in the cloud should not have public network interfaces turned on.
- Your lab domain should have security policies, preferably as strict as the corporate ones.
- Domain accounts should not be reused between projects, services, and especially users.
- Development machines should have updates installed regularly.
- Data from production should not be stored on dev environments (at the very least, anonymize it!).
- Passwords should not be stored in the code repository.
- Do not share passwords via public channels.
- Be suspicious against atypical security-related requests.
- Be assertive when asked to compromise security guidelines.
Key takeaways
In the vast majority of the security-related cases you encounter during the software development process, it will be enough to stop and think for a moment instead of blindly following whatever routine you may be used to.
So, to sum up the key takeaways:
- Be assertive, from the bidding and planning until the maintenance period.
- Do not trust anyone who tries to lower your security standards.
- If you need a solution for authentication or encryption, check if someone has already had the same problem.
- Be lazy, take other people’s code or libraries and use them.
- Treat your server and your development machine just as you would your home PC; install updates, take backups and change your passwords.
I hope that this article either taught you something or at least made you verify your bad habits when it comes to software development. To be honest, I would be even happier with the latter.