API Connect

 View Only

WebSocket APIs in API Connect

By Yahel Nachum posted Thu May 04, 2023 01:12 PM

  

Table of Contents

  • REST Endpoints vs WebSocket Endpoints
    • Denial-of-Service Attacks
    • Rate Limits
    • Count Limits
  • Creating a secure WebSocket API from a target service
  • Creating a secure WebSocket and HTTPS API from a target service
  • Example API and Product YAMLs

REST Endpoints vs WebSocket Endpoints

Traditional REST endpoints allow only a single request to be sent and a single response to be received. This usually leads to a small amount of time to complete a REST request/response cycle. In comparison, a WebSocket starts out as a normal GET request that is later upgraded into a WebSocket request, where either the client or the server can send a message at any point in time. Given this asynchronous messaging, the main use for a WebSocket connection is to subscribe to an event and be notified when that event occurs, usually with some additional information about the event as well.

An example of this could be subscribing to a stock price change of a particular stock. A client would start by opening a WebSocket connection to the server. After the connection was established, the client would send a message over the connection with the name of the stock that it would want to get price updates on. Once the server received the request it would send an updated price for that stock every time it changed in the stock market. Without either the client or the server initiating a closure of the connection, the client in theory could receive stock price updates forever. Since, WebSocket connections could stay open forever this poses unique security concerns compared to REST endpoints.

Denial-of-Service Attacks

A common attack vector for a REST endpoint is a Denial-of-Service attack (DoS). A DoS attack is when an attacker maliciously floods a server with requests, causing the system to be overwhelmed and deny service to other, valid users. A specific way an attacker can perform a DoS attack is by using the memory needed to open a connection as an attack vector. Since a server has a finite amount of memory at its disposal and each request takes a certain amount of memory to complete, if a large number of requests came in at the same time, then the system could run out of memory and crash trying to handle too many requests.

Rate Limits

A common solution to a DoS attack that is used by REST endpoints is a rate limit. A rate limit allows a client a certain number of requests per some time period. For example, a server could limit the number of requests from a particular client to 5 requests per 3 seconds. This effectively constrains the amount of stress on the system to be dependent on the number of clients trying to access the server at once.

When applying this rate limit on a WebSocket endpoint a DoS attack is still possible. Compared to a REST connection, a WebSocket connection stays open indefinitely. So even when a rate limit is enforced, a single client could still overwhelm the system by continuing to open connections until the server runs out of memory. Even with a very low rate limit of 1 request per minute, the system would eventually accumulate thousands of connections by the end of a week.

Count Limits

Instead, the proper security measure to apply on a WebSocket endpoint is a count limit. A count limit allows each client only a particular number of requests to be open at once. For example, a client could be limited to 10 connections at any given time. The server keeps track of how many connections are open for a particular client, and replenishes the count when connections are closed so that the client can open new connections in its place when it desires. When a count limit is enforced on a WebSocket endpoint, that endpoint is now safe from DoS attacks that use connection memory as a way to bring down the system.

Creating a secure WebSocket API from a target service

You can use API Manager to create an API that can handle WebSocket connections securely to a backend that supports WebSocket connections.

  1. Create a new API (REST, GraphQL, or SOAP) and specify From target service.
  2. Specify a target service URL that has the scheme https so that it passes the URL validation checks.
  3. Add the wss scheme to the schemes list in the API.
  4. Change the invoke policy in the assembly to a websocket-upgrade policy.
  5. Add a ratelimit policy that references a countlimit definition before the websocket-upgrade policy.
  6. Add ratelimit policies that reference a regular ratelimit definition in both the request and response sub-assembly within the websocket-upgrade policy.
  7. Test your new API with your favorite WebSocket tester (e.g. wscat npm cli tool)

Below is a screenshot showing the resulting API Assembly after following the steps above.

 

Creating a secure WebSocket and HTTPS API from a target service

You can use API Manager to create an API that can handle both WebSocket connections and regular HTTPS connections securely to a backend that supports both WebSocket and regular HTTPS connections.

  1. Create a new API (REST, GrapQL, or SOAP) and specify From target service.
  2. Specify a target service URL that has the scheme https so that it passes the URL validation checks.
  3. Add the wss scheme to the schemes list in the API.
  4. Add a switch policy in the assembly with a condition that checks when request.websocket is set to true.
  5. Move the invoke policy into the otherwise section of the new switch that was added.
  6. Copy the invoke policy and put the new duplicate in the condition that was created for request.websocket = true.
  7. Change the new duplicate invoke policy in the assembly to a websocket-upgrade policy.
  8. Add a ratelimit policy that references a countlimit definition before the websocket-upgrade policy.
  9. Add ratelimit policies that reference a regular ratelimit definition in both the request and response sub-assembly within the websocket-upgrade policy.
  10. Add a ratelimit policy that references a regular ratelimit definition before the invoke policy.
  11. Test your new API with your favorite WebSocket tester (e.g. wscat npm cli tool)

