Posted on 2021-02-27 byVlad Călin
Reading time of 1 minutes
When talking about security, hacks, exploits in the world of web application, most people think of very obscure flaws in libraries or operating systems that some attackers (or script kiddies) can exploit and gain boundless access to critical resources on the compromised server or web app.
Although these are also a valid concern, there are other kinds of flaws that might expose your web apps to attacks and data leaks: the kind of mistakes that have their root in faulty architecture and code designs.
In this post I'll outline some of them, and what are some easy fixes that can be done in an afternoon to prevent this kind of attacks.
An enumeration attack is when an attacker manages to extract valuable information that can enable more sphisticated attacks.
The most common type of enumeration attacks are email address enumeration.
Imagine the following scenario:
This kind of authentication is behavior is pretty wide-spread and I have encountered these types of error messages plenty of times.
With this kind of behavior, the attacker can brute force and extract some valuable information: what email addresses are registered on the website? They can start inputting email addresses until the "Incorrect password" message is returned. At that point, the attacker has discovered a valid target, and now can start the password enumeration for the targeted account.
To prevent this kind of attack, the server needs to respond with the same error message: "Invalid email or password" so the attacker doesn't know if there is an account registered with that email address or not, so they can't focus on a single email address. They'll have to perform a dictionary attack on all emails they have (most probably from a leaked email database), and as you can imagine, it is a time and resource intensive task.
The same issue is probably more wide-spread with password resets. The user is prompted to enter their password, and for a developer, the first instinct is this: if the user does not exist, return an error, if the user exists, send a password reset link via email. It's natural that in a state of error to respond with the error, but this behavior leads to the same kind of vulnerability. In this case, a success message should be returned no matter what: "If the account was in our database, an password reset link was sent via email".
In some web apps, permission checking is an afterthought, rather than a building block of the whole application. The base system is built initially as a proof of concept, and the permission checking system is added afterwards. This will most certainly lead to some forgotten application paths that will grant some users access to some information they are not supposed to access.
The most common occurrence of this kind of flaw is users getting access to other user's email addresses. This should be acceptable only between users from the same company, in the case of some business-to-business applications, where communication and knowledge of other people email addresses in necessary and expected.
In some extreme cases, you might discover that some user's session/access token has been compromised. In that case, you will need to cut off any access attempt using the compromised token. But in most of the cases, systems don't have implemented such a system.
JWT is inherently and by design vulnerable to this. There is no way to just revoke a token, since it is a stateless mechanism protected only by a timestamp and a digital signature. So the only way to revoke a token is to wait for it to expire.
In some more extreme cases, having a valid JWT will allow the user (and hence the attacker) to refresh it at will, this way prolonging the window of opportunity indefinitely.
Fortunately there are some ways to work around that: include in the JWT body some secret information that the server controls. When something goes wrong, the server just changes that information, and all JWTs generated using the old secret will suddenly be rejected. Although it could be considered an "ugly hack", it provides a "kill switch" for compromised tokens.
Another issue I've seen my fair share of is having access to the admin dashboard of some software on the default route, unprotected by a firewall and ready to be abused by some enumeration/dictionary attacks.
The biggest culprit is of course Wordpress: any Wordpress website has a high chance to have its admin dashboard
Another culprit is having the admin dashboard of a web application hosted at
/admin on the same website. This is either
due to the laziness of the development team, a severe oversight of how the public internet looks and behaves like
(it is flooded with swarms of bots automatically scraping and trying to get in these well known URLs) or just
Whatever the case may be, it is recommended to either put the admin interface behind a more hard-to-guess or even obfuscated link, protect it via a firewall that will block all untrusted traffic sources, or better: a combination of both.
Even though there are a bunch of other exploits and vulnerabilities out there, such as SQL injection, cross site scripting, etc, these vulnerabilities I enumerated stem from some architectural aspects and designs that are usually not even considered as being some attack vectors. They are pretty plain and simple, there is no extensive research on them, there is no glamour of talking about them.
When talking about security, people prefer to feel smarter by talking and listening talks about some very complicated memory access in some very specific CVEs.
But the reality is that these kind of security flaws have the capacity to ruin your web application (and business), more than obscure CVEs. Sure, they are important as well, but in that case, the only protection you can get is by keeping your software stack up to date. To prevent the simple attacks, the root problem is faulty architecture caused by decision makers (senior software engineers, tech leads, architects, CTOs) that are not focused enough on the security aspects of developing a web application.