Skip to content

Commit

Permalink
feat(json): support applying a security policy to check for potential…
Browse files Browse the repository at this point in the history
… threats during decoding

We will check the following:
1.JSON nesting depth
2.Object key length
3.String value length
4.Array element count
5.Object key-value pair count
  • Loading branch information
Water-Melon committed Jul 8, 2024
1 parent f3eba40 commit 7c3021a
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 32 deletions.
62 changes: 57 additions & 5 deletions docs/book/cn/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,15 @@ int mln_json_array_init(mln_json_t *j);
#### mln_json_decode

```c
int mln_json_decode(mln_string_t *jstr, mln_json_t *out);
int mln_json_decode(mln_string_t *jstr, mln_json_t *out, mln_json_policy_t *policy);
```
描述:将JSON字符串`jstr`解析成数据结构,结果会被放入参数`out`中。
描述:将JSON字符串`jstr`解析成数据结构,结果会被放入参数`out`中。`policy`为安全策略结构,由`mln_json_policy_init`进行初始化。
返回值:
- `0` - 成功
- `-1` - 失败
- `-1` - 失败, 如果`policy`不为`NULL`,则需要使用`mln_json_policy_error`检查具体违反了哪个安全限制条件。
Expand Down Expand Up @@ -346,6 +346,52 @@ mln_json_null_data_get(json)



#### mln_json_policy_init

```c
mln_json_policy_init(policy, _depth, _keylen, _strlen, _elemnum, _kvnum);
```
描述:对`mln_json_policy_t`类型的`policy`进行初始化,剩余参数含义如下:
- `_depth` JSON的最大嵌套层数。解析时,当遇到数组或对象时,嵌套层数会递增。数组或对象解析完成时,嵌套层数会递减。
- `_keylen` 对象中key的最大长度。
- `_strlen` 字符串值的最大长度。
- `_elemnum` 最大数组元素个数。
- `_kvnum` 最大对象key-value对个数。
返回值:无
#### mln_json_policy_error
```c
mln_json_policy_error(policy)
```

描述:从`mln_json_policy_t`类型的`policy`获取错误号。这个宏一般用于`mln_json_decode`返回`-1`时检查是否有违反安全策略的情况。

返回值:`int`型错误号,错误号有如下值:

- `M_JSON_OK` 没有违反安全策略。

- `M_JSON_DEPTH` 嵌套层数过多。

- `M_JSON_KEYLEN` 对象Key的长度超过限制。

- `M_JSON_STRLEN` 字符串值的长度超过限制。

- `M_JSON_ARRELEM` 数组元素个数超过限制。

- `M_JSON_OBJKV` 对象key-value对个数超过限制。



#### mln_json_parse

