我之前写过为什么将 JSON 用于人类可编辑的配置文件是一个坏主意,今天我们将讨论 YAML 格式的一些常见问题。
默认情况下不安全
YAML 默认是不安全的。加载用户提供的(不可信的)YAML 字符串需要仔细考虑。
!!python/object/apply:os.system
args: ['ls /']
用 print(yaml.load(open('a.yaml')))
运行它,应该给你这样的东西:
bin etc lib lost+found opt root sbin tmp var sys
boot dev efi home lib64 mnt proc run srv usr
0
许多其他语言(包括 Ruby 和 PHP 1 )默认情况下也不安全(LCTT 译注:这里应该说的是解析 yaml)。在 GitHub 上搜索 yaml.load 会得到惊人的 280 万个结果,而 yaml.safe_load 只能得到 26000 个结果。
提个醒,很多这样的 yaml.load()
都工作的很好,在配置文件中加载 yaml.load()
通常没问题,因为它通常(虽然并不总是!)来自“可靠源”,而且很多都来自静态的 YAML 测试文件。但是,人们还是不禁怀疑在这 280 万个结果中隐藏了多少漏洞。
这不是一个理论问题。在 2013 年,正是由于这个问题,所有的 Ruby on Rails 应用程序都被发现易受远程代码执行攻击。
有人可能会反驳说这不是 YAML 格式的错误,而是那些库实现错误的的问题,但似乎大多数库默认不是安全的(特别是动态语言),所以事实上这是 YAML 的一个问题。
有些人可能会反驳认为修复它就像用 safe_load()
替换 load()
一样容易,但是很多人都没有意识到这个问题,即使你知道它,它也是很容易忘记的事情之一。这是非常糟糕的 API 设计。
可能很难编辑,特别是对于大文件
YAML 文件可能很难编辑,随着文件变大,这个难度会快速增大。
一个很好的例子是 Ruby on Rails 的本地化翻译文件。例如:
en:
formtastic:
labels:
title: "Title" # Default global value
article:
body: "Article content"
post:
new:
title: "Choose a title..."
body: "Write something..."
edit:
title: "Edit title"
body: "Edit body"
看起来不错,对吧?但是如果这个文件有 100 行怎么办?或者 1,000 行?在文件中很难看到 “where”,因为它可能在屏幕外。你需要向上滚动,但是你需要跟踪缩进,即使遵循缩进指南也很难,特别是因为 2 个空格缩进是常态而且 制表符缩进被禁止 2 。
不小心缩进出错通常不算错误,它通常只是反序列化为你不想要的东西。这样只能祝你调试快乐!
我已经愉快地编写 Python 长达十多年,所以我已经习惯了显眼的空白,但有时候我仍在和 YAML 抗争。在 Python 中,虽然没有那种长达几页的函数,但数据或配置文件的长度没有这种自然限制,这就带来了缺点和损失了清晰度。
对于小文件,这不是问题,但它确实无法很好地扩展到较大的文件,特别是如果你以后想编辑它们的话。
这非常复杂
在浏览一个基本的例子时,YAML 看似“简单”和“显而易见”,但事实证明并非如此。YAML 规范有 23449 个单词,为了比较,TOML 有 3339 个单词,Json 有 1969 个单词,XML 有 20603 个单词。
我们中有谁读过全部规范吗?有谁读过并理解了全部?谁阅读过,理解进而记住所有这些?
例如,你知道在 YAML 中编写多行字符串有 9 种方法吗?并且它们具有细微的不同行为。
是的 :-/
如果你看一下那篇文章的修订历史,它就会变得更加有趣,因为文章的作者发现了越来越多的方法可以实现这一点,以及更多的细微之处。
它从预览开始告诉我们 YAML 规范,它表明(强调我的):
本节简要介绍了 YAML 的表达能力。预计初次阅读的人不可能理解所有的例子。相反,这些选择用作该规范其余部分的动机。
令人惊讶的行为
以下会解析成什么(Colm O’Connor 提供的例子):
- Don Corleone: Do you have faith in my judgment?
- Clemenza: Yes
- Don Corleone: Do I have your loyalty?
结果为:
[
{'Don Corleone': 'Do you have faith in my judgment?'},
{'Clemenza': True},
{'Don Corleone': 'Do I have your loyalty?'}
]
那么这个呢:
python: 3.5.3
postgres: 9.3
3.5.3
被识别为字符串,但 9.3
被识别为数字而不是字符串:
{'python': '3.5.3', 'postgres': 9.3}
这个呢:
Effenaar: Eindhoven
013: Tilburg
013
是 蒂尔堡 的一个流行音乐场地,但 YAML 会告诉你错误答案,因为它被解析为八进制数字:
{11: 'Tilburg', 'Effenaar': 'Eindhoven'}
所有这一切,以及更多,就是为什么许多经验丰富的 YAMLer 经常会将所有字符串用引号引起来的原因,即使它不是严格要求。许多人不使用引号,而且很容易忘记,特别是如果文件的其余部分(可能由其他人编写)不使用引号。
它不方便
因为它太复杂了,它所声称的可移植性被夸大了。例如,考虑以下这个从 YAML 规范中获取的示例:
“`
? – Detroit Tigers
– Chicago cubs
:
– 2001-07-23
? [ New York Yankees,
Atlanta Braves ]
: [ 2001-07-02, 2001-08-12,
2001-08-14 ]
“`
抛开大多数读者可能甚至不知道这是在做什么之外,请尝试使用 PyYAML 在 Python 中解析它:
yaml.constructor.ConstructorError: while constructing a mapping
in "a.yaml", line 1, column 1
found unhashable key
in "a.yaml", line 1, column 3
在 Ruby 中,它可以工作:
{
["Detroit Tigers", "Chicago cubs"] => [
#<Date: 2001-07-23 ((2452114j,0s,0n),+0s,2299161j)>
],
["New York Yankees", "Atlanta Braves"] => [
#<Date: 2001-07-02 ((2452093j,0s,0n),+0s,2299161j)>,
#<Date: 2001-08-12 ((2452134j,0s,0n),+0s,2299161j)>,
#<Date: 2001-08-14 ((2452136j,0s,0n),+0s,2299161j)>
]
}
这个原因是你不能在 Python 中使用列表作为一个字典的键:
“`
{[‘a’]: ‘zxc’}
Traceback (most recent call last):
File ““, line 1, in
TypeError: unhashable type: ‘list’
“`
而这种限制并不是 Python 特有的,PHP、JavaScript 和 Go 等常用语言都有此限制。
因此,在 YAML 文件中使用这种语法,你将无法在大多数语言中解析它。
这是另一个从 YAML 规范的示例部分中获取的:
“`
Ranking of 1998 home runs
- Chicago Cubs
- St Louis Cardinals
“`
Python 会输出:
yaml.composer.ComposerError: expected a single document in the stream
in "a.yaml", line 3, column 1
but found another document
in "a.yaml", line 8, column 1
然而 Ruby 输出:
["Mark McGwire", "Sammy Sosa", "Ken Griffey"]
原因是单个文件中有多个 YAML 文档(`
via: https://arp242.net/weblog/yaml_probably_not_so_great_after_all.html
作者:Martin Tournoij 选题:lujun9972 译者:MjSeven 校对:wxy
主题测试文章,只做测试使用。发布者:eason,转转请注明出处:https://aicodev.cn/2019/01/08/yaml%ef%bc%9a%e5%8f%af%e8%83%bd%e5%b9%b6%e4%b8%8d%e6%98%af%e9%82%a3%e4%b9%88%e5%ae%8c%e7%be%8e/