arsalandywriter.com

Enhancing AWS WebSocket Security with Lambda Authorizers

Written on

Chapter 1: Introduction to WebSocket Security

In the previous article, we explored creating a WebSocket API on AWS, enabling us to establish a working API for message exchanges. However, security is a crucial aspect that we now need to address.

As we delve into the world of WebSockets, the importance of safeguarding our API from potential threats becomes clear. We certainly wouldn't want unauthorized individuals to access our API and launch attacks, such as DDoS or injection threats.

In this post, we will enhance our earlier setup by incorporating a Lambda authorizer, ensuring that only authenticated users can connect to our system.

Understanding WebSocket Authentication

You might wonder, “Why is this topic necessary? I can easily add a Lambda authorizer to any API.” While that is true, there are specific nuances regarding WebSockets that are essential to grasp. Most JavaScript libraries for front-end applications do not typically support standard Authorization headers.

When establishing a connection, the WebSocket API only recognizes the Sec-WebSocket-Protocol header. Tools like Postman allow additional headers during connection attempts, which is beneficial, but you may encounter limitations when implementing the code on your application's front end.

To navigate this limitation while maintaining secure connections, we have two primary methods to consider:

  1. Sending delimited values via the Sec-WebSocket-Protocol header
  2. Including the authentication token as a query string parameter named access_token

Each method has its advantages and drawbacks, and the choice ultimately depends on your specific needs. In our AWS implementation, both options are supported. However, I recommend using the query string parameter method for its simplicity and clarity, avoiding complications with the Sec-WebSocket-Protocol header.

Instead of utilizing the standard Authorization header for new connections, we will send a query string parameter called access_token, which contains our JWT.

It's also important to note that authentication is only required during the $connect event. All subsequent interactions can utilize the established connection, making the process straightforward!

If you followed the initial part of this series, you have already deployed a basic WebSocket API in your AWS account, which can manage connections and subscriptions. Today, we will work with an updated branch of that repository to introduce enhancements.

If you are unsure how to check out non-main branches in a repository, you can execute the following commands in your VS Code terminal:

git fetch

git checkout part-two

Once you have the source code locally, you can deploy it as you did previously, using the sam deploy command. Before doing so, remember to update the samconfig.toml file.

In our Lambda authorizer, we will verify that the provided JWT (authentication token) originates from a trusted source. This involves checking that the JWT is signed with our secret key. Choose a secret key that you can remember, and ensure that it remains confidential, especially in a production environment.

Using the sam deploy --guided command is advisable to set the parameters for this stack anew. Once you've completed that, feel free to deploy!

What Did We Just Deploy?

First, I appreciate your trust in this process! You have deployed the same foundational setup as before, now augmented with several new features:

  • A new Lambda authorizer
  • An updated $connect Lambda that stores user information
  • A test Lambda that generates a JWT based on the secret you provided during deployment
  • Integration with Secrets Manager to securely store your JWT secret

The repository includes a comprehensive infrastructure diagram generated from the template.yaml file. If you haven't done so already, I highly recommend incorporating generated diagrams into your workflow. They are easy to create and immensely valuable. Below is the complete set of resources deployed on AWS to establish a secure WebSocket API.

Infrastructure Diagram of a Secure WebSocket Architecture

Connecting to a Secure WebSocket

With everything successfully deployed, it's time to connect to our WebSocket! First, we must ensure that the connection is secure. Let's connect in the same manner as we did in the first part of this series:

  1. Open your desktop application.
  2. Select New -> WebSocket Request.
  3. Enter the route from the output of your SAM deployment (use the WebsocketUri output value) in the address field.
  4. Click on the Headers tab and add the Sec-WebSocket-Protocol header with the value websocket.
  5. Hit Connect.

If everything goes correctly, we should receive a 401 error due to the absence of an authentication token.

401 response when connecting without an auth token

Now, let's obtain a token and establish our connection:

aws lambda invoke --function-name CreateTestJwt response.json

Open the generated response.json file and copy the value from the authToken property. In Postman, append a query string parameter called access_token to the URL and paste in the authToken value, then hit Connect.

Connected successfully!!

Now that we are connected, we can freely send and receive messages through the WebSocket without needing further authentication.

What’s Next?

With our WebSocket secured, we can now breathe a sigh of relief, knowing that malicious users cannot disrupt our system. The first step is to relax!

Furthermore, we can now send notifications on a user-specific basis. Lambda authorizers return a context object containing valuable data that you can use within your code. In our example authorizer, we've decoded the userId, firstName, lastName, and sub from the JWT and passed this information to the $connect function.

This data is stored within the connection record in DynamoDB, allowing us to send personalized push notifications when necessary.

User information from the lambda authorizer

The user ID is stored as GSI1PK, enabling us to retrieve connection details for users and send them notifications if needed. Although this functionality isn't implemented in this walkthrough, we will explore it in greater detail in future articles within this series.

Upcoming Topics

In this era of asynchronous programming, we’re diving deep into WebSockets. Future articles will cover documentation of WebSocket APIs using the Async API Spec, implementing user-based push notifications, and transitioning from synchronous to asynchronous endpoints seamlessly.

Feel free to experiment with the stack presented in this tutorial, familiarize yourself with the components, and make any desired modifications. Happy coding!

AWS API Gateway Websocket Tutorial With Lambda | COMPLETELY SERVERLESS!

This video guides you through creating a completely serverless WebSocket API using AWS API Gateway and Lambda.

Secure your API Gateway with Lambda Authorizer | Step by Step AWS Tutorial - YouTube

Learn how to implement a Lambda authorizer to secure your API Gateway, ensuring that only authenticated users can access your services.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Reimagining the Bauhaus: A Vision for the Next Century

Exploring the legacy of Bauhaus design and its relevance to contemporary challenges in society and ecology.

Understanding Men's Subtle Apologies in Relationships

Discover the nuanced ways men apologize and how they differ from women's expectations in relationships.

Maximizing Meeting Success: Essential Strategies and Tips

Discover essential strategies for effective meetings, ensuring clear objectives and engagement for all participants.