YAML loaders can instantiate objects, so loading untrusted YAML unsafely is a remote-code-execution bug.
YAML can carry tags that tell a loader to build language-specific objects, for example !!python/object/apply:os.system. A loader that honours them will run code chosen by whoever wrote the file. So:
- Python: use
yaml.safe_load(oryaml.load(s, Loader=yaml.SafeLoader)), neveryaml.loadwithLoader=yaml.LoaderorUnsafeLoaderon untrusted input. Since PyYAML 5.1, callingyaml.loadwithout aLoaderwarns, and since 6.0 it is an error. - Ruby:
YAML.safe_load, the default forYAML.loadsince Psych 4. - JavaScript: js-yaml 4 and the
yamlpackage load only standard types by default. - Java: SnakeYAML 2 uses a safe constructor by default; older code with
new Yaml()was vulnerable.
Even safe loaders can be abused by alias bombs ("billion laughs"): a few lines with nested aliases that expand to billions of elements when the data is walked or serialized. Limit input size, and use parsers' alias limits where available (Go's yaml.v3 and the yaml npm package have them).
Some applications need to edit YAML while keeping comments and formatting. Normal loaders discard both. Use a round-trip library: ruamel.yaml in Python, the yaml package's Document API in JavaScript, yaml.v3 Node in Go.
For speed in Python, yaml.CSafeLoader uses libyaml and is many times faster.
Example
import yaml
doc = '''
name: demo
ports: [80, 443]
'''
print(yaml.safe_load(doc)) # {'name': 'demo', 'ports': [80, 443]}
evil = "!!python/object/apply:os.system ['echo pwned']"
try:
yaml.safe_load(evil)
except yaml.constructor.ConstructorError as e:
print("refused:", e.problem) # refused: could not determine a constructor for the tag ...
from ruamel.yaml import YAML # round-trip: comments survive
import sys
rt = YAML()
data = rt.load("replicas: 2 # keep this comment\n")
data["replicas"] = 3
rt.dump(data, sys.stdout) # replicas: 3 # keep this commentRemember
- Use safe_load (or the language's safe API) for any untrusted YAML
- Alias bombs can blow up memory even with safe loaders: cap input size
- Use round-trip libraries like ruamel.yaml to edit files and keep comments
Watch out for
- Loading user-supplied YAML with yaml.load(..., Loader=yaml.Loader) or UnsafeLoader.