```c
Expand Down Expand Up @@ -496,7 +542,7 @@ int main(int argc, char *argv[])
mln_string_t *res, exp = mln_string("protocols.0");
mln_string_t tmp = mln_string("{\"paths\":[\"/mock\"],\"methods\":null,\"sources\":null,\"destinations\":null,\"name\":\"example_route\",\"headers\":null,\"hosts\":null,\"preserve_host\":false,\"regex_priority\":0,\"snis\":null,\"https_redirect_status_code\":426,\"tags\":null,\"protocols\":[\"http\",\"https\"],\"path_handling\":\"v0\",\"id\":\"52d58293-ae25-4c69-acc8-6dd729718a61\",\"updated_at\":1661345592,\"service\":{\"id\":\"c1e98b2b-6e77-476c-82ca-a5f1fb877e07\"},\"response_buffering\":true,\"strip_path\":true,\"request_buffering\":true,\"created_at\":1661345592}");
if (mln_json_decode(&tmp, &j) < 0) { //解码字符串,生成mln_json_t的结点
if (mln_json_decode(&tmp, &j, NULL) < 0) { //解码字符串,生成mln_json_t的结点
fprintf(stderr, "decode error\n");
return -1;
}
Expand Down Expand Up @@ -566,9 +612,15 @@ static int handler(mln_json_t *j, void *data)
static void parse(mln_string_t *p)
{
mln_json_t j;
mln_json_policy_t policy;
mln_string_t exp = mln_string("resolutions");
mln_json_decode(p, &j);

mln_json_policy_init(policy, 3, 11, 10, 1, 2);

mln_json_decode(p, &j, &policy);

mln_json_parse(&j, &exp, handler, NULL);

mln_json_destroy(&j);
}

Expand Down
62 changes: 57 additions & 5 deletions docs/book/en/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ Return value:
#### mln_json_decode

```c
int mln_json_decode(mln_string_t *jstr, mln_json_t *out);
int mln_json_decode(mln_string_t *jstr, mln_json_t *out, mln_json_policy_t *policy);
```
Description: Parse the JSON string `jstr` into a data structure, and the result will be put into the parameter `out`.
Description: Parse the JSON string `jstr` into a data structure, and the result will be put into the parameter `out`. The policy is a security policy structure initialized by mln_json_policy_init.
Return value:
- `0` - on success
- `-1` - on failure
- `-1` - on failure. If policy is not NULL, you need to use mln_json_policy_error to check which specific security constraint has been violated.
Expand Down Expand Up @@ -345,6 +345,52 @@ Return value:



#### mln_json_policy_init

```c
mln_json_policy_init(policy, _depth, _keylen, _strlen, _elemnum, _kvnum);
```
Description: Initialize the `policy` of type `mln_json_policy_t`. The meanings of the remaining parameters are as follows:
- `_depth` The maximum nesting depth of the JSON. During parsing, the nesting depth increases when encountering an array or object, and decreases when the array or object is fully parsed.
- `_keylen` The maximum length of keys in objects.
- `_strlen` The maximum length of string values.
- `_elemnum` The maximum number of elements in an array.
- `_kvnum` The maximum number of key-value pairs in an object.
Return Value: None
#### mln_json_policy_error
```c
mln_json_policy_error(policy)
```

Description: Get the error code from the `policy` of type `mln_json_policy_t`. This macro is typically used to check for security policy violations when `mln_json_decode` returns `-1`.

Return Value: An int type error code with the following values:

- `M_JSON_OK` No security policy violations.

- `M_JSON_DEPTH` The nesting depth is too deep.

- `M_JSON_KEYLEN` The length of the object key exceeds the limit.

- `M_JSON_STRLEN` The length of the string value exceeds the limit.

- `M_JSON_ARRELEM` The number of elements in the array exceeds the limit.

- `M_JSON_OBJKV` The number of key-value pairs in the object exceeds the limit.



#### mln_json_parse

```c
Expand Down Expand Up @@ -493,7 +539,7 @@ int main(int argc, char *argv[])
mln_string_t *res, exp = mln_string("protocols.0");
mln_string_t tmp = mln_string("{\"paths\":[\"/mock\"],\"methods\":null,\"sources\":null,\"destinations\":null,\"name\":\"example_route\",\"headers\":null,\"hosts\":null,\"preserve_host\":false,\"regex_priority\":0,\"snis\":null,\"https_redirect_status_code\":426,\"tags\":null,\"protocols\":[\"http\",\"https\"],\"path_handling\":\"v0\",\"id\":\"52d58293-ae25-4c69-acc8-6dd729718a61\",\"updated_at\":1661345592,\"service\":{\"id\":\"c1e98b2b-6e77-476c-82ca-a5f1fb877e07\"},\"response_buffering\":true,\"strip_path\":true,\"request_buffering\":true,\"created_at\":1661345592}");
if (mln_json_decode(&tmp, &j) < 0) { //Decode the string and generate the node of mln_json_t
if (mln_json_decode(&tmp, &j, NULL) < 0) { //Decode the string and generate the node of mln_json_t
fprintf(stderr, "decode error\n");
return -1;
}
Expand Down Expand Up @@ -563,9 +609,15 @@ static int handler(mln_json_t *j, void *data)
static void parse(mln_string_t *p)
{
mln_json_t j;
mln_json_policy_t policy;
mln_string_t exp = mln_string("resolutions");
mln_json_decode(p, &j);

mln_json_policy_init(policy, 3, 11, 10, 1, 2);

mln_json_decode(p, &j, &policy);

mln_json_parse(&j, &exp, handler, NULL);

mln_json_destroy(&j);
}

Expand Down
40 changes: 39 additions & 1 deletion include/mln_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
#define M_JSON_V_TRUE 1
#define M_JSON_V_NULL NULL

/*
* policy errors
*/
#define M_JSON_OK 0
#define M_JSON_DEPTH 1
#define M_JSON_KEYLEN 2
#define M_JSON_STRLEN 3
#define M_JSON_ARRELEM 4
#define M_JSON_OBJKV 5

typedef struct mln_json_s mln_json_t;
typedef int (*mln_json_iterator_t)(mln_json_t *, void *);
typedef int (*mln_json_object_iterator_t)(mln_json_t * /*key*/, mln_json_t * /*val*/, void *);
Expand Down Expand Up @@ -59,6 +69,15 @@ struct mln_json_call_attr {
void *data;
};

typedef struct {
mln_size_t depth;
mln_size_t key_len;
mln_size_t str_len;
mln_size_t arr_elem_num;
mln_size_t obj_kv_num;
int error;
} mln_json_policy_t;

#define mln_json_is_object(json) ((json)->type == M_JSON_OBJECT)
#define mln_json_is_array(json) ((json)->type == M_JSON_ARRAY)
#define mln_json_is_string(json) ((json)->type == M_JSON_STRING)
Expand Down Expand Up @@ -112,6 +131,14 @@ struct mln_json_call_attr {
mln_json_null_type_set(json);\
json->data.m_j_null = NULL;\
} while (0)
#define mln_json_policy_init(policy, _depth, _keylen, _strlen, _elemnum, _kvnum) do {\
policy.depth = _depth;\
policy.key_len = _keylen;\
policy.str_len = _strlen;\
policy.arr_elem_num = _elemnum;\
policy.obj_kv_num = _kvnum;\
policy.error = M_JSON_OK;\
} while (0)
#else
#define mln_json_string_init(j, s) ({\
mln_json_t *json = (j);\
Expand All @@ -138,7 +165,18 @@ struct mln_json_call_attr {
mln_json_null_type_set(json);\
json->data.m_j_null = NULL;\
})
#define mln_json_policy_init(policy, _depth, _keylen, _strlen, _elemnum, _kvnum) ({\
policy.depth = _depth;\
policy.key_len = _keylen;\
policy.str_len = _strlen;\
policy.arr_elem_num = _elemnum;\
policy.obj_kv_num = _kvnum;\
policy.error = M_JSON_OK;\
})
#endif

#define mln_json_policy_error(policy) ((policy).error)

extern int mln_json_obj_init(mln_json_t *j) __NONNULL1(1);
extern int mln_json_array_init(mln_json_t *j) __NONNULL1(1);
extern void mln_json_destroy(mln_json_t *j);
Expand All @@ -151,7 +189,7 @@ extern mln_uauto_t mln_json_array_length(mln_json_t *j) __NONNULL1(1);
extern int mln_json_array_append(mln_json_t *j, mln_json_t *value) __NONNULL2(1,2);
extern int mln_json_array_update(mln_json_t *j, mln_json_t *value, mln_uauto_t index) __NONNULL2(1,2);
extern void mln_json_array_remove(mln_json_t *j, mln_uauto_t index);
extern int mln_json_decode(mln_string_t *jstr, mln_json_t *out);
extern int mln_json_decode(mln_string_t *jstr, mln_json_t *out, mln_json_policy_t *policy);
extern mln_string_t *mln_json_encode(mln_json_t *j);
extern int mln_json_parse(mln_json_t *j, mln_string_t *exp, mln_json_iterator_t iterator, void *data) __NONNULL2(1,2);
extern int mln_json_generate(mln_json_t *j, char *fmt, ...) __NONNULL2(1,2);
Expand Down
Loading

0 comments on commit 7c3021a

Please sign in to comment.