Removing sensitive information from HTTP headers in Lambda functions
1. The scenario
Example Corp has several microservices. Some of these services run on Lambda functions. One such function uses a 3rd party API to fetch some data. The application uses the popular HTTP client axios for making the requests, and authenticates with a JWT.
One day the 3rd party API responded with an error. Alice, who is a developer at Example Corp, looked at the logs, and to her surprise, she saw something like this:
{
// lots of other properties
"headers": {
"Accept": "application/json",
"Authorzation": "Bearer <JWT TOKEN HERE>",
"User-Agent": "axios/0.27.2"
},
"method": "get",
}
// more properties
It’s not good, Alice said to herself. There is sensitive information in the logs. She quickly created a ticket on the issue and started to remove the token from the response.
2. The solution
She wanted to apply the solution specifically to the requests the application made to the 3rd party API. She decided to dedicate an axios client to these calls and use an interceptor function to remove the sensitive information from the response.
2.1. Code
She didn’t have to do a lot of work. In the end, her code was similar to this:
import axios from 'axios';
const axiosInstance = axios.create();
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const sanitizedError = {
message: error.message,
name: error.name,
stack: error.stack,
method: error.config.method,
url: error.config.url,
status: error.stats,
};
return Promise.reject(sanitizedError);
}
);
// Lambda handler
export const handler = async () => {
try {
const { data } = await axiosInstance.get('<URL_TO_CALL>', {
headers: {
Authorization: 'Bearer TOKEN',
},
});
// process response
} catch (error) {
console.log('An error occurred', error);
throw error;
}
};
The axios interceptor is available on both the request
and response
sides. Here we implement the interceptors for the response
, but request
interceptors work similarly.
2.2. Interceptor
Response interceptors (provided by the axios.interceptors.response.use()
method) stop the response and allow us to implement custom logic before we can see the result.
Similarly to Promises the interceptor accepts two function arguments.
The first function (successHandler
) intercepts the response when everything works as expected. Its argument will be the response
object. In this example, we don’t want to do anything with the response, so we will return
it.
The second argument (errorHandler
) is the function that modifies the thrown errors. The middleware provides the error
object as an argument to the callback function. We can create a custom object with the properties we want. As the code snippet shows, we ignore the axios config
object that contains the token. We only keep the properties that we want to use: name
, message
, method
, and a couple of others.
Let’s invoke the Lambda function now. We should only see the properties we have set in the sanitizedError
object. sanitizedError
doesn’t contain the headers
, so we won’t see the Authorization
header in the logs anymore.
It’s important to return a rejected Promise (or throw an error), or axios will consider the response a success.
3. Summary
When an axios request throws an error, we will see the entire response object in the logs. The response might contain sensitive information, for example, authorization tokens. We can remove these fields by intercepting the response and rejecting a promise with a custom error object.
4. References, further reading
Axios Interceptors - More about axios interceptors