Phoenix Liveview - Websocket connect_info 阻止 iframe 内容

问题描述 投票:0回答:2

我实际上正在开发一个phoenix应用程序,我需要在嵌入在iframe上的一些网站中渲染它,我完成了逻辑编码,但我发现它只能在Firefox上运行,当在Chome或Opera上使用它时,它结束于无限循环充电尝试渲染内容,抛出以下警告:

enter image description here

我试图通过像这样的额外选项来允许这种情况,但没有成功。

  @session_options [
    store: :cookie,
    key: "_analytics_key",
    signing_salt: "BM3P8GYS",
    extra: "SameSite=None;",
  ]

然后我发现在 Endpoint 的最后一个版本中,它有一个名为 same_site 的 cookie 特定选项,所以我尝试了这样的操作,但得到了相同的结果:

  @session_options [
    store: :cookie,
    key: "_analytics_key",
    signing_salt: "BM3P8GYS",
    same_site: "None",
    #extra: "SameSite=None;",
    secure: true
  ]

每次渲染失败时,我都会在控制台上看到以下日志: enter image description here

一切似乎都有效,但我发现从端点上的 websocket 中删除 connect_info 会自动解决问题,就像这样:

# socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
  socket "/live", Phoenix.LiveView.Socket, websocket: []

但这会影响监护人之类的东西,我猜还会影响一些安全问题,所以我一直在寻找一种方法来仅在尝试在 iframe 上渲染页面时删除它,我正在考虑使用一个插件来执行此操作,但我不这样做不知道这对于这个特定部分是否可能,也许有人知道我可以在这里做些什么来完成我想要的事情?预先感谢!

elixir phoenix-live-view
2个回答
1
投票

嵌入其他页面/站点时,您需要配置 csp 标头。 保留 connect_info 为默认值。

defmodule UtasksWeb.Plugs.Csp do
  import Plug.Conn
  import Phoenix.Controller

  def init(opts), do: opts

  def call(conn, _opts) do
    put_resp_header conn, "content-security-policy", csp(conn)
  end

  defp csp(conn) do
    "default-src 'self' *.googleapis.com *.gstatic.com; \
    connect-src 'self' #{ws_url conn} #{ws_url conn, "wss"}; \
    script-src 'self' 'unsafe-inline' 'unsafe-eval' https://statics.teams.cdn.office.net; \
    style-src 'self' 'unsafe-inline' 'unsafe-eval' *.googleapis.com *.gstatic.com; \
    frame-ancestors teams.microsoft.com *.teams.microsoft.com *.skype.com"
  end

  defp ws_url(conn, protocol \\ "ws") do
    endpoint = Phoenix.Controller.endpoint_module(conn)
    %{endpoint.struct_url | scheme: protocol} |> URI.to_string()
  end
end

0
投票

我在 Elixir 论坛上发布了一个通过 iframe 嵌入 LiveView 的解决方案,我在这里总结一下。

假设您想要访问的页面不需要身份验证,则有 3 个步骤:

  1. 为 iframe 发起的请求添加单独的 LiveView 套接字:
# endpoint.ex

socket "/embed/live", Phoenix.LiveView.Socket, websocket: true, longpoll: true
  1. 更新您的 JavaScript 入口点以连接到适当的端点:
# app.js

let socketUrl = window.location.pathname.startsWith("/embed/") ? "/embed/live" : "/live"
let liveSocket = new LiveSocket(socketUrl, Socket, {...})
  1. 创建一个新管道,设置 HTTP 标头以告诉浏览器允许它们在 iframe 内渲染路由:
# router.ex

  # Similar to default `:browser` pipeline, but with one more plug
  # `:allow_iframe` to securely allow embedding in an iframe.
  pipeline :embedded do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {MyAppWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :allow_iframe
  end

  # A plug to set a CSP allowing embedding only on certain domains.
  # This is just an example, actual implementation depends on project
  # requirements.
  defp allow_iframe(conn, _opts) do
    conn
    |> delete_resp_header("x-frame-options")
    |> put_resp_header(
      "content-security-policy",
      "frame-ancestors 'self' https://example.com" # Add your list of allowed domain(s) here
    )
  end

  # Configure LiveView routes using the `:embedded` pipeline
  # and custom `embedded.html.heex` layout.
  scope "/embed", MyAppWeb do
    pipe_through [:embedded]

    live_session :embedded,
      layout: {MyAppWeb.Layouts, :embedded} do
      live "/:id", EmbeddedLive
    end
  end

确保您的布局和可嵌入的 LiveView 不依赖于会话信息。


上面的步骤 1 解决了重新加载页面的“无限循环”问题,而无需更改影响应用程序其余部分的 cookie 设置(感谢 Phoenix 创建者 Chris McCord,他在 GitHub 问题 上建议了这一点)。

© www.soinside.com 2019 - 2024. All rights reserved.