Consenti richieste cross-origin solo per message bus

Sto cercando di implementare un widget di chat che possa essere incorporato in qualsiasi sito Web e per questo ho deciso di utilizzare MessageBus per comunicare tra il widget e il mio backend Rails. Poiché può essere incorporato in qualsiasi origine, ho dovuto gestire le richieste cross-origin.

Tuttavia, voglio abilitare CORS solo per le richieste di message bus e non per tutte le mie altre route. Ho già visto questo problema CORS configuration · Issue #135 · discourse/message_bus · GitHub

Questo è ciò che ho fatto nel mio 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

Funziona, ma consente le richieste cross-origin per qualsiasi route, che non è quello che voglio.

Ho anche provato a creare il mio middleware

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

  def call(env)
    # Se si tratta di message bus e di una richiesta OPTIONS, restituisci le intestazioni CORS
    if env['PATH_INFO'].start_with?('/message-bus') && env['REQUEST_METHOD'] == 'OPTIONS'
      # Applica le intestazioni CORS per le richieste /message-bus
      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

Ma ho ancora problemi cross-origin con questo middleware.

Qualche altra idea?

Un’altra cosa che sarebbe fantastica è abilitare il cross-origin solo per canali specifici. (Nel mio caso i canali message bus della chat). Ho usato MessageBus in altre parti della mia applicazione che non fanno richieste cross-origin e vorrei mantenerle così.

2 Mi Piace

Sono riuscito a farlo funzionare usando il secondo metodo (middleware personalizzato per abilitare cors solo per le richieste Message Bus). Il problema era su access-control-allow-headers che non erano impostati nel modo giusto. La soluzione è stata popolare access-control-allow-headers della risposta con gli header della richiesta Access-Control-Request-Headers.

Ecco l’implementazione finale:

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)
    # Se è message bus e una richiesta OPTIONS, restituisci gli header CORS
    if env[PATH_INFO].start_with?('/message-bus') && env[REQUEST_METHOD] == OPTIONS && env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
      origin = env['HTTP_ORIGIN']
      # Applica gli header CORS per le richieste /message-bus
      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

Ho avuto problemi a capire il ruolo dell’header HTTP “Access-Control-Request-Headers”, quindi ho chiesto a ChatGPT:

L’header HTTP “Access-Control-Request-Headers” viene utilizzato nel contesto delle richieste Cross-Origin Resource Sharing (CORS). CORS è un meccanismo che consente alle applicazioni web di effettuare richieste a un dominio diverso da quello da cui l’applicazione è originata.

Quando un’applicazione web effettua una richiesta cross-origin, in genere invia una richiesta preliminare (“preflight”) al server per determinare se la richiesta effettiva è consentita. La richiesta preliminare include l’header “Access-Control-Request-Headers”, che elenca gli header aggiuntivi che l’applicazione desidera includere nella richiesta effettiva.

Ad esempio, se un’applicazione web desidera includere header personalizzati come “X-Auth-Token” o “Authorization” nella sua richiesta cross-origin, specificherà tali header nell’header “Access-Control-Request-Headers” della richiesta preliminare.

Il server che riceve la richiesta preliminare può quindi ispezionare l’header “Access-Control-Request-Headers” per determinare se gli header richiesti sono consentiti per la richiesta effettiva. Se il server approva gli header richiesti, risponde con l’header appropriato Access-Control-Allow-Headers nella risposta preliminare, consentendo all’applicazione web di procedere con la richiesta effettiva.

In sintesi, “Access-Control-Request-Headers” è un header HTTP utilizzato nelle richieste preliminari CORS per specificare gli header aggiuntivi che un’applicazione web desidera includere nella sua richiesta cross-origin.

Sto condividendo questo nel caso in cui qualcun altro stia cercando di ottenere la stessa cosa. Sono anche aperto a feedback, specialmente per quanto riguarda i problemi di sicurezza che questa implementazione potrebbe aprire.

1 Mi Piace

Puoi aggiungere intestazioni di risposta aggiuntive per la documentazione:

Ad esempio, in config/initializers/message_bus.rb:

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

Ho dimenticato di menzionare, ma questa è la prima cosa che ho provato e non ha funzionato. Ma questo è stato prima che scoprissi Access-Control-Allow-Headers. Forse funzionerà se li aggiungo in questo modo.


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

Proverò di nuovo in questo modo invece del middleware personalizzato e aggiornerò questo thread.

Nessuna fortuna nemmeno con quello. Per ora ho mantenuto il middleware personalizzato.