Let’s talk again about data security in web applications. User data is a precious resource, the loss of which has a range of consequences, with varying degrees of severity. But once again, reading about passwords or looking at classic phishing examples will be boring for most users – there is plenty of this information on the Internet. Therefore, today we will tell you a few atypical stories that our experts had to face.
A bit of theory
Let’s start, of course, by defining what data is considered confidential. OWASP includes passwords, credit card numbers, medical records, personal information and business secrets. In addition, for a list of sensitive data, you should consult the laws and industry regulations of the regions from which users may come to you. If we are talking about Russia, you need to look into the Federal Law of the Russian Federation No. 152-FZ “On Personal Data”, and in the case of European users, for example, the General Data Protection Regulation (GDPR) or the UK Data Protection Act (DPA), which govern the use of personal data.
In any case, let’s go, study the requirements and understand what user data we are obliged to keep safe and sound. By the way, in addition to this, there are also business requirements. The law may not require you to take strict measures on sensitive data that an app creates or stores for its users, but breaching that data will still harm your users and, consequently, your reputation.
The following can be considered as sensitive data stored in applications:
- Most user data. Here, however, you need to make a caveat: your data, available to the entire Internet, for example, usernames that we already see somewhere in the chat, may not be confidential).
- Application data (such as session IDs and encryption keys) that helps protect user data from being exposed.
Time for amazing stories
Let’s discuss some of the most common vulnerabilities that our specialists encountered in practice in a non-trivial way. The essence of the vulnerabilities does not change, this is the same OWASP list, but the story of such cases will help to look at application security from a different angle.
Looking for old versions
Various checklists for the security of web applications in the network wagon and small cart. But, as practice shows, keeping track of everything is unrealistic. Especially when it comes to using old versions of components in a large project. So, once on a service using spoofed phone numbers, an old API family was found where the number had not yet been hidden. Spoofing numbers is a good service to hide the seller’s real number from the notice board. However, if you carefully study the sent requests, find the documentation (or guess the necessary methods yourself), you could form requests to the old API and get the real user numbers through it.
In another case, the old API resulted in specific monetary losses. Investigation into a serious security issue revealed that the same vulnerable API method was used not only by valuable customers, but also by bad people who secretly withdraw funds from their accounts. The management had to accept the risks – the income significantly exceeded the losses, and it was decided to postpone the replacement of the vulnerable API.
It is possible that older versions of application components will not be subject to all current security policies, which is why the process of timely implementation of updates is so important.
We look broader at processes
The problems of large projects do not end there. You can often find security holes due to banal inconsistency of processes. Let us illustrate with an example: on one of the services, in a chat with technical support, users could send along with a complaint the number of the card associated with the service. According to the requirements of the security service, this number had to be hidden from the technical support staff. Upon closer examination, it turned out that the card numbers in the chat are really masked, but at the same time in the response from the server there was a number of hidden fields that were not substituted into the chat, but were visible in the traffic. If any data should be hidden, notify both the front-end and the back-end about it.
We do not talk too much
A similar process inconsistency issue was found in the Forgot Password Reset functionality. After entering the login (in this case, the email address), the site specified where to send the password: to the mail or to the linked phone number. If the second option was chosen, then an alert with a half-hidden phone number appeared on the page. But in the HTTP response from the server, the number was displayed in full. So you could collect a whole list of pairs “mail-phone number”. Disclosure-s.
By the way, talking about the password reset functionality. If, when implementing the registration functionality, only a few forget to use the general sentence “wrong login or password”, then when recovering the password, information about the presence of a particular login in the system is disclosed much more often. Specific responses from the server, confirming the existence of an entry in the system, allow us to compile a list of users and sometimes successfully use it in further attacks (phishing, password spraying, etc.). But not only explicit messages reveal information, the conclusion can be made by timing (for example, old versions of OWA, a web client for accessing Microsoft Exchange, suffered from this) or the response body.
We do not store excess
It is good practice to keep only what is really needed. So, on a certain site, every time the system sent an email to the user (for example, when registering, announcing events, etc.), all messages were recorded in the log. This log was available on the site, the text of messages, the surname and name of users, as well as their mail were stored inside. Interestingly, many directory search utilities did not find this file. Since its size was huge (several GB), the utilities simply could not wait for it to download and skipped it. But after trying several options of utilities, the auditors finally got their way.
In this story, in addition to the fact that redundant data was written to the log, access to them was not limited in any way. This, paradoxically, is far from a rare case. Here it is worth recalling a number of high-profile cases (for example, two, three) when quite serious amounts of data were leaking from Elasticsearch servers.
Well, let’s not talk about sad things. Just remember to use authorization correctly where you need it.
We are friends with logic
Yet the most common sore that allows user data to leak is Insecure Direct Object Reference (IDOR). In support of this, we will tell two non-classical stories.
Usually IDOR means reading information by identifier, bypassing authorization or rights checking. But there are cases when it is possible to get someone else’s data by sending a request to create a record with an identifier that already exists in the system. Some of the old data for this identifier will be overwritten, but the rest of the data will now be available to the new user (and will become inaccessible to the previous one – the owner will change).
Initially, access to someone else’s data will be denied, but a poor implementation that allows a user to influence an object’s identifier pushes a huge data security hole. All object identifiers must be generated on the backend side.
In another case, IDOR was found while looking at transactions. Three parameters were passed in the HTTP request:
POST /api/get_transactions HTTP/1.1 Host: example.com GT|2315486|2315488|40117812222030001111
, where 2315486 is the user ID,
2315488 – card identifier,
40117812222030001111 – account number.
At first glance, it seems impossible to iterate over 3 values (34 digits) in the foreseeable future. But, as it turned out later, the account number (40117812222030001111) could have been omitted in the request: the request still remained valid, as did the response. Accordingly, there were only two numbers left for selection – the user ID and the card ID.
Further, it turned out that the first value (user ID) can be anything, for example 1111111, but always the same size as the card ID (7-digit number). Accordingly, the GT parameter from a 34-digit number turned into a 7-digit one, and it is not difficult to sort it out (the system also did not have a limit on the number of requests from users). As a result, to get information about transactions, it was enough to know only the card ID (or iterate over its value).
IDOR vulnerabilities are not uncommon in general, but they are most common in complex web applications. Like other logical vulnerabilities, they are quite difficult to identify with code scanners.
Let’s check everything again
We conclude our story with a case in an online store that used websockets. When placing an order, an ID was generated for it in the system, and the user subscribed with this ID for all updates regarding the order. But, as it turned out later, it was possible to send a similar request “to subscribe” not with a specific ID, but with the wildcard “*” in its place. As a result, it was possible to get information about all user orders (with full name, addresses, phone numbers and other related information).
We are sure that there are plenty of similar, and sometimes much more furious stories in the practice of information security specialists, but it is not always possible to share them with the community. You can usually hear this in private conversation over a cup of tea. And yet, sharing such cases (impersonally, of course) is an extremely useful practice for both the community and developers. Finding vulnerabilities is a kind of art to think differently. Let’s inspire each other.