如何在 Java 中解析和验证 WebSocket 帧?

2023-12-01

我用Java编写了一个WebSocket帧解码器:

private byte[] decodeFrame(byte[] _rawIn) {
        int maskIndex = 2;
        byte[] maskBytes = new byte[4];

        if ((_rawIn[1] & (byte) 127) == 126) {
            maskIndex = 4;
        } else if ((_rawIn[1] & (byte) 127) == 127) {
            maskIndex = 10;
        }

        System.arraycopy(_rawIn, maskIndex, maskBytes, 0, 4);

        byte[] message = new byte[_rawIn.length - maskIndex - 4];

        for (int i = maskIndex + 4; i < _rawIn.length; i++) {
            message[i - maskIndex - 4] = (byte) (_rawIn[i] ^ maskBytes[(i - maskIndex - 4) % 4]);
        }

        return message;
    }

它有效,但我不知道如何验证帧以确保它仅解码有效帧。

协议描述http://tools.ietf.org/html/rfc6455不幸的是,它并没有透露太多关于帧验证的信息。


解析原始 websocket 框架非常简单。 但您必须一次检查一个字节的标头。

这是一个粗略的例子:

我留下了一些 TODO 供您自行解决(当然是在阅读 RFC-6455 规范之后)

您可以验证的事情:

基本成帧协议:RFC-6455 - 第 5.2 节

  • 操作码是否是规范中定义的有效操作码之一?
  • RSV 位是否使用不当?

客户端到服务器屏蔽:RFC 6455 - 第 5.3 节

  • 如果帧是由客户端发送的,该帧是否被屏蔽?
  • 蒙版在帧与帧之间是随机的吗?
  • 不允许使用 [0x00, 0x00, 0x00, 0x00] 作为掩码。

分段:RFC 6455 - 第 5.4 节

  • 是不是一个碎片化的控制帧?
  • 由多个帧组成的大消息的分段是否无序?
  • 是否有新消息在带有 FIN 标志的前一个消息完成之前开始?

控制帧:RFC 6455 - 第 5.5 节

  • 控制帧的净荷长度是否超过125字节?
  • 有效负载是否碎片化?

关闭框架:RFC 6455 - 第 5.5.1 节

  • 如果有效负载中提供了状态代码,则该状态代码是否符合中声明的状态代码之一第 7.4.1 节?不要忘记检查Websocket 状态代码的 IANA 注册表RFC 最终确定后添加的)
  • 状态码是否允许在帧中通过网络发送? (例如,请参见代码 1005 和 1006)
  • 如果框架中提供了/reason/,它是否符合UTF-8编码规则?
  • 在关闭帧后,您是否收到过任何类型的帧? (这是禁忌)

数据帧:RFC 6455 - 第 5.6 节

  • 如果您收到 TEXT 有效负载数据(来自 TEXT + CONTINUATION 帧),该有效负载数据是否符合 UTF-8 编码规则?

虽然您可以在单个帧级别进行验证,但您会发现上面的一些验证是多个帧之间的状态和行为的验证。您可以在以下位置找到更多此类验证:发送和接收数据:RFC 6455 - 第 6 节.

但是,如果混合中有扩展,那么您还需要从协商的扩展堆栈的角度来处理帧。 当使用扩展时,上面的一些测试似乎无效。

示例:你有压缩扩展 (RFC-7692)(例如permessage-deflate) 在使用中,则无法使用网络外的原始帧来验证 TEXT 有效负载,因为您必须首先通过扩展传递该帧。请注意,扩展可以更改碎片以满足其需要,这也可能会扰乱您的验证。

package websocket;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;

public class RawParse
{
    public static class Frame
    {
        byte opcode;
        boolean fin;
        byte payload[];
    }

