As a front-end developer, you've built tons of great features. You've handcrafted amazing UI/UX elements. You've probably also catered to security concerns in a web application. But when it comes to back-end interactions, you might feel limited in what you control, especially with respect to the data that comes back from the server.
Even with the utmost caution, a common yet lethal vulnerability in your application can be introduced. The reason for this could be simply because the server sends back unnecessary or extra sensitive data back to the client. This leads to excessive data exposure in your application. So what can we do about it? How can we prevent it?
In this post, I'll show you what excessive data exposure is, and I'll provide you with some examples. Then I'll walk you through how you can prevent it in your Angular application.
What is Excessive Data Exposure?
Let's say you have an Angular application that interacts with a remote back-end server to get a bunch of data for different pages. Whenever your users loads a page, you request the required data from the back end and display it to the user. So, for example, if your user is visiting an "offers" page, you display them a bunch of cool offers on your website.
Now, if they visit a different page, say an "about" page, you display your website's "about" information fetched from an API. But are your Angular components really aware of what data each API is returning? Or are they only concerned with the data that they require in that instance?
I bet it's the latter. This is because how your back-end APIs are structured is immaterial to your front-end codebase. So when you visited the "offers" page, maybe the API returned the offers data alongside some extra information that you're not using. But wait, no big deal, right? As long as the front end receives the data it wants, everything else can be ignored, correct?
Not exactly. We know that browsers make it really simple to inspect network requests via the developer tools.
The extra sensitive data that your back-end API returns can be of significant use to an attacker.
They can use it to steal some information about your users.
This is called excessive data exposure. It's a situation where critical and sensitive data is unnecessarily exposed by your application. If this was hard to understand, let's dive deeper into it with an example.
Excessive Data Exposure: Example
Let's say your Angular app is a social media application. Your users can create their own profile, share posts, etc. Let's say your users can share someone's profile with other users. So when you click the Share button, you can see that user's profile. Sounds good and safe, right?
But now let's say that the back-end API that fetches a user's data accidentally also fetches that user's access token. You'll probably ignore it, or you may not even notice it since you don't make use of that data on the front end anywhere. However, that piece of information is sensitive, and literally anyone can extract it by inspecting the network request on that page.
Here's the entire flow demonstrated visually:
This is an example of excessive data exposure. An attacker who gains access to a user's access token can easily break into their accounts. Then they can modify their information in your database. They can even gain access to data such as their passwords, bank details, etc.
Methods to Prevent Excessive Data Exposure
We'll talk about two primary methods to prevent excessive data exposure. Each of these methods assumes a different kind of API architecture for your application.
Back End Based Prevention
First, if you're using REST APIs, you need to refactor those APIs in terms of what data they're sending back. Sometimes an API runs a query to grab all the data in a row of a table and dumps it back to the client. With this, it may send data that's of no use to the client and is also considered sensitive from a security standpoint.
For instance, in the previous example, the API was sending back access tokens for a user. The way around this is to have the API filter out this data on the back end, either directly in the database query, if possible, or at the time of processing.
Front End Based Prevention
However, what happens when you're using third-party APIs? Or what do you do in cases where you don't want to modify your back end? Let's assume your front end consumes data from GraphQL-based APIs.
GraphQL gives your front end full control over what data it receives and processes. You can more responsibly update your GraphQL query to only request data that you need. You can also carefully craft your query such that you don't end up requesting sensitive data points that can be avoided.
In case you're not using GraphQL for your backend APIs, you can ensure that your front end does all of the following:
Doesn't leak sensitive data via browser storage (local storage and session storage) or cookies that can be easily spotted.
Doesn't store sensitive data in local variables or in data attributes of DOM elements that can be extracted via client-side JavaScript.
Ensures client and server interaction happens over only HTTPS, which encrypts data by default.
However, these are just some best practices you can adopt to add another layer of security to your front end. It doesn't necessarily protect sensitive data from being exposed, since the attacker can always see such information in the network tab of the browser.
Practical Prevention of Excessive Data Exposure in Angular
We've seen a lot of theory, but let's put it into some practice. For this tutorial, we'll use an open GraphQL API called GraphQLZero. If you wish to explore this API, you can do so in the GraphQL playground here. You can generate the query for getting response back from the API and then update the query to see how the response changes.
For now, let's first go ahead and create a new Angular app.
Create a New Angular App
Inside a directory of your choice, run the following command to create a fresh Angular project:
ng new angular-excessive-data-exposure-app
We won't need routing or any additional configuration, so feel free to skip that when prompted in the CLI. Once you do that, you should have a new Angular project created for you. Now, let's update its app.component.html file with the following code:
<h1>Angular Excessive Data Exposure App</h1>
Now, let's kickstart the Angular local development server by running the following command inside the root directory:
ng serve
If you now visit http:///localhost:4200, you should see the following page:
Awesome! Let's move ahead.
Introducing a Case of Excessive Data Exposure
First, we'll create a case of excessive data exposure by requesting extra data from the API. Head over to the app.component.ts and add the following variables inside it:
apiUrl='https://graphqlzero.almansi.me/api'
query=`{
user(id: 1) {
id
username
email
address {
geo {
lat
lng
}
}
}
}`
userData:any;
The apiUrl simply holds a string to the query endpoint. Then, we have a query string object that we'll use when we make a request to the GraphQL API. Finally, we have a userData variable to store the user data that we get back from the API as response.
Now let's make the request to the endpoint. Inside the ngOnInit lifecycle method, add the following code:
ngOnInit() {
fetch(this.apiUrl, {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify({
query: this.query
})
}).then(res => res.json()).then((data)=>{
this.userData=data.data.user
console.log(this.userData)
})
}
We simply use the fetch API to make a request to the above GraphQL API. We also pass the query string inside the JSON body. Then we process the data and store the relevant result inside the userData variable. Makes sense?
Now, we'll consume this data in our template. Head over to the app.component.html file and update it with the following code:
<h1>Angular Excessive Data Exposure App</h1>
<p>username : <span>{{userData?.username}}</span></p>
<p>email : <span>{{userData?.email}}</span></p>
<p>location : <span>{{userData?.address?.geo?.lat}}</span>, <span>{{userData?.address?.geo?.lng}}</span></p>
Now go back to your app and you should see that data displayed on the page:
Great! Looks like you did everything correctly, right? Well, not exactly. We're only using the user's username, email and location fields from the response in the UI. But let's inspect the entire request and response from the browser's network tab:
Notice that we also get back the user's ID. Imagine if this ID is an access or authentication token equivalent. It's a sensitive piece of information that's clearly not of any use here. Looks like we have an excessive data exposure vulnerability!
How to Prevent Excessive Data Exposure
Remember when I said that with GraphQL, a front end has complete control over what data it requests and what information the API sends back in response?
If you look closely at the query, we explicitly ask for the user's ID. If we remove it from the query, the API should not send that data back at all. So let's try to do that. Here's what the updated query string should look like:
query=`{
user(id: 1) {
username
email
address {
geo {
lat
lng
}
}
}
}`
We now no longer receive the id field back. Great! We have just prevented excessive data exposure by cautiously modifying the GraphQL query.
Conclusion
If you're not using GraphQL but instead using REST for your back-end APIs, your front end can't do a lot to protect your application from excessive data exposure. So always make sure your REST APIs are structured in such a way that they only send back the data needed. However, as wise front-end developers, you can prompt your back-end team to be more cautious about what data they're sending and play your own part in responsibly preventing excessive data exposure in your application. Until next time!
This post was written by Siddhant Varma. Siddhant is a full stack JavaScript developer with expertise in frontend engineering. He’s worked with scaling multiple startups in India and has experience building products in the Ed-Tech and healthcare industries. Siddhant has a passion for teaching and a knack for writing. He's also taught programming to many graduates, helping them become better future developers.