A few basic aspects of security when developing web apps

Many of you around there design and develop web applications using your technology of choice. Many of them remember security aspects. Many of those do it right. But do they? And what about the remaining part? This article is to remind all of us about the mistakes we all make when creating web apps and some fallacies that we may become the victims of when thinking about security. There will be no bits of code but there will be some universal ideas that you can implement using the language and framework you feel most comfortable with.

When to think about 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 the estimation and bidding phase. Remember that the consequences of neglect can be quite severe, starting from public image issues, additional work, or budgeting problems, up until financial fines or legal actions against you and your company. Be on top of things and think about:

  • The needs and requirements of the customer. They may already know what they want in the area of security. They may already have some tools in their ecosystem that you’ll need to integrate with. Often though they will not have any of those or even they will not know they need data and systems security. In such a case it is your role to let them know, even if it means you have to scare them with what will happen when those evil hackers crack into their precious data store. Because they will if you not protect it, so it’s not even a lie.
  • The conditions under which the application will run. Will it be publicly accessible or intranet-based? Who is it targeted to? Will it be under heavy load or not? Does the framework or application platform you’re using implement basic (and not so basic) security concepts? All these answers, and many others, will guide you towards a correct design and budgeting for your new system.
  • What tools will you use to ensure the security of the app? Are they free or require licensing? Are they easy to use or will you need to hire or train a dedicated developer for that? Will they integrate well with the customer’s ecosystem? All of these questions will have an impact on the budget and team composition, so it’s good to remember about them.

Base on all of these aspects you will need to plan for appropriate funding of the development and testing of the security aspects. So if you’re an architect, include it in the estimations. If you’re responsible for budgeting, include it in the price. If you’re a salesperson, emphasize to the potential customer how important it is to consider the safeness of the application and that this extra cost your offer includes compared to others is because, contrary to them, you know what you’re doing when it comes to security.

One last thing: if at any, and I mean any, phase of the project you notice that there are some vulnerabilities, let everyone involved know. Even if it’s the last testing stage and fixing the hole means that some part of the application will need to be rewritten or redesigned. Maybe there is a quick solution, maybe there is some budget left (yeah, I heard somewhere it happened once for someone) or at the very least, let the customer know about it and maybe they’ll reach for their wallet.

I am a business analyst or a UX designer

There are many ways in which a UX designer can go wrong when (not) thinking about security. Not even because of not knowing enough but often because the customer doesn’t know better. They want their system to be easy to use and easy to manage, sure, but that doesn’t mean you can’t design the application safely. Don’t be tempted to go for shortcuts. Or at the very least let the customer know that what they want will make the solution vulnerable in some ways. It’s especially crucial if the customer is not very tech-savvy. You have to be the smarter person then.

