YAML:可能並不是那麼完美 | Linux 中國

2021-02-07 Linux中國

我之前寫過為什麼將 JSON 用於人類可編輯的配置文件是一個壞主意[1],今天我們將討論 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 應用程式都被發現易受[4]遠程代碼執行攻擊。

有人可能會反駁說這不是 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 個空格縮進是常態而且 制表符縮進被禁止[5] 2 。

不小心縮進出錯通常不算錯誤,它通常只是反序列化為你不想要的東西。這樣只能祝你調試快樂!

我已經愉快地編寫 Python 長達十多年,所以我已經習慣了顯眼的空白,但有時候我仍在和 YAML 抗爭。在 Python 中,雖然沒有那種長達幾頁的函數,但數據或配置文件的長度沒有這種自然限制,這就帶來了缺點和損失了清晰度。

對於小文件,這不是問題,但它確實無法很好地擴展到較大的文件,特別是如果你以後想編輯它們的話。

這非常複雜

在瀏覽一個基本的例子時,YAML 看似「簡單」和「顯而易見」,但事實證明並非如此。YAML 規範[6]有 23449 個單詞,為了比較,TOML[7] 有 3339 個單詞,Json[8] 有 1969 個單詞,XML[9] 有 20603 個單詞。

我們中有誰讀過全部規範嗎?有誰讀過並理解了全部?誰閱讀過,理解進而記住所有這些?

例如,你知道在 YAML 中編寫多行字符串有 9 種方法[10]嗎?並且它們具有細微的不同行為。

是的 :-/

如果你看一下那篇文章的修訂歷史[11],它就會變得更加有趣,因為文章的作者發現了越來越多的方法可以實現這一點,以及更多的細微之處。

它從預覽開始告訴我們 YAML 規範,它表明(強調我的):

本節簡要介紹了 YAML 的表達能力。預計初次閱讀的人不可能理解所有的例子。相反,這些選擇用作該規範其餘部分的動機。

令人驚訝的行為

以下會解析成什麼(Colm O』Connor[12] 提供的例子):

- 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 是蒂爾堡Tilburg的一個流行音樂場地,但 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 "<stdin>", line 1, in <module>

 TypeError: unhashable type: 'list'

而這種限制並不是 Python 特有的,PHP、JavaScript 和 Go 等常用語言都有此限制。

因此,在 YAML 文件中使用這種語法,你將無法在大多數語言中解析它。

這是另一個從 YAML 規範的示例部分中獲取的:

# Ranking of 1998 home runs

---

- Mark McGwire

- Sammy Sosa

- Ken Griffey

# Team ranking

---

- 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 文檔(--- 意味開始一個新文檔)。在 Python 中,有一個 load_all 函數來解析所有文檔,而 Ruby 的 load() 只是加載第一個文檔,據我所知,它沒有辦法加載多個文檔。

在實現之間存在很多不兼容[13]。

目標實現了嗎?

規範說明:

YAML 的設計目標是降低優先級:

那麼它做的如何呢?

YAML 很容易被人類閱讀。

只有堅持一小部分子集時才有效。完整的規則集很複雜 —— 遠遠超過 XML 或 JSON。

YAML 數據在程式語言之間是可移植的。

事實並非如此,因為創建常見語言不支持的結構太容易了。

YAML 匹配敏捷語言的原生數據結構。

參見上面。另外,為什麼只支持敏捷(或動態)語言?其他語言呢?

YAML 有一個一致的模型來支持通用工具。

我甚至不確定這意味著什麼,我找不到任何詳細說明。

YAML 支持一次性處理。

這點我接受。

YAML 具有表現力和可擴展性。

嗯,是的,但它太富有表現力(例如太複雜)。

YAML 易於實現和使用。

$ cat `ls -1 ~/gocode/src/github.com/go-yaml/yaml/*.go | grep -v _test` | wc -l

9247

