Frontend Application Security: How We Protect Sensitive User Data

Carlos Ruana
Strands Tech Corner
6 min readJan 9, 2020

This article outlines Strands’ web application security practices from a frontend point of view. Given that most risks are controlled by our solid restful API, the aim here is not to give an in-depth description of the top 10 OWASP security risks, but to explain how we deal with some of them from our frontend.

Image shows a wasp, which actually is the logo of the OWASP organization.

Avoiding Injection Attacks

Injection attacks, particularly SQL Injection and XSS (Cross-site-scripting) are currently considered the most dangerous vulnerabilities when developing web applications. Working with frameworks such as Angular, React, Vue.js or Ruby on Rails helps frontend developers dealwith most of the injection risks, as all of them implement default escaping mechanisms to avoid attacks. However, you should understand the particularities of each of them to be fully covered.

React JSX escapes variables before adding them to the DOM, meaning all values are converted to String before being rendered, avoiding malicious users injecting code.

Despite this, you should have some special cases in mind:

  • React ONLY escapes children elements as texts but not props. As a result, code could end up placed in some HTML properties, where malicious code could be executed. Most of these cases occur when handling users' data from input fields or from the URL, when receiving users’ input data already stored in the DB, or even when rendering components from a JSON or XML markup that has not been sanitized before parsing it to JS. The solution when receiving untrusted data from those channels is manual sanitization (that is, escaping control characters) before being placed on those props/attributes, also in script tags, URL locations, event handlers or CSS style tags.

You definitely want to avoid these examples without proper sanitation:

// Do not use not trusted data on hrefconst urlParam = window.location.search.substring(1);function HelloWorldComponent() {
return <a href={urlParams}>Click me!</div>;
}

User could change your URL from:

http://myurl/listdogs?page=1

to some random script which will be executed:

http://myurl/listdogs?javascript:alert(1);

// Do not use not trusted data on style tags you can also end up with some random scriptfunction HelloWorldComponent() {
return <a style="-o-link:'javascript:alert(1)';">Hi!</a>;
}
  • The use of eval or dangerouslySetInnerHTML could also be potentially risky when applied with users’ input data or a compromised DB/API. Employing these methods is strongly discouraged and that’s why we have rules set in our Sonar configuration to prevent developers from using them, except on very rare cases where its application is justified. Instead of eval you should use JSON.toJSON() and JSON.parse() and, if you really need to use dangerouslySetInnerHTML, you should escape control characters manually.

If you are using some of the previous examples, there are several libraries out there that will sanitize your texts for you. Using your own regular expressions for sanitizing can be quite dangerous if you are not really experienced with XSS attacks.

Also, if you are not using React DOM to escape values into your JSX or any other similar library, you may want to follow the steps on those cheat sheets to avoid vulnerabilities:

Third-party libraries risks

Even when you think you have covered all the risks in your application, you can be surprised to discover a vulnerability in some code your team has not even written. Although making sure that the external libraries you are using are 100% free of risk is close to impossible, there are several tricks you can do to mitigate that chance.

Using open versions of libraries that your developers will be pulling every time they do npm/yarn install is not a good idea, not only because it is unclear which bugs you are also importing each time, but for the risk it implies. Vulnerabilities on libraries with lots of downloads are often quickly fixed, but those libraries are also the most targeted by hackers. It is always better to choose a closed version of those libraries and check regularly for vulnerabilities and fixes.

Even a library initially free of vulnerabilities can be hacked (updated o replaced) with a version with intentionally malicious changed code. The best way to mitigate this is by having your own npm repository (acting as a cache) and enabling a Content Security Policy (CSP) to avoid importing compromised files from outside your company. In fact, good management of your CSP can avoid multiple XSS attacks.

Also, if you are using Jenkins Pipelines or any other CI tool, you may set up some automatic scans on those libraries using npm/yarn audit and report those vulnerabilities.

Preventing sensitive data exposure

In our money-management applications, we often handle very sensitive user data. Our backend team deals with mechanisms such as password encryption and session management, but most requests revolve around how personal data is managed in the frontend. The best way of avoiding exposing this data is to never store it in the frontend. That is why we totally discourage using localStorage and sessionStorage in our application (other than for storing “silly” data, if needed.)

HTML5 Web Storage has no data protection and is considered unsafe as hackers could use XSS attacks to read from it.

Additionally, logging out users after their session has expired might be a good idea to clear all data stored in the DOM, always depending on the level of importance your application gives to your users’ data.

Broken access control

Authentication, authorization and business logic controls should never be implemented on the client-side functionality, that is why ––for better user experience–– we only implement routing in React.

This relates to the previous topic of sensitive-data exposure. By limiting backend access, we avoid users without authorization reaching sensitive data.

Given the fact that is possible that different users might access our applications through the same computer, we should avoid caching any data in the browser by adding HTTP Cache-control headers and meta tags.

Security misconfiguration

In the frontend, a bad configuration when creating a production version of an application usually does not create a vulnerability. However, this can lead to code not being completely abstract or with a wrong source map, which could help hackers to find your vulnerabilities faster.

A good idea is having different webpack configurations for development and production, and let automatic processes (as Jenkins CI) create the production build for you.

Conclusion

Security in Web apps is not a one-time effort, there is a routine that you have to follow while your application in on production.

  • Check for new vulnerabilities and follow security organizations such as OWASP and their recommendations.
  • Review your third-party inventory regularly and update it if needed.
  • Use specialized tools or manually scan your logs for signs of malicious activity.
  • Take Pull Request very seriously, check for best practices violation and remind your team of the most dangerous current vulnerabilities.
  • Use automatic mechanisms to set Static Code Analysis Rules using a security tool such as SonarQube. Even if you have manually checked that your code is 100% bulletproof maybe someone else (not you, of course) will introduce a new vulnerability, probably when you are on holiday and you cannot decline the Pull Request!

--

--