CBOR
Concise Binary Object Representation
CBOR allows for the representation of a wide range of data types in a compact binary format. It is designed to be extensible and self-describing, so that types of the encoded data can be determined without any out-of-band information, making it "schemaless".
This is achieved by using a tag system, where each data type is assigned a unique tag. The tag is used to determine the type of the encoded data from the initial byte of a value, followed by the data itself. The tag is stored in the most significant 5 bits of the initial byte, while the remaining 3 bits are generally used to determine the length of the data, with exceptions for some tags. The initial byte and any additional bytes consumed to construct the argument are collectively referred to as the "head" of the "data item".
| Name | Tag Number |
|---|---|
| Date Time (String) | 0 |
| Date Time (Epoch) | 1 |
| Positive Bignum | 2 |
| Negative Bignum | 3 |
| Decimal Fraction | 4 |
| Big Float | 5 |
| Base64URL | 21 |
| Base64 | 22 |
| Base16 | 23 |
This is not an exhaustive list of all the tags. IANA maintains a registry of tags you can refer to.
CBOR is similar to otherformats such as JSON, BSON, MessagePack, or Protcol Buffers. The following table shows the encoded bytes for a simple message in each format.
| Format | Bytes | Size |
|---|---|---|
| JSON |
| 17 |
| BSON |
| 22 |
| MessagePack |
| 15 |
| CBOR |
| 13 |
| Protocol Buffers |
| 7 |
Looking at the table, we can see that CBOR is not the smallest format, but it is still relatively small. It is
most similar to MessagePack, which it actually took inspiration from.
Unlike the rest of the examples shown, Protcol Buffers is not a schemaless format. It requires a schema to be defined, which
is then used to encode and decode the data. This is why it can ommit the field name "hello" and just reference it by
a field number (\x0a).
The advantage of a schemaless format like CBOR is that the sender and receiver do not need to agree on a schema, which can be a challenge when working with multiple parties. The disadvantage is that the data is often not as compact, and it is not as easy to validate the data being received, resulting in a potential security risk from maliciously crafted messages. It's a double-edged sword.
CBOR implements a variety of potentially dangerous data types that can be used by an attacker, such as indefinite length maps and arrays, duplicate map keys, arbitrary precision numbers, and more. These data types can be used to launch a denial of service attack (resource exhaustion), and need to be handled carefully. These attacks are discussed in the Security Considerations section of the RFC.
To make this clear, let's consider an infinite array (\x9f) of infinite UTF-8 text strings (\x7f), which is a valid CBOR data type.
If a server were to decode this, it would essentially need to allocate an infinite amount of memory to
store the strings. This would cause the server to eventually run out of available memory, and crash or become unresponsive.
\x9f\x7f\x61\x61\xff...\xff Each \x61\x61 is single character ("a"), and the 0xff is the "break code",
one for each string in the array, and one more to terminate the array.
Depending on the decoder implementation, and network transport, an infinite amount of \x61\x61 could be sent by the attacker.
Attacker
\x9f\x7f\x61\x61\xff...
\x61\x61\xff
Server
Array["a", "a", "a", ...]
This attacks would be very cheap for an attacker to send, and would balloon the memory usage of the decoder on the server.
Variations of this attack exist in each of the other codecs. This is not solely a CBOR problem.
There are a number of ways to mitigate these kinds of attacks, such as limiting the size of the data being decoded, limiting the depth of the data structure, or restricting the types allowed in the data structure (generally do not allow infinite maps and arrays).
Network transport level limits on the number of bytes that can be sent by a single client for a logical request can also be used to mitigate these attacks, along with restricting the number of requests that can be sent by a single client in a given time period. While you're at it, add idle/read/write timeouts to mitigate slow-loris-like attacks.
mTLS can also be used to ensure that only trusted clients are able to connect to the server.
Doing any these in combination enables defense in depth.