メッセージバスのみクロスオリジンリクエストを許可

ウェブサイトに埋め込めるチャットウィジェットを実装しようとしており、そのためにウィジェットとRailsバックエンド間の通信にMessageBusを使用することにしました。任意のオリジンに埋め込むことができるため、クロスオリジンリクエストを処理する必要がありました。

ただし、他のすべてのルートではなく、MessageBusリクエストに対してのみCORSを有効にしたいと考えています。この問題はすでに確認済みです:CORS configuration · Issue #135 · discourse/message_bus · GitHub

config/initializers/cors.rbで次のように設定しました。

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options]
  end
end

これは機能しますが、これは私が望んでいないすべてのルートに対してクロスオリジンリクエストを許可してしまいます。

独自のミドルウェアも試しました。

# app/middleware/message_bus_cors_middleware.rb
class MessageBusCorsMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    # Message Busであり、OPTIONSリクエストの場合はCORSヘッダーを返します
    if env['PATH_INFO'].start_with?('/message-bus') && env['REQUEST_METHOD'] == 'OPTIONS'
      # /message-busリクエストにCORSヘッダーを適用します
      headers = {
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Methods' => 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD',
        'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Visitor-Token',
        'Access-Control-Max-Age' => '86400'
      }
      [200, headers, []]
    else
      @app.call(env)
    end
  end
end

しかし、このミドルウェアでもクロスオリジンに関する問題がまだ発生しています。

他に何かアイデアはありますか?

また、特定のチャネル(私の場合はチャットのMessageBusチャネル)に対してのみクロスオリジンを有効にできると素晴らしいです。アプリケーションの他の部分でMessageBusを使用しており、クロスオリジンリクエストを行っていないため、そのように保ちたいと考えています。

「いいね!」 2

カスタムミドルウェアを使用して、メッセージバスリクエストのみ CORS を有効にすることで、機能させることができました。問題は access-control-allow-headers が正しく設定されていなかったことです。修正は、レスポンスの access-control-allow-headers をリクエストヘッダーの Access-Control-Request-Headers で埋めることでした。

以下が最終的な実装です。

class MessageBusCorsMiddleware
  HTTP_ORIGIN = 'HTTP_ORIGIN'
  HTTP_X_ORIGIN = 'HTTP_X_ORIGIN'

  HTTP_ACCESS_CONTROL_REQUEST_METHOD = 'HTTP_ACCESS_CONTROL_REQUEST_METHOD'
  HTTP_ACCESS_CONTROL_REQUEST_HEADERS = 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS'
  OPTIONS = 'OPTIONS'
  REQUEST_METHOD = 'REQUEST_METHOD'
  PATH_INFO      = 'PATH_INFO'

  def initialize(app)
    @app = app
  end

  def call(env)
    # メッセージバスであり、OPTIONSリクエストの場合はCORSヘッダーを返す
    if env[PATH_INFO].start_with?('/message-bus') && env[REQUEST_METHOD] == OPTIONS && env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
      origin = env['HTTP_ORIGIN']
      # /message-bus リクエストにCORSヘッダーを適用
      headers = {
        "access-control-allow-origin" => "*",
        "access-control-allow-methods" => "GET, POST, PUT, PATCH, DELETE, OPTIONS",
        "access-control-expose-headers" => "",
        "access-control-max-age" => "7200"
      }

      headers.merge!('access-control-allow-headers' => env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]) if env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
      [200, headers, []]
    else
      @app.call(env)
    end
  end
end

HTTP ヘッダー「Access-Control-Request-Headers」の役割を理解するのに苦労していたため、ChatGPT に質問しました。

HTTP ヘッダー「Access-Control-Request-Headers」は、クロスオリジンリソース共有(CORS)リクエストのコンテキストで使用されます。CORS は、Web アプリケーションがアプリケーションの発信元とは異なるドメインにリクエストを送信できるようにするメカニズムです。

Web アプリケーションがクロスオリジンリクエストを行う場合、通常は実際の送信が許可されているかどうかを判断するために、サーバーに初期の「プリフライト」リクエストを送信します。プリフライトリクエストには、「Access-Control-Request-Headers」ヘッダーが含まれており、実際の送信に含めたい追加のヘッダーがリストされています。

たとえば、Web アプリケーションがクロスオリジンリクエストに「X-Auth-Token」や「Authorization」のようなカスタムヘッダーを含めたい場合、プリフライトリクエストの「Access-Control-Request-Headers」ヘッダーにそれらのヘッダーを指定します。

プリフライトリクエストを受信するサーバーは、「Access-Control-Request-Headers」ヘッダーを検査して、要求されたヘッダーが実際の送信に許可されているかどうかを判断できます。サーバーが要求されたヘッダーを承認した場合、プリフライトレスポンスで適切な Access-Control-Allow-Headers ヘッダーを返信し、Web アプリケーションが実際の送信に進むことを許可します。

要約すると、「Access-Control-Request-Headers」は、Web アプリケーションがクロスオリジンリクエストに含めたい追加ヘッダーを指定するために、CORS プリフライトリクエストで使用される HTTP ヘッダーです。

もし他の人が同じことを達成しようとしている場合に役立つかと思い、これを共有します。また、この実装が開く可能性のあるセキュリティ上の問題に関して、フィードバックがあれば歓迎します。

「いいね!」 1

ドキュメントに従って、追加のレスポンスヘッダーを追加できます。

例: config/initializers/message_bus.rb で:

MessageBus.extra_response_headers_lookup do |env|
  [
    ["Access-Control-Allow-Origin", "http://example.com:3000"],
  ]
end
「いいね!」 1

言い忘れましたが、これが最初に試したことで、うまくいきませんでした。しかし、それは Access-Control-Allow-Headers について知る前のことです。このように追加すればうまくいくかもしれません。


MessageBus.extra_response_headers_lookup do |env|
  [
    ["Access-Control-Allow-Origin", "http://example.com:3000"],
    ["Access-Control-Allow-Headers", env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]]
  ]
end

代わりにカスタムミドルウェアを使用せずに、この方法で再度試してみて、このスレッドを更新します。

それでもうまくいきませんでした。今のところ、カスタムミドルウェアを使用しています。