Cross-Origin-Anfragen nur für die Message Bus zulassen

Ich versuche, ein Chat-Widget zu implementieren, das in jede Website eingebettet werden kann, und dafür habe ich mich entschieden, MessageBus für die Kommunikation zwischen dem Widget und meinem Rails-Backend zu verwenden. Da es in jeden Ursprung eingebettet werden kann, musste ich mich mit Cross-Origin-Anfragen befassen.

Ich möchte jedoch CORS nur für Message-Bus-Anfragen und nicht für alle meine anderen Routen aktivieren. Ich habe dieses Problem bereits gesehen: CORS configuration · Issue #135 · discourse/message_bus · GitHub

Das habe ich in meiner config/initializers/cors.rb gemacht.

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

Es funktioniert, aber dies erlaubt Cross-Origin-Anfragen für jede Route, was nicht das ist, was ich will.

Ich habe auch versucht, meine eigene Middleware zu erstellen:

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

  def call(env)
    # Wenn es sich um Message Bus und eine OPTIONS-Anfrage handelt, gib CORS-Header zurück
    if env['PATH_INFO'].start_with?('/message-bus') && env['REQUEST_METHOD'] == 'OPTIONS'
      # Wende CORS-Header für /message-bus-Anfragen an
      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

Aber mit dieser Middleware habe ich immer noch Cross-Origin-Probleme.

Gibt es noch eine andere Idee?

Eine weitere Sache, die großartig wäre, ist, Cross-Origin nur für bestimmte Kanäle zu aktivieren. (In meinem Fall die Chat-Message-Bus-Kanäle). Ich verwende MessageBus in anderen Teilen meiner Anwendung, die keine Cross-Origin-Anfragen durchführen, und ich möchte das beibehalten.

2 „Gefällt mir“

Ich konnte es mit der zweiten Methode zum Laufen bringen (benutzerdefinierte Middleware, um CORS nur für Nachrichtenbusanfragen zu aktivieren). Das Problem lag bei den access-control-allow-headers, die nicht richtig gesetzt waren. Die Lösung bestand darin, die access-control-allow-headers der Antwort mit den Access-Control-Request-Headers der Anfrage zu füllen.

Hier ist die endgültige Implementierung:

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)
    # Wenn es sich um einen Nachrichtenbus und eine OPTIONS-Anfrage handelt, werden CORS-Header zurückgegeben
    if env[PATH_INFO].start_with?('/message-bus') && env[REQUEST_METHOD] == OPTIONS && env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
      origin = env['HTTP_ORIGIN']
      # CORS-Header für /message-bus-Anfragen anwenden
      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

Ich hatte Schwierigkeiten, die Rolle des HTTP-Headers “Access-Control-Request-Headers” zu verstehen, also habe ich ChatGPT gefragt:

Der HTTP-Header “Access-Control-Request-Headers” wird im Kontext von Cross-Origin Resource Sharing (CORS)-Anfragen verwendet. CORS ist ein Mechanismus, der es Webanwendungen ermöglicht, Anfragen an eine andere Domäne als die, von der die Anwendung stammt, zu stellen.

Wenn eine Webanwendung eine Cross-Origin-Anfrage stellt, sendet sie normalerweise eine anfängliche “Preflight”-Anfrage an den Server, um festzustellen, ob die eigentliche Anfrage zulässig ist. Die Preflight-Anfrage enthält den Header “Access-Control-Request-Headers”, der die zusätzlichen Header auflistet, die die Anwendung in die eigentliche Anfrage aufnehmen möchte.

Wenn eine Webanwendung beispielsweise benutzerdefinierte Header wie “X-Auth-Token” oder “Authorization” in ihre Cross-Origin-Anfrage aufnehmen möchte, würde sie diese Header im Header “Access-Control-Request-Headers” der Preflight-Anfrage angeben.

Der Server, der die Preflight-Anfrage empfängt, kann dann den Header “Access-Control-Request-Headers” überprüfen, um festzustellen, ob die angeforderten Header für die eigentliche Anfrage zulässig sind. Wenn der Server die angeforderten Header genehmigt, antwortet er mit dem entsprechenden Header “Access-Control-Allow-Headers” in der Preflight-Antwort, wodurch die Webanwendung mit der eigentlichen Anfrage fortfahren kann.

Zusammenfassend lässt sich sagen, dass “Access-Control-Request-Headers” ein HTTP-Header ist, der in CORS-Preflight-Anfragen verwendet wird, um die zusätzlichen Header anzugeben, die eine Webanwendung in ihre Cross-Origin-Anfrage aufnehmen möchte.

Ich teile dies für den Fall, dass jemand anderes versucht, dasselbe zu erreichen. Ich bin auch offen für Feedback, insbesondere in Bezug auf Sicherheitsprobleme, die diese Implementierung eröffnen kann.

1 „Gefällt mir“

Sie können zusätzliche Antwortheader gemäß der Dokumentation hinzufügen:

In z. B. config/initializers/message_bus.rb:

MessageBus.extra_response_headers_lookup do |env|
  [
    ["Access-Control-Allow-Origin", "http://example.com:3000"],
  ]
end
1 „Gefällt mir“

Ich habe vergessen zu erwähnen, aber das ist das Erste, was ich versucht habe und es hat nicht funktioniert. Aber das war, bevor ich von Access-Control-Allow-Headers erfahren habe. Vielleicht funktioniert es, wenn ich sie so hinzufüge.


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

Ich werde es stattdessen auf diese Weise versuchen und diesen Thread aktualisieren.

Das hat auch nicht geklappt. Vorerst habe ich die benutzerdefinierte Middleware beibehalten.