Here are a few sample ideas that need to be avoided, unless the customer is fully aware of the risk and accepts it. All of them are real-life examples.

  • Logging one user in the context of another one
  • Permission elevation
  • Accessing protected functionalities by an unauthorized (public) URL, even if it’s obfuscated
  • Accessing other users’ data by an unauthorized (public) URL, even if it’s obfuscated
  • Using passwords that are easy to guess (like birthdays) to “secure” data shared with the 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.)
  • Too revealing error messages, especially around authentication or password reset (for example 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 instance by spear phishing.

So what are the key points that the business analyst or UX designer should talk about with the customer?

  • Suggest external authentication provider, there are loads these days (enterprise solutions, such as ADFS, RSA Link, Evidian or One Identity, as well as public ones, such as OAuth, OpenID, notably Google, Facebook, Twitter, Amazon, or Microsoft authenticators).
  • Suggest some sort of multi-factor authentication, again there are a lot of available solutions out there.
  • If you’re not a technically skilled architect, talk to one, for sure there are some in your company or the wild.
  • Most importantly – be assertive, never fall for an “I don’t need any of these” types of client.

I am a solution architect

In this case, you have the luck and privilege to be arguably the most important person in the process of security design. During the design phase, your analyst/UX designer has talked to you for an opinion on what to suggest to the customer (or they should have if they had read this article). Now it’s your turn to choose the best tool of the framework for the job. I’m not going to give you any answers but surely I can point you towards some key concepts that you ought to remember. 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 at least against them.

  • First of all, talk to the customer’s IT department, if possible. You’ll learn many interesting things that will make your decisions easier, harder, or at the very least well-informed. By the way, this goes not only for 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 customer. Maybe there already is an SSO or another authentication solution in place.
  • I’m not going to tell you to use SSL in your apps. But what I will tell you is to configure your development and testing environments with all the encryption, authentication, and authorization mechanisms already in place. Yes, it may cost you some time to configure and yes, it may cause debugging to be a little bit more tricky but it will usually save you even more effort when dealing with problems during deployment to the target environments. Plus, 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’s microservices, some kind of a distributed system, or a monolithic app with only database access, it’s always a bad idea to use anonymous access, unencrypted endpoints, or leaving 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 X.509 certificates.
  • Design proper error handling and/or configure whatever your framework gives you, especially 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. Notably:
    • 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 ones out there (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 a mail with every exception dump, expect this email to be added to the spam rules on the first day of the app’s life.
  • 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 not too 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.
  • Do not store passwords or sensitive data unless necessary. And if you do, do it well!
  • Encrypt your configuration files.
  • Automate your deployments, there’ll be less risk of a human error.
  • Be assertive, never agree to implement last-minute changes, do not deploy a version without the testing and release process completed. Even if “it’s just a small fix”.
  • 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, available at https://cheatsheetseries.owasp.org/

I am a tester

Are you a penetration tester? If yes, skip ahead. If no, become one! At least on a small, basic scale. A very common situation in the software development world is that the testers focus on the functionalities, believing in the developers to take care of security. With this approach, you are either a very lucky tester who works with a very competent development team or you are just about to witness a break-in into 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.
  • Make friends with tools that assist you in the process, both manual (browser console and add-ons, Fiddler, Postman) and automatic (OWASP ZAP, Metasploit, W3af). The automatic ones will be harder to learn but much more powerful and you will find vulnerabilities you never thought were 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 it 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 – 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. If you see any potential security holes, shout! Worst case scenario, someone will tell you that this vulnerability you’re seeing is taken care of by some other component. Often you will at least make someone else aware of the problem.

You may also think that your framework has it all covered and you will be right. In most cases. And you only need one for the things to go 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, and I mean never ever, with very specific and even more rare exceptions, implement your encryption or authentication. When doing so you will inevitably make a mistake. Just don’t. There are too many libraries and tools available to write your own. Also, each framework includes its own set, used and field-tested in thousands of applications before. Yes, do research 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 hand 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, doesn’t matter from the app’s point of view. 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 a 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.
  • Here are a few mistakes you can make. By no means this is 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 whatsoever or the validation not thorough enough. Remember, the validations should either be the same or get more strict 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.
    • Blind trust in 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 can be compromised pretty easily, not even with complicated software. Here’s a screen from regular Firefox with no add-ons:
  • 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 very fun and sometimes useful but it’s no 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.
  • 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 (https://owasp.org/www-project-top-ten/). 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
  • Last but not least – update 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 but if not, keep the versions up to date.

I am a DevOps engineer

If you monitor and oversee the networks and environments, you are much smarter than me and I will not tell you how to do your job. However, I can still list a few cases I’ve encountered in this area, just to give you a heads up that it’s something that happens in the actual world.

  • 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 one.
  • Domain accounts should not be reused between projects, services, and not 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.
  • A healthy level of distrust is always desired – do not share passwords by public channels, be suspicious against atypical security-related requests, be assertive when asked to compromise security guidelines.

Summary

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 follow whatever routine you may be used to. Be assertive, from the bidding and planning until the maintenance period. Do not trust anyone who tries to lower your standards of security. If you need a solution for authentication or encryption, someone already had this problem. Be lazy, take other people’s code or libraries and use them. Last but not least, treat your server and your development machine just as you would with your home PC – install updates, take backups and change your passwords.

I hope that this article either taught you something or at least it made you review and verify your bad habits when it comes to software development. To be honest, I would be even happier with the latter.

Share on linkedin
Share on facebook
Share on twitter

You may also find interesting:

Product Design Sprint

Design Sprint is a process of business problems solving developed by Google Ventures. It was created to help start-ups build better products. We adapted this method to the requirements of big organizations.

Design-Driven Innovation

Innovation is often about changing the meaning of what a product or a service is and what it offers its users. This is the core of the design.

UX & Visual Design

Interfaces, processes, and ecosystems, improve customer journeys, help to increase sales, and provide better customer service. We combine business and users’ needs to create digital products that make money.

Scrum at a glance

SCRUM has become the dominant approach to organising project work. The high variability of the economic and legal environment forces companies to increase the speed of adaptation.

Contact us