    public static Frame parse(byte raw[])
    {
        // easier to do this via ByteBuffer
        ByteBuffer buf = ByteBuffer.wrap(raw);

        // Fin + RSV + OpCode byte
        Frame frame = new Frame();
        byte b = buf.get();
        frame.fin = ((b & 0x80) != 0);
        boolean rsv1 = ((b & 0x40) != 0);
        boolean rsv2 = ((b & 0x20) != 0);
        boolean rsv3 = ((b & 0x10) != 0);
        frame.opcode = (byte)(b & 0x0F);

        // TODO: add control frame fin validation here
        // TODO: add frame RSV validation here

        // Masked + Payload Length
        b = buf.get();
        boolean masked = ((b & 0x80) != 0);
        int payloadLength = (byte)(0x7F & b);
        int byteCount = 0;
        if (payloadLength == 0x7F)
        {
            // 8 byte extended payload length
            byteCount = 8;
        }
        else if (payloadLength == 0x7E)
        {
            // 2 bytes extended payload length
            byteCount = 2;
        }

        // Decode Payload Length
        while (--byteCount > 0)
        {
            b = buf.get();
            payloadLength |= (b & 0xFF) << (8 * byteCount);
        }
        
        // TODO: add control frame payload length validation here

        byte maskingKey[] = null;
        if (masked)
        {
            // Masking Key
            maskingKey = new byte[4];
            buf.get(maskingKey,0,4);
        }
        
        // TODO: add masked + maskingkey validation here

        // Payload itself
        frame.payload = new byte[payloadLength];
        buf.get(frame.payload,0,payloadLength);

        // Demask (if needed)
        if (masked)
        {
            for (int i = 0; i < frame.payload.length; i++)
            {
                frame.payload[i] ^= maskingKey[i % 4];
            }
        }

        return frame;
    }

    public static void main(String[] args)
    {
        Charset UTF8 = Charset.forName("UTF-8");

        Frame closeFrame = parse(hexToByteArray("8800"));
        System.out.printf("closeFrame.opcode = %d%n",closeFrame.opcode);
        System.out.printf("closeFrame.payload.length = %d%n",closeFrame.payload.length);

        // Examples from https://www.rfc-editor.org/rfc/rfc6455#section-5.7
        Frame unmaskedTextFrame = parse(hexToByteArray("810548656c6c6f"));
        System.out.printf("unmaskedTextFrame.opcode = %d%n",unmaskedTextFrame.opcode);
        System.out.printf("unmaskedTextFrame.payload.length = %d%n",unmaskedTextFrame.payload.length);
        System.out.printf("unmaskedTextFrame.payload = \"%s\"%n",new String(unmaskedTextFrame.payload,UTF8));

        Frame maskedTextFrame = parse(hexToByteArray("818537fa213d7f9f4d5158"));
        System.out.printf("maskedTextFrame.opcode = %d%n",maskedTextFrame.opcode);
        System.out.printf("maskedTextFrame.payload.length = %d%n",maskedTextFrame.payload.length);
        System.out.printf("maskedTextFrame.payload = \"%s\"%n",new String(maskedTextFrame.payload,UTF8));
    }

