How to pass `style_nonce` or `img_nonce` to socket.assigns, to make inline style work with CSP?

Hey Guys,

I have a peculiar requirement, i.e. I want to use inline style or maybe a style directive, for styling source code.

I prefer using Chroma for source code highlighting as it has support for lots of languages & has lots of styling.

Here’s the demo: Chroma Playground (v2.8.0)

Now that I’m moving my blog to Elixir & Phoenix. How do I make the inline style work with CSP?

I’m unable to pass the style_nonce to the blog live view!!

Here’s the CSP Header Plug:

defmodule DerpyToolsWeb.Plugs.CustomSecureBrowserHeaders do
  def init(options), do: options

  def call(conn, _opts) do
    img_nonce = generate_nonce()
    style_nonce = generate_nonce()
    script_nonce = generate_nonce()

    csp_headers =

    |> Plug.Conn.assign(:csp_img_nonce, img_nonce)
    |> Plug.Conn.assign(:csp_style_nonce, style_nonce)
    |> Plug.Conn.assign(:csp_script_nonce, script_nonce)
    |> Phoenix.Controller.put_secure_browser_headers(csp_headers)

  def csp_headers(img_nonce, script_nonce) do
    csp_content =
      case Application.fetch_env!(:derpy_tools, :env) do
        # :prod -> connect-src wss:// wss://;
        # :stg -> connect-src wss:// wss://;
        :dev ->
          default-src 'self' data: https://*;
          font-src 'self' data: https://*;
          style-src 'self' data: https://* 'unsafe-inline';
          img-src 'self' data: https://* 'nonce-#{img_nonce}';
          script-src 'self' https://* 'nonce-#{script_nonce}';
          connect-src wss:// wss:// wss:// wss:// wss://localhost:* ws://localhost:*;

        _ ->

    case csp_content do
      nil -> %{}
      csp_content -> %{"content-security-policy" => csp_content |> String.replace("\n", "")}

  defp generate_nonce(size \\ 10),
    do: size |> :crypto.strong_rand_bytes() |> Base.url_encode64(padding: false)

Here’s how I use it:

<script nonce={assigns[:csp_script_nonce]}>
  localStorage.getItem("dark_mode") === "true" && document.documentElement.classList.add("dark");

I tried to pass it the way csrf_token is passed, using get_connect_params, but that doesn’t work as the value is available only when socket is connected.

Is there another way?

Or should I stick to global styling for code highlighting?

1 Like

Figured it out:

    |> Plug.Conn.put_session(:img_nonce, img_nonce)
    |> Plug.Conn.put_session(:style_nonce, style_nonce)
    |> Plug.Conn.put_session(:script_nonce, script_nonce)
    |> Plug.Conn.assign(:img_nonce, img_nonce)
    |> Plug.Conn.assign(:style_nonce, style_nonce)
    |> Plug.Conn.assign(:script_nonce, script_nonce)
    |> Phoenix.Controller.put_secure_browser_headers(csp_headers)

Then those values can be read from session in on_mount.

Image nonce doesn’t work though:


I thought that I will be able to allow some images from sites by using a nonce in img tag, but it didn’t work.

Anyone uses nonce with images? Or you allow all images in CSP?

1 Like