Announcing Phoenix LiveView Support in Paraxial.io

Michael Lubas, 2024-04-30

Phoenix LiveView has seen incredible adoption in the past few years, providing developers with the ability to ship real time web applications with minimal latency and bandwidth usage. Paraxial.io is the only security product with a strong Elixir focus, so a natural question is “Does Paraxial.io support LiveView”? The answer is yes!

1. Cross site scripting (XSS) detection

LiveView uses HEEx, a templating language with files that end in .html.heex. Last year I noticed that these files were not being scanned for cross site scripting (XSS) issues with Sobelow, and submitted a PR to fix this. The SAST component of Paraxial.io uses Sobelow, and these files are currently being scanned correctly.

2. LiveView initial connection spam

Imagine a bad client opening several hundred LiveView connections per second for some malicious purpose, such as running a bot to break into user accounts. Each connection starts as a normal HTTP request, so Paraxial.io bot defense can detect the unusual traffic and ban the IP address.

This feature has always existed in Paraxial.io. You may be wondering: What if the bad guy establishes one connection, but sends hundreds of websocket messages per second over the connection, to avoid detection?

3. LiveView websocket connection spam

Today Paraxial.io can now guard against a malicious client sending too many websocket requests over a LiveView connection. Consider the following:

def handle_event("login", %{"user" => user_params}, socket) do
  case Accounts.login(user_params) do
    {:ok, user} ->
      {:noreply,
        socket
        |> assign(current_user: user)
        |> put_flash(:info, "Login successful")}

    {:error, changeset} ->
      {:noreply, assign(socket, changeset: changeset)}
  end
end

With Paraxial.io you can rate limit the number of login attempts send over one websocket connection. Note that for the below code to work you must use the :peer_data option with socket connect_info, then put the address in the socket assigns.

def handle_event("login", %{"user" => user_params}, socket) do
  ip_string = socket.assigns.address |> :inet.ntoa() |> to_string()
  key = "user-login-#{ip_string}"
  seconds = 5
  count = 5
  ban_length = "hour"
  ip = socket.assigns.address
  msg = "`> 5 requests in 5 seconds to login from #{ip_string}`"

  case Paraxial.check_rate(key, seconds, count, ban_length, ip, msg) do
    {:allow, _} ->
      do_handle_login(user_params)
    {:deny, _} -> 
      conn
      |> put_resp_content_type("text/html")
      |> send_resp(429, "Rate limited")
  end
end

def do_handle_login(user_params) do
  case Accounts.login(user_params) do
    {:ok, user} ->
      {:noreply,
        socket
        |> assign(current_user: user)
        |> put_flash(:info, "Login successful")}

    {:error, changeset} ->
      {:noreply, assign(socket, changeset: changeset)}
  end
end

The banned IPs will be tracked in the Paraxial.io backend, and you will receive an alert via the Paraxial.io Slack App.

See the documentation page LiveView Bot Defense for more information.


Paraxial.io stops data breaches by securing your Elixir and Phoenix apps. Detect and fix critical security issues today.

Subscribe to stay up to date on new posts.