$ cat /usr/lib/python3.5/site-packages/yaml/*.py | wc -l

5713

結論

不要誤解我的意思,並不是說 YAML 很糟糕 —— 它肯定不像使用 JSON 那麼多的問題[1] —— 但它也不是非常好。有一些一開始並不明顯的缺點和驚喜,還有許多更好的替代品,如 TOML[7]和其他更專業的格式。

就個人而言,當我有選擇時,我不太可能再次使用它。

如果你必須使用 YAML,那麼我建議你使用 StrictYAML[14],它會刪除一些(雖然不是全部)比較麻煩的部分。

反饋

你可以發送電子郵件至 martin@arp242.net[15] 或創建 GitHub issue[16] 以獲取反饋、問題等。

腳註

1.

在 PHP 中你需要修改一個 INI 設置來獲得安全的行為,不能只是調用像 yaml_safe()這樣的東西。PHP 想盡辦法讓愚蠢的東西越發愚蠢。幹得漂亮! ↩

2.

不要在這裡做空格與制表符之爭,如果這裡可以用制表符的話,我可以(臨時)增加制表符寬度來使它更易讀——這是制表符的一種用途。  ↩

via: https://arp242.net/weblog/yaml_probably_not_so_great_after_all.html

作者:Martin Tournoij[18] 選題:lujun9972 譯者:MjSeven 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

相關焦點

  • YAML語言教程
    languages: - Ruby - Perl - Pythonwebsites: YAML: yaml.org Ruby: ruby-lang.org Python: python.org Perl: use.perl.org 轉為 JavaScript 如下。
  • ❲阮一峰❳YAML 語言教程
    本文介紹 YAML 的語法,以 JS-YAML(https://github.com/nodeca/js-yaml) 的實現為例。你可以去在線 Demo(http://nodeca.github.io/js-yaml/) 驗證下面的例子。YAML 語言(發音 /ˈjæməl/ )的設計目標,就是方便人類讀寫。它實質上是一種通用的數據串行化格式。
  • 【第1257期】YAML 語言教程
    本文介紹 YAML 的語法,以 JS-YAML:https://github.com/nodeca/js-yaml的實現為例。你可以去在線 Demo:http://nodeca.github.io/js-yaml/ 驗證下面的例子。一、簡介YAML 語言(發音 /ˈjæməl/ )的設計目標,就是方便人類讀寫。它實質上是一種通用的數據串行化格式。
  • 如何快速成為一名優秀的YAML工程師?
    yaml工具pip3 install pyyamlpython3 -c 'import yaml, sys; yaml.safe_load(sys.stdin)' < demo.yamlYaml命令行內高亮顯示[2],可直接在Github上進行下載二進位文件
  • Python之UnitTest中yaml文件使用
    DDT數據驅動的使用,我們可以讀取txt文本文件來實現數據和代碼的分離,其實我們還可以使用今天要介紹的這種方式,那就是直接讀取yaml文件PyYaml的安裝pip install pyyamlPyYaml的簡單使用準備yaml數據,例如下面yaml文件編寫測試代碼,必須從ddt導入file_data執行結果展示
  • [接口測試 - 基礎篇] 11 掌握下python解析YAML格式也是需要的
    YAML是"YAML Ain't a Markup Language"(YAML不是一種置標語言)的遞歸縮寫。                       - 雜談系列    web站點:        中文名: 開源優測社區        狀態: 已暫停        城市: 廣州        網址: www.testingunion.com注意:字符串不一定要用雙引號標識在縮排中空白字符的數目並不是非常重要
  • 澳媒:中國不是完美國家,那麼哪個國家又完美呢?
    近些年,澳大利亞的一些政客,無端攻擊中國,大肆渲染所謂「中國威脅論」。對此,澳大利亞《雪梨先驅晨報》發表文章,提問「中國不是完美國家,那麼哪個國家又完美呢?」文章寫道,西澳大利亞州財政部長本·懷亞特在最後一次議會演講中表示,一些政客只想著攻擊中國,而不是培養跟「主要貿易夥伴」的關係。懷亞特表示,這些攻擊中國的言論毫無用處,澳大利亞需要跟中國建立互相尊重的關係,鞏固兩國的貿易利益。這番言論,很明顯是在抨擊高調「反華」的議員安德魯·哈斯蒂。
  • Linux DNS 查詢剖析(第一部分) | Linux 中國
    請注意,本系列主題並不是「DNS 工作原理」,而是與查詢 Linux 主機配置的真實 DNS 伺服器(這裡假設查詢了一臺 DNS 伺服器,但後面你會看到有時並不需要)相關的內容,以及如何確定使用哪個查詢結果,或者如何使用其它方式確定 IP 地址。1) 其實並沒有名為「DNS 查詢」的系統調用
  • 「linux專欄」linux系統中如何安裝Chrome瀏覽器?
    安裝完Redhat8的作業系統之後,默認只有Mozilla Firefox的火狐瀏覽器,那麼我們該如何安裝其他的瀏覽器,比如說Chorme瀏覽器呢?「linux專欄」linux中yum網絡源與本地源雙配置,總有一個適合你或者是
  • 在 Linux 下 9 個有用的 touch 命令示例 | Linux 中國
    在 touch 命令中使用 -c 選項即可,如果文件存在,那麼我們可以改變文件的訪問時間,如果不存在,我們也可不會創建它。[root@linuxtechi ~]# touch -c sysadm-20.txt[root@linuxtechi ~]# touch -c winadm-20.txt[root@linuxtechi ~]# ls -l winadm-20.txtls: cannot access winadm-20.txt: No such file
  • 2019 年總結我的 Linux 學習筆記
    然而,中國式的教科書還偏偏最喜歡講理論,講那些玄而又玄的專業名詞,就是讓你看不懂,好讓你知道這門學科不是所有人能教的。當然,這只是我的一點小玩笑。如果你要用這些東西有用嗎?我可以明確的告訴你,肯定是有用的啊!數據結構、計算機原理、作業系統原理、計算機網路、程式語言,哪一項不是現今主流技術的理論基石?因為是基石,基石肯定由理論組成,那為什麼理論這麼難懂呢?我私自認為,理論嘛。
  • Linux常用命令 - sed
    使用 sed 搜索和替換文本的一般形式如下:sed -i 's/SEARCH_REGEX/REPLACEMENT/g' INPUTFILE-i 將其輸出寫入標準輸出seds 替代命令,可能是 sed 中使用最多的命令-/分隔符字符。
  • 《Linux就該這麼學》與《鳥哥的linux私房菜》哪個更適合初學者?
    那麼對於一個想要變成linux「最強王者」的小白來說,需要怎麼樣才能更好的、更快的變成linux大牛呢自己本身就是學習軟體專業的,畢業後由於當時無法滿足自己生活所需,決然放棄軟體相關工作。但是身邊的朋友卻有不少人還在軟體行業摸爬滾打,也有比較優秀的,比如某某項目經理等。
  • Linux mkdir 命令的初學者教程 | Linux 中國
    編譯自 | https://www.howtoforge.com/linux-mkdir-command/  作者 | Himanshu Arora
  • linux和windows的不同
    講解對象:linux和windows的不同作者:融水公子 rsgz注意:學習linux時候儘量忘記windows思維區分1 windows中的dos命令是不區分大小寫的2 linux中命令行是區分大小寫的3 linux中所有的東西都是文件形式保存(硬體,用戶,文件都是文件)特點:1 想要這些文件永久生效2 有些文件臨時生效,一旦重啟就沒有了
  • 為 Linux 初學者講解 wc 命令 | Linux 中國
    編譯自 | https://www.howtoforge.com/linux-wc-command-explained-for-beginners-6
  • Linux替代Windows系統軟體大比拼
    linux系統近兩年在桌面應用上的發展有目共睹,並且很多人開始活躍起來了,想轉到linux下。前些日子,我寫了「為什麼我們不選擇 linux?」 的blog。引起了眾多網友的響應,足可見linux在業內的影響。然而我寫那幾條大家不選擇linux的理由並不是空空而談,是因自身使用linux的體會有感而發的。
  • Linux 內核如何處理中斷|Linux 中國
    本文字數:2640,閱讀時長大約:4分鐘 https://linux.cn/article-12965-1.html 作者:Stephan Avenwedde 譯者:萌新阿巖 中斷是現代 CPU 工作方式中重要的部分。
  • Linux 的 fmt 命令用法與案例 | Linux 中國
    編譯自 | https://www.howtoforge.com/linux-fmt-command/  作者 | Himanshu Arora
  • 邪教Arch Linux
    自從前輩大俠Unix隱居之後,windows希望一統武林,然而linux和mac不敢寂寞,在易用性和圖形界面上與之分庭抗禮。一時間江湖風起雲湧,英傑輩出。然而,在這競爭的如火如荼之時,有個少年Arch Linux踏入了這個江湖。Arch Linux始終避免意識形態之爭,不被政治因素和主流觀點所左右,實用性大於意識形態。