WebSocket server doesn't properly handle protocol mistakes.

オープン
#669 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
45/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
停滞
技術スタック
scala
領域
networking

調査の方向性

package.scala:26 の org.http4s.websocket.package.makeFrame から始め、失敗する前に FrameTranscoder.bufferToFrame が opcode をどのように処理するかを追跡します。予約済み opcode の動作を RFC 6455 section 5.2 と比較します。完了の条件は、不正な WebSocket フレームが MatchError を露出させずに接続を失敗させ、理解しやすい処理を生成することです。

索引モデルが issue の本文から書いたものです。

説明

bug module:blaze-server

Unfortunately, I don't know how exactly to reproduce this error.

The problem is that when I ran a local WebSocket server it was closing all incoming connections and printing the next stack trace:

scala.MatchError: 7 (of class java.lang.Integer)
    at org.http4s.websocket.package$.makeFrame(package.scala:26)
    at org.http4s.websocket.FrameTranscoder.bufferToFrame(FrameTranscoder.scala:165)
    at org.http4s.server.blaze.WebSocketDecoder.bufferToMessage(WebSocketDecoder.scala:30)
    at org.http4s.blaze.pipeline.stages.ByteToObjectStage.$anonfun$readAndDecodeLoop$1(ByteToObjectStage.scala:85)
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:60)
    at org.http4s.blaze.util.Execution$ThreadLocalTrampoline.run(Execution.scala:108)
    at org.http4s.blaze.util.Execution$ThreadLocalTrampoline.execute(Execution.scala:94)
    at org.http4s.blaze.util.Execution$$anon$2.execute(Execution.scala:53)
    at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:68)
    at scala.concurrent.impl.Promise$DefaultPromise.dispatchOrAddCallback(Promise.scala:312)
    at scala.concurrent.impl.Promise$DefaultPromise.onComplete(Promise.scala:303)
    at org.http4s.blaze.pipeline.stages.ByteToObjectStage.readAndDecodeLoop(ByteToObjectStage.scala:99)
    at org.http4s.blaze.pipeline.stages.ByteToObjectStage.startReadDecode(ByteToObjectStage.scala:72)
    at org.http4s.blaze.pipeline.stages.ByteToObjectStage.readRequest(ByteToObjectStage.scala:68)
    at org.http4s.blaze.pipeline.stages.ByteToObjectStage.readRequest$(ByteToObjectStage.scala:56)
    at org.http4s.server.blaze.WebSocketDecoder.readRequest(WebSocketDecoder.scala:8)
    at org.http4s.blaze.pipeline.Tail.channelRead(Stages.scala:88)
    at org.http4s.blaze.pipeline.Tail.channelRead$(Stages.scala:85)
    at org.http4s.server.blaze.WSFrameAggregator.channelRead(WSFrameAggregator.scala:16)
    at org.http4s.server.blaze.WSFrameAggregator.readRequest(WSFrameAggregator.scala:24)
    at org.http4s.blaze.pipeline.Tail.channelRead(Stages.scala:88)
    at org.http4s.blaze.pipeline.Tail.channelRead$(Stages.scala:85)
    at org.http4s.blazecore.websocket.Http4sWSStage.channelRead(Http4sWSStage.scala:18)
    at org.http4s.blazecore.websocket.Http4sWSStage.$anonfun$readFrameTrampoline$1(Http4sWSStage.scala:54)
    at org.http4s.blazecore.websocket.Http4sWSStage.$anonfun$readFrameTrampoline$1$adapted(Http4sWSStage.scala:53)
    at monix.eval.internal.TaskCreate$.$anonfun$async$1(TaskCreate.scala:124)
    at monix.eval.internal.TaskCreate$.$anonfun$async$1$adapted(TaskCreate.scala:121)
    at monix.eval.internal.TaskRestartCallback.start(TaskRestartCallback.scala:58)
    at monix.eval.internal.TaskRunLoop$.executeAsyncTask(TaskRunLoop.scala:564)
    at monix.eval.internal.TaskRunLoop$.startFull(TaskRunLoop.scala:115)
    at monix.eval.internal.TaskRunLoop$.$anonfun$restartAsync$1(TaskRunLoop.scala:192)
    at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

Seems that it doesn't properly handle deviations from the protocol and throws an error in case if a received opcode doesn't match to the expected valid set of opcodes.

I've checked the WebSockets RFC and it says that a server must close a connection and return 400 in case of an handshake error - https://tools.ietf.org/html/rfc6455#section-4.2.1

Also, it says that opcodes from 3 to 7 are reserved for the future usage and I didn't find information on how they should be handled exactly. However, there is a sentence: If an unknown opcode is received, the receiving endpoint MUST _Fail the WebSocket Connection_., though not sure if it's applicable to them - https://tools.ietf.org/html/rfc6455#section-5.2

Probably at least server could handle such opcodes and print something less scary and more understandable for users.

主要言語
Scala
スター
371
フォーク
63
平均マージ
7時間 47分
マージ済み PR(30日)
5

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

http4s/blaze のほかの issue

http4s/blaze の issue をすべて見る

似ている issue

Scala の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。