如何在Java中解析和验证WebSocket框架? [英] How to parse and validate a WebSocket frame in Java?

查看:184
本文介绍了如何在Java中解析和验证WebSocket框架?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wrote a WebSocket frame decoder in Java:

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;
    }

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

It works, but I have no idea how to validate a frame in order to make sure that it decodes only valid frames.

协议说明 http://tools.ietf.org/html/rfc6455 遗憾的是并没有多说帧验证。

The protocol description http://tools.ietf.org/html/rfc6455 unfortunately does not tell much about frame-validation.

推荐答案

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

Parsing a raw websocket frame is easy enough. But you have to inspect the header one byte at a time.

这是一个粗略的例子:

我留下了一些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节?不要忘记检查 IANA注册的websocket状态代码在RFC完成后添加的内容)

  • 状态代码是否允许通过网络在帧中发送? (例如,参见代码1005和1006)

  • 如果框架中提供了/ reason /,它是否符合UTF-8编码规则?

  • 在关闭框架后,您是否收到任何类型的框架? (这是禁忌)

  • If a status code is provided in the payload, does the status code conform to one of the status codes declared in section 7.4.1? Don't forget to to check the IANA registry of websocket status codes that were added after the RFC was finalized)
  • Is the status code one that is allowed to be sent over the network in a Frame? (see codes 1005, and 1006 for example)
  • If a /reason/ is provided in the frame, does it conform to UTF-8 encoding rules?
  • Have you received any frames, of any kind, after a Close frame? (this is a no-no)

数据框:RFC 6455 - 第5.6节


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

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

While you can validate at the individual frame level, you will find that some of the validations above are validations of state and behavior between multiple frames. You can find more of these kinds of validations in Sending and Receiving Data: RFC 6455 - Section 6.

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

However, if you have extensions in the mix, then you will also need to process the frames from the point of view of the negotiated extension stack as well. Some tests above would appear to be invalid when an extension is being used.

示例:您有压缩扩展(RFC-7692)(例如 permessage-deflate )正在使用中,然后是TEXT有效负载的验证不能通过网络上的原始帧完成,因为您必须首先通过扩展传递帧。请注意,扩展可以更改碎片以满足其需要,这也可能会破坏您的验证。

Example: You have Compression Extension (RFC-7692) (such as permessage-deflate) in use, then the validation of TEXT payload cannot be done with the raw frame off the network, as you must first pass the frame through the extension. Note that the extension can change the fragmentation to suit its needs, which might mess up your validation as well.

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 http://tools.ietf.org/html/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;
    }
}

这篇关于如何在Java中解析和验证WebSocket框架?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