You should always do server-side validation! Always!

Original article with another example on my personal website

always-do-server-side-validation-featured.png

Web technologies have evolved a lot in the past few years, both on the server-side as well as on the client’s side. There are many web frameworks, UI kits, JavaScript libraries and everything you need to easily and rapidly develop a website or web application. JavaScript libraries have matured and gotten very powerful and processing power, even on low-end computers, does not really pose a problem nowadays for most web-apps. Because of this, web applications have gotten more complex, user interfaces fancier and… programmers lazy.

Because JavaScript frameworks are so powerful, many are tempted to write business logic in the client-side part of the application. While this can save processing power for the server by unloading computational tasks to the user, and is highly recommended whenever possible, it may pose some security problems. This is especially true if the user can submit data to the server via forms or other input method.

Because of this, always perform server-side validation of any request, even if you (usually) have a client between the server and the user. Let us look at three scenarios and how failing to validate the input received poses a security vulnerability. Furthermore, even though it may seem obvious that there is a problem, this is not always the case and variations from all three scenarios were real problems I saw in real products.

Scenario 1: Privilege escalation by altering user role

For the first scenario we will be looking at an application that has different roles for registered user. It can be any such web-app, but I will consider it a content management system like WordPress. Here, users can have different roles: Administrator, Editor, Contributor, Normal User. Any user that is Contributor or above can change the role of another user, but he can’t grant higher privileges than what he has. For example, an Editor can’t make somebody else an Administrator.

To achieve this, the user edit page has a form with the user’s information and a drop-down with the possible user roles. The page is rendered using the template engine (in this example I will be using Twirl from Play Framework) and the acceptable values are indeed supplied by the server. Our malicious user is a ‘Contributor’, so only two roles should be available for him.

<select>
@for(role<-acceptableUserRoles) {
  <option value="@role.getIdHash()">@role.getName()</option>
}
</select>

Now, our programmer thought that he is smart and, for the value, he explicitly set it to an ID Hash. This is the hash of the ID of the role in the database since the app is quite complex and new roles can be added if needed. He even makes sure that the has received when a user is saved is valid and corresponds to an actual role. The method that handles the request looks something like this.

public Result saveUserRole(Http.Request request) {
    Form<UserInfoDTO> form = formFactory.form(UserInfoDTO.class).bindFromRequest(request);
    if (form.hasErrors()) return badRequest();
    
    List<Role> roles = rolesDao.getRoles();
    Role submitedRole = null;
    for (Role role:roles) {
        if (role.getIdHash() == form.get().role()) {
            submitedRole = role;
            break;
        }
    }

    if (role == null) return badRequest();

    UserDO user = userDao.find(form.get().userId());
    if (user == null) return notFound();

    user.setRole(role);
    userDao.update(user);

    return ok();
}

At first glance, everything seems to be ok. The form is validated, the role is validated, the user is requested from the database, and let’s even assume that there is a CSFR token that protects from malicious submits. Can you spot the problem? The server assumes that because the UI code was generated on the server, the role received is one of the roles that the user has permission to offer. There is no check to see if the role that is being attributed is not higher than what the current user has.

Our malicious user can simply open the inspector in the browser and edit the HTML to add a new value to the

Yes, he must know the hash, but if other protection mechanisms aren’t in place, he can just brute-force it since he knows the format. If these are default values and not randomly generated at each install, the task can be even easier since the user can install a dummy application on his computer and find the hash this way.

Scenario 2: Access to confidential pages by knowing/guessing the URL

Another common mistake is failing to check if a user has access to a page or not. There are multiple variations on this, either by failing to validate that a page is accessible to a specific role or by failing to validate the visibility of a page. We will look at both cases here.

This time we will be dealing with the presentation website for a company and they are about to launch a new product. The editors write the needed materials, prepare the new product page and wait for the product to be revealed in a few days so they can publish the page. Everything is ready, including final URL for the new product page, since it had to be sent to news outlets beforehand.

One such outlet was not included in the original press release, but because it is a high-profile company and there are rumors about the new product, they want all the juicy info as well. So they begin to try and guess the URL of the new product page. They know the name of the product and based on passed products, have an idea of how the new page’s URL may look like. Since the CMS fails to validate that an article was actually published or not, by accessing the URL the news outlet has all the information they need and can freely publish them faster than any other news site.

A variation on this can happen in complex systems as well. It is not uncommon for some functionalities or pages of a site to be inaccessible if the user does not have the right role. The developer made a list of pages that are visible for each user and based on that list and the currently logged in user, it builds the menu in the HTML file. This is done in a similar fashion to the select form from the first scenario. If the developer assumes that the user will navigate only by clicking the menu items in the UI, he may oversee server-side validation. This way, an user can access a page that he is not supposed to simply by entering the correct URL. This is a problem quite frequently encountered in web-apps that are not mature or still undergoing development, but with a public version already released.

For another example, you can read my original article on Why you should always do server-side validation

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Ecency