hawkyre

hawkyre

I need to decode the plug session manually for my websocket server (I used to only have API calls and that worked fine, but now that I need websockets, which don’t work with plug, I have to decode the session cookie without the conn struct). I have managed to get a bitstring, but there seems to be some kind of extra step to go from the bitstring to the map from session key to session value that I’m missing. Here’s the source code I have so far.

These 2 functions are taken almost straight from the plug source code:

defp derive(secret_key_base, key, key_opts) do
  secret_key_base
  |> KeyGenerator.generate(key, key_opts)
end

defp read_raw_cookie(raw_cookie, opts) do
  signing_salt = derive(opts.secret_key_base, opts.signing_salt, opts.key_opts)

  case opts do
    %{encryption_salt: nil} ->
      MessageVerifier.verify(raw_cookie, signing_salt)

    %{encryption_salt: _} ->
      encryption_salt = derive(opts.secret_key_base, opts.encryption_salt, opts.key_opts)
      MessageEncryptor.decrypt(raw_cookie, encryption_salt, signing_salt)
  end
  |> case do
    :error -> nil
    {:ok, result} -> result
  end
end

This is what I do to set up the options for decoding:

opts = %{
  key_opts: [
    length: 64
  ],
  secret_key_base: Application.fetch_env!(:llserver, :session_secret_key_base),
  encryption_salt: Application.fetch_env!(:llserver, :session_encryption_salt),
  signing_salt: Application.fetch_env!(:llserver, :session_signing_salt)
}

cookies = request.headers["cookie"]
ll_cookie = Cookies.decode(cookies)["_ll_session"]
session = read_raw_cookie(ll_cookie, opts)

This returns a session, which in the console prints like this:

<<131, 116, 0, 0, 0, 1, 109, 0, 0, 0, 13, 115, 101, 115, 115, 105, 111, 110, 95,
  116, 111, 107, 101, 110, 109, 0, 0, 0, 72, 100, 99, 56, 48, 49, 97, 100, 102,
  45, 54, 49, 99, 54, 45, 52, 48, 51, 49, 45, 97, 98, ...>>

This is of length 101, however my session keys are 2 concatenated UUIDs like this:

[
  %LLServer.Schema.UserSession{
    __meta__: #Ecto.Schema.Metadata<:loaded, "usersession">,
    id: 1,
    inserted_at: ~N[2023-01-16 13:48:04],
    session: "dc801adf-61c6-4031-ab0c-4fa93012e9d865754857-90d3-4e19-a101-845b4d1b13f5",
    updated_at: ~N[2023-01-16 13:48:04],
    user: #Ecto.Association.NotLoaded<association :user is not loaded>,
    user_id: 1
  }
]

The decryption function seems to work, but I have no idea how to decode that bitstring into the actual session token. The way I put it into the cookies is by calling put_session with a key of :session_token, so I’m assuming that is also saved in the session. How can I retrieve the map just as if I was using Plug.Conn.get_session/1 ?

Showing Posts 1 to 6

voltone

voltone

You need to call :erlang.binary_to_term/1 on that. Or, better: Plug.Crypto.non_executable_binary_to_term(session, [:safe])

hawkyre

hawkyre OP

Thanks a lot! I may do a simple library in the future with all the stuff in here just in case someone else also needs this kind of functionality because it feels weird to not see it already solved.

LostKobrakai

LostKobrakai

All of that code is in plug, so not sure why you feel this is “not solved”. It’s not really clear what you’re doing, but there’s reasons why cookies (and therefore sessioon data) are not just exposed to channels (over websockets) in phoenix – these cannot be properly secured.

Phoenix.Endpoint — Phoenix v1.7.0-rc.2 shows a (seemingly new) session option, which could allow you to access session data when using a CSRF token on the websocket request, which works around those security concerns.

mtrudel

mtrudel

Creator of Bandit

You may want to take a look at the WebSock & WebSockAdapter libraries, along with the new upgrade_adapter/3 function added in Plug 1.14. This is the stack that Phoenix is based on as of 1.7, and comprises a fully open standard WebSocket support in both Bandit and Cowboy web servers.

For what you’re looking to do, you’d want to grab the relevant parts of the Plug.Conn request as part of your HTTP handler for your WebSocket upgrade, and then pass those to your WebSock implementation as the second element of the arg tuple to upgrade_adapter/3. Your WebSock implementation will then see those values as the value to its init/1 call.

Howto docs are a little bit sparse at the moment, but some of the test servers in the Bandit test suite (such as this one) should probably be pretty instructive.

hawkyre

hawkyre OP

I’m not using phoenix, and I’m just using this in the initial connection to the websocket route, not for sending messages once the websocket connection has been established.

hawkyre

hawkyre OP

Oh hey that looks pretty interesting; I’ll definitely check it out. Thanks!

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews