From 334071a380c37e4d24b37de79e7ed7cc4c63a7e5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 18:31:00 +0000 Subject: [PATCH] docs: security considerations --- README.md | 49 +++++++++++++++ lib/refs/json-schema-secure.json | 100 +++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 lib/refs/json-schema-secure.json diff --git a/README.md b/README.md index a66cc0178..1e394e78d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Defining custom keywords](#defining-custom-keywords) - [Asynchronous schema compilation](#asynchronous-schema-compilation) - [Asynchronous validation](#asynchronous-validation) + - [Security considerations](#security-considerations) - Modifying data during validation - [Filtering data](#filtering-data) - [Assigning defaults](#assigning-defaults) @@ -605,6 +606,54 @@ validate(data).then(successFunc).catch(errorFunc); See [Options](#options). +## Security considerations + +##### Untrusted schemas + +Ajv treats JSON schemas as trusted as your application code. This security model is based on the most common use case, when the schemas are static and bundled together with the application. + +If your schemas are received from untrusted sources (or generated from untrusted data) there may be several scenarios you may want to prevent: +- compiling schemas can cause stack overflow (if they are too deep) +- compiling schemas can be slow (e.g. [#557](https://github.com/epoberezkin/ajv/issues/557)) +- validating certain data can be slow + +It is difficult to predict all the scenarios, but at the very least it is recommended to limit the size of untrusted JSON Schemas (e.g. as JSON string length) and the maximum schema object depth (that can be high for relatively small JSON strings). Even that would not prevent slow regular expressions in schemas. + +Regardless the measures you take, using untrusted schemas increases security risks. + + +##### Circular references in JavaScript objects + +Ajv does not support schemas and validated data that have circular references in objects. See [issue #802](https://github.com/epoberezkin/ajv/issues/802). + +An attempt to compile such schemas or validate such data would cause stack overflow (or will not complete in case of asynchronous validation). Untrusted data can lead to circular references, depending on the parser you use. + + +##### Security risks of trusted schemas + +Some keywords in JSON Schemas can lead to very slow validation for certain data. These keywords include (but, most likely, not limited to): + +- `pattern` and `format` for large strings - use `maxLength` to mitigate +- `uniqueItems` for large non-scalar arrays - use `maxItems` to mitigate +- `patternProperties` for large property names - use `propertyNames` to mitigate + +__Please note__: The suggestions above to prevent slow validation would only work if you do NOT use `allErrors: true` in production code (using it would continue validation after validation errors). + +You can validate your JSON schemas against [this meta-schema](https://github.com/epoberezkin/ajv/blob/master/lib/refs/json-schema-secure.json) to check that these recommendations are followed: + +``` +const isSchemaSecure = ajv.compile(require('ajv/lib/refs/json-schema-secure.json')); + +const schema1 = {format: 'email'}; +isSchemaSecure(schema1); // false + +const schema2 = {format: 'email', maxLength: 256}; +isSchemaSecure(schema2); // true +``` + +__Please note__: even following all these recommendation is not a guarantee that validation of untrusted data is absolutely safe - it can still lead to some undesirable situations. + + ## Filtering data With [option `removeAdditional`](#options) (added by [andyscott](https://github.com/andyscott)) you can filter data during the validation. diff --git a/lib/refs/json-schema-secure.json b/lib/refs/json-schema-secure.json new file mode 100644 index 000000000..d6a8e4c42 --- /dev/null +++ b/lib/refs/json-schema-secure.json @@ -0,0 +1,100 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#", + "title": "Meta-schema for the security assessment of JSON Schemas", + "description": "If a JSON Schema fails validation against this meta-schema, it may be unsafe to validate untrusted data", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + } + }, + "allOf": [ + { + "description": "prevent slow validation of large property names", + "if": { + "required": ["patternProperties"] + }, + "then": { + "required": ["propertyNames"], + "properties": { + "propertyNames": { + "required": ["maxLength"] + } + } + } + }, + { + "description": "prevent slow validation of large non-scalar arrays", + "if": { + "required": ["uniqueItems"], + "properties": { + "uniqueItems": {"const": true}, + "type": { + "anyOf": [ + { + "enum": ["object", "array"] + }, + { + "type": "array", + "contains": {"enum": ["object", "array"]} + } + ] + } + } + }, + "then": { + "required": ["maxItems"] + } + }, + { + "description": "prevent slow validation of large strings", + "if": { + "anyOf": [ + {"required": ["pattern"]}, + {"required": ["format"]} + ] + }, + "then": { + "required": ["maxLength"] + } + } + ], + "properties": { + "additionalItems": {"$ref": "#"}, + "additionalProperties": {"$ref": "#"}, + "dependencies": { + "additionalProperties": { + "anyOf": [ + {"type": "array"}, + {"$ref": "#"} + ] + } + }, + "items": { + "anyOf": [ + {"$ref": "#"}, + {"$ref": "#/definitions/schemaArray"} + ] + }, + "definitions": { + "additionalProperties": {"$ref": "#"} + }, + "patternProperties": { + "additionalProperties": {"$ref": "#"} + }, + "properties": { + "additionalProperties": {"$ref": "#"} + }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": {"$ref": "#/definitions/schemaArray"}, + "anyOf": {"$ref": "#/definitions/schemaArray"}, + "oneOf": {"$ref": "#/definitions/schemaArray"}, + "not": {"$ref": "#"}, + "contains": {"$ref": "#"}, + "propertyNames": {"$ref": "#"} + } +}