Below is a screenshot showing the resulting API Assembly after following the steps above.

 

Example API and Product YAMLs

Here is and API and Product YAML that I created following the "Creating a secure WebSocket API from a target service part of this blog. You should replace the target-url.value: https://mywebsocketendpointhere.com/ with your own valid WebSocket endpoint.

API YAML

swagger: '2.0'
info:
  version: 1.0.0
  title: test-yahel-websocket-api
  x-ibm-name: test-yahel-websocket-api
basePath: /test-yahel-websocket-api
x-ibm-configuration:
  properties:
    target-url:
      value: https://mywebsocketendpointhere.com/
      description: URL of the proxy policy
      encoded: false
  cors:
    enabled: true
  gateway: datapower-api-gateway
  type: rest
  phase: realized
  enforced: true
  testable: true
  assembly:
    execute:
      - ratelimit:
          version: 2.2.0
          title: count limit
          source: plan-named
          count-limit:
            - name: websocket-connections
              operation: inc
      - websocket-upgrade:
          title: websocket-upgrade
          version: 2.0.0
          verb: keep
          target-url: $(target-url)
          follow-redirects: false
          timeout: 60
          parameter-control:
            type: allowlist
            values: []
          header-control:
            type: blocklist
            values: []
          inject-proxy-headers: true
          request-assembly:
            execute:
              - ratelimit:
                  version: 2.2.0
                  title: request rate limit
                  source: plan-named
                  rate-limit:
                    - name: websocket-request-ratelimit
                      operation: consume
          response-assembly:
            execute:
              - ratelimit:
                  version: 2.2.0
                  title: response rate limit
                  source: plan-named
                  rate-limit:
                    - name: websocket-response-ratelimit
                      operation: consume
    finally: []
  activity-log:
    enabled: true
    success-content: activity
    error-content: payload
paths:
  /:
    get:
      responses:
        '200':
          description: success
          schema:
            type: string
      consumes: []
      produces: []
    put:
      responses:
        '200':
          description: success
          schema:
            type: string
      consumes: []
      produces: []
    post:
      responses:
        '200':
          description: success
          schema:
            type: string
      consumes: []
      produces: []
    delete:
      responses:
        '200':
          description: success
          schema:
            type: string
      consumes: []
      produces: []
    head:
      responses:
        '200':
          description: success
          schema:
            type: string
      consumes: []
      produces: []
    patch:
      responses:
        '200':
          description: success
          schema:
            type: string
      consumes: []
      produces: []
securityDefinitions:
  clientID:
    type: apiKey
    in: header
    name: X-IBM-Client-Id
security:
  - clientID: []
schemes:
  - https
  - wss

Product YAML

info:
  version: 1.0.0
  title: test-yahel-websocket-product
  name: test-yahel-websocket-product
gateways:
  - datapower-api-gateway
plans:
  default-plan:
    title: Default Plan
    description: Default Plan
    rate-limits:
      default:
        value: 100/1hour
        hard-limit: false
    assembly-rate-limits:
      websocket-request-ratelimit:
        - value: 3/1minute
          cache-only: true
          is-client: true
          use-api-name: false
          use-app-id: false
          use-client-id: false
          hard-limit: true
      websocket-response-ratelimit:
        - value: 5/1minute
          cache-only: true
          is-client: true
          use-api-name: false
          use-app-id: false
          use-client-id: false
          hard-limit: true
    assembly-count-limits:
      websocket-connections:
        - hard-limit: true
          cache-only: true
          is-client: true
          use-api-name: false
          use-app-id: false
          use-client-id: false
          auto-decrement: true
          value: 2
    apis:
      test-yahel-websocket-api1.0.0: {}
apis:
  test-yahel-websocket-api1.0.0:
    $ref: test-yahel-websocket-api_1.0.0.yaml
visibility:
  view:
    type: public
    orgs: []
    tags: []
    enabled: true
  subscribe:
    type: authenticated
    orgs: []
    tags: []
    enabled: true
product: 1.0.0



#APIConnect
#RESTAPI

1 comment
115 views

Permalink

Comments

7 days ago

Hi Yahel, 

My current project has a challenge that the consumer application is not able to configure the websocket call, but the backend is the websocket API.  How can I do to leverage API Connect as the gateway exposing a RESTful API but connecting to backend webSocket API.   If API Gateway is to maintain the websocket connection,  the consumer application just needs to send over the payload while using Websocket upgrade policy to set up the connection, send over the payload and also close the connection in one assembly , does it work this way?   

Thanks your help in advance !

Andy