    public static byte[] hexToByteArray(String hstr)
    {
        if ((hstr.length() < 0) || ((hstr.length() % 2) != 0))
        {
            throw new IllegalArgumentException(String.format("Invalid string length of <%d>",hstr.length()));
        }

        int size = hstr.length() / 2;
        byte buf[] = new byte[size];
        byte hex;
        int len = hstr.length();

        int idx = (int)Math.floor(((size * 2) - (double)len) / 2);
        for (int i = 0; i < len; i++)
        {
            hex = 0;
            if (i >= 0)
            {
                hex = (byte)(Character.digit(hstr.charAt(i),16) << 4);
            }
            i++;
            hex += (byte)(Character.digit(hstr.charAt(i),16));

            buf[idx] = hex;
            idx++;
        }

        return buf;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Java 中解析和验证 WebSocket 帧? 的相关文章

  • 存根方法时出现 InvalidUseOfMatchersException

    我有这个 TestNG 测试方法代码 InjectMocks private FilmeService filmeService new FilmeServiceImpl Mock private FilmeDAO filmeDao Bef
  • 如何在 Firebase 远程配置中从 JSON 获取值

    我是 Android 应用开发和 Firebase 的新手 我想知道如何获取存储在 Firebase 远程配置中的 JSONArray 文件中的值 String 和 Int 我使用 Firebase Remote Config 的最终目标是
  • 使用 GWT 读取非常大的本地 XML 文件

    我正在使用 GWT 构建我的第一个 Java 应用程序 它必须从一个非常大的 XML 文件中读取数据 当我尝试发送对文件中信息的请求时遇到问题 并且我不太确定它是否与文件的大小或我的语义有关 在我的程序中 我有以下内容 static fin
  • 打印星号的 ASCII 菱形

    我的程序打印出这样的钻石 但只有当参数或菱形的每一面为4 例如如果我输入6 底部三角形的间距是错误的 我一直在试图找出答案 当参数改变时 底部的三角形不会改变 只有顶部的三角形会改变 它只适用于输入4 public static void
  • 通往楼梯顶部的可能路径

    这是一个非常经典的问题 我听说谷歌在他们的面试中使用过这个问题 问题 制定一个递归方法 打印从楼梯底部到楼梯顶部的所有可能的独特路径 有 n 个楼梯 您一次只能走 1 步或 2 步 示例输出 如果它是一个有 3 级楼梯的楼梯 1 1 1 2
  • 在 Wildfly 中与 war 部署共享 util jar 文件

    假设我有一个名为 util jar 的 jar 文件 该 jar 文件主要包含 JPA 实体和一些 util 类 无 EJB 如何使这个 jar 可用于 Wildfly 中部署的所有 war 无需将 jar 放置在 war 的 WEB IN
  • 大数据使用什么数据结构

    我有一个包含一百万行的 Excel 工作表 每行有 100 列 每行代表一个具有 100 个属性的类的实例 列值是这些属性的值 哪种数据结构最适合在这里使用来存储数百万个数据实例 Thanks 这实际上取决于您需要如何访问这些数据以及您想要
  • 什么时候可以在 Java 中使用 Thead.stop() ?

    Thread stop 的 Java 文档听起来好像如果您调用 Thread stop 世界就会终结 已弃用 这种方法本质上是不安全的 停止线程 Thread stop 导致它解锁所有已锁定的监视器 作为未经检查的 ThreadDeath
  • 如何检测 Java 字符串中的 unicode 字符?

    假设我有一个包含 的字符串 我如何找到所有这些 un icode 字符 我应该测试他们的代码吗 我该怎么做呢 例如 给定字符串 A X 我想将其转换为 AYXY 我想对其他 unicode 字符做同样的事情 并且我不想将它们存储在某种翻译映
  • 在另一个模块中使用自定义 gradle 插件模块

    我正在开发一个自定义插件 我希望能够在稍后阶段将其部署到存储库 因此我为其创建了一个独立的模块 在对其进行任何正式的 TDD 之前 我想手动进行某些探索性测试 因此 我创建了一个使用给定插件的演示模块 到目前为止 我发现执行此操作的唯一方法
  • 了解joda时间PeriodFormatter

    我以为我明白了 但显然我不明白 你能帮我通过这些单元测试吗 Test public void second assertEquals 00 00 01 OurDateTimeFormatter format 1000 Test public
  • 内部存储的安全性如何?

    我需要的 对于 Android 我需要永久保存数据 但也能够编辑 并且显然是读取 它 用户不应访问此数据 它可以包含诸如高分之类的内容 用户不得对其进行编辑 我的问题 我会 并且已经 使用过Internal Storage 但我不确定它实际
  • Freemarker 和 Struts 2,有时它计算为序列+扩展哈希

    首先我要说的是 使用 Struts2 Freemarker 真是太棒了 然而有些事情让我发疯 因为我不明白为什么会发生这种情况 我在这里问是因为也许其他人有一个想法可以分享 我有一个动作 有一个属性 说 private String myT
  • 流中的非终结符 forEach() ?

    有时 在处理 Java Stream 时 我发现自己需要一个非终端 forEach 来触发副作用但不终止处理 我怀疑我可以用 map item gt f item 之类的方法来做到这一点 其中方法 f 执行副作用并将项目返回到流中 但这似乎
  • QuerySyntaxException:无法找到类

    我正在使用 hql 生成 JunctionManagementListDto 类的实际 Java 对象 但我最终在控制台上出现以下异常 org hibernate hql internal ast QuerySyntaxException
  • 如何在 Java 中创建接受多个值的单个注释

    我有一个名为 Retention RetentionPolicy SOURCE Target ElementType METHOD public interface JIRA The Key Bug number JIRA referenc
  • Java Swing:需要一个高质量的带有复选框的开发 JTree

    我一直在寻找一个 Tree 实现 其中包含复选框 其中 当您选择一个节点时 树中的所有后继节点都会被自动选择 当您取消选择一个节点时 树中其所有后继节点都会自动取消选择 当已经选择了父节点 并且从其后继之一中删除了选择时 节点颜色将发生变化
  • Java中HashMap和ArrayList的区别?

    在爪哇 ArrayList and HashMap被用作集合 但我不明白我们应该在哪些情况下使用ArrayList以及使用时间HashMap 他们两者之间的主要区别是什么 您具体询问的是 ArrayList 和 HashMap 但我认为要完
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数
  • Java EE 目录结构

    我对以下教程有疑问 http www mkyong com jsf2 jsf 2 internationalization example http www mkyong com jsf2 jsf 2 internationalizatio

随机推荐

  • 如何仅获取外键过滤器的最新记录

    我有一张这样的桌子 事件表 id status date order FK 1 Planned 05 02 2015 1 2 Delivered 04 02 2015 2 3 Packed 03 02 2015 3 4 Return 06
  • 如何衡量图像检测算法的成功率和准确率?

    有谁知道如何正确量化图像检测算法的成功 如何结合这两个误差源 因为一个来源是算法未能检测到的对象数量 另一个来源是算法错误识别为对象的误报数量 例如 如果图像中有 574 个物体 但算法只检测到其中 540 个 同时产生 113 个误报 那
  • 动态重命名方法

    我们可以在类定义时使用元类重命名类方法 这个问题是not关于那个 这更像是一个思想实验 所以请稍微幽默一下 假设我想编写两个这样使用的装饰器 class SomeClass object append A def some method s
  • NsdManager 发现在 Android 9 上不起作用

    我已经尝试了很长时间 让 NsdManager 发现功能在 Android 9 上运行 它可以在之前的任何 Android 版本上运行 没有任何问题 出于测试目的 我使用这个简单的代码片段 并且清单中存在 INTERNET 权限 var n
  • Python 回车符不起作用

    我有一个长时间运行的脚本 可以循环数据库中的行 我经常希望它打印已处理的行数 但不需要每次都创建新行 这基本上就是我所拥有的 import sys mystr rows complete r for i in range 0 100 if
  • 是否可以在没有任何Python库的情况下从xlsx文件中读取信息?

    我被迫问这个问题 我的导师给了我一个任务 用纯Python从文件中提取数据 有一些txt文件很简单 但有一个文件xlsx扩展名 我找不到任何可以用纯Python从中提取数据的地方 我已经搜索了3周多了 如果不可能 请告诉我 以便我可以自信地
  • 在图片框中显示图像数组?

    我对 Visual C 很陌生 我想在图片框中显示图像数组 这是我的代码 string list Directory GetFiles C pictures jpg Image images new Image 5 for int inde
  • 为文本框创建气球工具提示?

    如何在 SWT 中为文本框创建气球工具提示 这是一个修改后的 SWT 代码片段 展示了如何向 SWT 添加气球工具提示Text实例 下次请考虑更具体地询问 我现在不问 如果这就是您要找的东西 Tooltip example snippet
  • 如何请求 gzipped/压缩的 SOAP 响应?

    我使用 wsimport 工具创建了一个运行良好的肥皂客户端 现在我喜欢从服务器请求压缩响应 因为响应可能非常大 我不知道服务器是否能够发送压缩内容 据我所知 我必须在请求中添加类似 Accept Encoding gzip 的内容 我该如
  • Copy-Item 使用凭据将文件从本地复制到远程服务器

    我正在尝试将一些文件和文件夹从本地计算机复制到远程服务器 Copy Item copy test txt destination serverip c backups 但我收到错误 Copy Item Logon failure unkno
  • 使用 PHP 检查图像是否唯一的好方法是什么?

    使用 PHP 检查图像是否唯一的最佳方法是什么 假设我有一个大约 30 张图像 大约 500 500 像素 的目录 并且有人上传了另一张图片 有什么好方法来检查上传的图像是否不在该目录中 是否有某种方法可以创建可以轻松比较的图像哈希 然后
  • 如何使用 JavaScript 将所有 HTML 子元素移动到另一个父元素?

    Imagine div span Foo span b Bar b Hello World div div div 可以编写什么 JavaScript 来移动所有子节点 元素和文本节点 old parent to new parent没有
  • RouterModule.forRoot(ROUTES) 与 RouterModule.forChild(ROUTES)

    这两者之间有什么区别 各自的用例是什么 The docs并不完全有帮助 forRoot 创建一个包含所有指令的模块 给定的 路由 以及路由器服务本身 forChild 创建一个模块 包含所有指令和给定的路由 但不包括 路由器服务 我模糊的猜
  • 将复选框选择传递给操作

    我有一堆复选框 用于让用户指定他们想要在网格上看到哪些列 目前 每个复选框都有自己的键 本质上是它的标签名称 并且在我看来是这样声明的 Html CheckBox column Key Request Form column Key nul
  • 连接到远程我的 Sql 显示用户 [电子邮件受保护] 的访问被拒绝(使用密码:是)

    一切工作正常 当我配置TMySqlConnection使用 WAMP 本地服务器在本地计算机上连接的驱动程序 但在尝试配置驱动程序以连接到远程 MySQL 服务器时 出现错误 用户访问被拒绝 电子邮件受保护 使用密码 是 如何解决该问题并强
  • 是否有任何 Redis 客户端(Java 首选)支持 Redis 集群上的事务?

    我在网上仔细查找 但找不到提供此功能的成熟 Redis 客户端 只找到这个project 有人知道 Redis 客户端提供上述功能吗 谢谢 Redis 集群中的事务与 Redis Standalone 中的事务是不同的 TL DR 这更多的
  • jQuery 的 mouseout() 和 mouseleave() 有什么区别?

    jQuery 的 mouseout 和 mouseleave 有什么区别 mouseleave 事件与 mouseout 事件的不同之处在于它处理事件冒泡的方式 如果本例中使用了 mouseout 那么当鼠标指针移出 Inner 元素时 就
  • 使用 Inno Setup 安装 IIS

    我正在尝试使用 Inno Setup 安装 IIS 但遇到各种错误 我在 DOS 上尝试了命令并且有效 start w pkgmgr iu IIS WebServerRole IIS WebServer IIS CommonHttpFeat
  • 如何将json转换为数据表?

    有谁知道如何将 json 字符串从 asp net 转换为 DataTable 吗 我开始了解反序列化 它需要类 我只想返回数据表 谁能告诉我如何将其转换为数据表 假设您的 JSON 字符串是一个对象列表 每个对象将对应于 DataTabl
  • 如何在 Java 中解析和验证 WebSocket 帧?

    我用Java编写了一个WebSocket帧解码器 private byte decodeFrame byte rawIn int maskIndex 2 byte maskBytes new byte 4 if rawIn 1 byte 1