web-dev-qa-db-ja.com

認証のためにauth_tokenをActionCableに送信します

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      #puts params[:auth_token]
      self.current_user = find_verified_user
      logger.add_tags 'ActionCable', current_user.name
   end

  end
end

アクションケーブルのエンドポイントとしてwebを使用していないので、auth_tokenを認証に使用したいと思います。デフォルトでは、アクションケーブルは認証にセッションユーザーIDを使用します。接続メソッドにパラメーターを渡す方法は?

18
alxibra

認証トークンをクエリパラメータとして送信できました。

JavaScriptアプリでコンシューマーを作成するとき、次のようにケーブルサーバーのURLでトークンを渡します。

wss://myapp.com/cable?token=1234

私のケーブル接続では、request.paramsにアクセスすることでこれを取得できますtoken

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
      logger.add_tags 'ActionCable', current_user.name
    end

    protected:
    def find_verified_user
      if current_user = User.find_by(token: request.params[:token])
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

明らかに理想的ではありませんが、WebSocketを作成するときにカスタムヘッダーを送信できないと思います。

44
Pierre Fraisse

ピエールの答え 機能します。ただし、アプリケーションでこれらのパラメーターを期待することについて明示することは良い考えです。

たとえば、構成ファイルの1つ(例:application.rbdevelopment.rbなど...)これを行うことができます:

config.action_cable.mount_path = '/cable/:token'

そして、あなたのConnectionクラスからそれにアクセスするだけです:

request.params[:token]
15
Yuval Karmi

残念ながらWebSocket接続では、追加のヘッダーとカスタムヘッダーはサポートされていません1 ほとんどで2 websocketクライアントおよびサーバー。したがって、可能なオプションは次のとおりです。

  • URLパラメータとしてアタッチし、サーバーで解析する

    path.to.api/cable?token=1234
    
    # and parse it like
    request.params[:token]
    

短所:ログに記録され、サーバーにアクセスできる他のユーザーが利用できるシステムプロセス情報が増える可能性があるため、脆弱性がある ここ

解決策:トークンを暗号化して添付するため、ログで確認できたとしても、復号化されるまでは意味がありません。

  • 許可されているパラメータの1つにJWTをアタッチします。

クライアント側:

# Append jwt to protocols
new WebSocket(url, existing_protocols.concat(jwt))

これを行うReactおよびReact-NativeのJSライブラリ action-cable-react-jwt を作成しました。お気軽にご利用ください。

サーバ側:

# get the user by 
# self.current_user = find_verified_user

def find_verified_user
  begin
    header_array = self.request.headers[:HTTP_SEC_WEBSOCKET_PROTOCOL].split(',')
    token = header_array[header_array.length-1]
    decoded_token = JWT.decode token, Rails.application.secrets.secret_key_base, true, { :algorithm => 'HS256' }
    if (current_user = User.find((decoded_token[0])['sub']))
      current_user
    else
      reject_unauthorized_connection
    end
  rescue
    reject_unauthorized_connection
  end
end

1 ほとんどのWebsocket API( Mozilla's を含む)は以下のものと同じです:

WebSocketコンストラクターは、1つの必須パラメーターと1つのオプションパラメーターを受け入れます。

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString[] protocols
);

url

接続するURL。これは、WebSocketサーバーが応答するURLである必要があります。

protocolsオプション

単一のプロトコル文字列またはプロトコル文字列の配列。これらの文字列はサブプロトコルを示すために使用されるため、単一のサーバーで複数のWebSocketサブプロトコルを実装できます(たとえば、1つのサーバーで、指定したプロトコルに応じて異なるタイプの対話を処理できるようにする場合があります)。プロトコル文字列を指定しない場合、空の文字列が想定されます。

2 常に例外があります。たとえば、このnode.js lib ws はカスタムヘッダーの作成を許可するため、通常のAuthorization: Bearer tokenヘッダーを使用してサーバー上で解析できますが、クライアントとサーバーの両方で解析できますwsを使用する必要があります。

すでにコメントで述べたように、受け入れられた答えは良いアイデアではありません、これは、慣例により、URLは禁止されているためですそのような機密データを含む。詳細については、こちらをご覧ください: https://tools.ietf.org/html/rfc6750#section-5. (ただし、これは特にOAuthに関するものです)。

ただし、別のアプローチがあります:ws urlを介してHTTP基本認証を使用します。ほとんどのWebSocketクライアントでは、URLの先頭に次のようにhttp基本認証を付加することで、ヘッダーを暗黙的に設定できることがわかりました:_wss://user:[email protected]/cable_。

これにより、_Authorizationヘッダーに_Basic ..._の値が追加されます。私の場合、私は devisedevise-jwt と一緒に使用していて、jwtをAuthorizationヘッダー。だから私は次のようにURLを設定します:_wss://[email protected]/cable_ヘッダーをこれに設定します(疑似):Basic base64("token:")そして戦略でそれを解析します。

4
phikes

以前の回答に追加するには、JWTをパラメーターとして使用した場合、少なくともbtoa(your_token) @jsおよびBase64.decode64(request.params[:token]) @Rails as Railsはドット「。」を区切り文字と見なすため、トークンは@Railsのparams側で切り捨てられます

1
mmln

もう1つの方法(最後に他の答えではなくそれを行った方法)は、チャンネルでauthenticateアクションを実行することです。これを使用して現在のユーザーを特定し、接続/チャネルに設定しました。すべてのものはWebSocketを介して送信されるため、暗号化されている場合(つまり、wss)、資格情報はここでは問題になりません。

1
phikes

ActionCable.createCustomerを使用したい場合。しかし、私と同じように再生可能なトークンを持っています:

const consumer = ActionCable.createConsumer("/cable")
const consumer_url = consumer.url
Object.defineProperty(
  consumer, 
  'url', 
  {
      get: function() { 
        const token = localStorage.getItem('auth-token')
        const email = localStorage.getItem('auth-email')
        return consumer_url+"?email="+email+"&token="+token
      }
  });
return consumer; 

その後、接続が失われた場合は、新しい新しいトークンで開かれます。

1
Jan Topiński

Pierre's answer のセキュリティについて:暗号化にSSLを使用するWSSプロトコルを使用している場合、安全なデータを送信するための原則はHTTPSと同じです。 SSLを使用する場合、クエリ文字列パラメーターとリクエストの本文が暗号化されます。したがって、HTTP APIでHTTPSを介してあらゆる種類のトークンを送信し、それが安全であると考える場合、WSSでも同じである必要があります。 HTTPSの場合と同様に、リクエストのURLがサーバーにログオンしてパスワードとともに保存される可能性があるため、クエリパラメータを介してパスワードなどの資格情報を送信しないでください。代わりに、サーバーが発行するトークンなどを使用します。

また、これを確認することもできます(これは基本的にJWT認証+ IPアドレス検証のようなものを説明しています) https://devcenter.heroku.com/articles/websocket-security#authentication-authorization

0