eval()不是魔鬼,只是被誤解了(翻譯)
原文來自:https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/ 作者:Nicholas C.Zakas
在JavaScript中,我不確定是否有比eval()受到更多誹謗的。它就是個簡單的函式被設計用來將字串轉換為可被執行的JavaScript程式碼。在我的早期的職業生涯裡,它比任何其他的東西更受關注和誤解。
大多數人認為‘eval()是魔鬼’這句話是Douglas Crockford說的。他說:“eval函式(及其親屬,Function,setTimeout,和setInterval)提供對JavaScript編譯器的訪問。這有時是必要的,但大多數情況,它證明這個存在是極其糟糕的程式碼。因為eval()這個特性常常被誤用”。
因為道格拉斯的大多數作品並沒有註明日期,所以,我不確定他是否是在2002年創造了這個術語。不管怎麼說吧,不管是否真正理解了eval()的使用,他都成為了一個熱門的短語。
儘管這種理論流行開來了(道格拉斯的堅持),但這並不意味著eval()的存在就有問題。使用eval()不會自動觸發XSS攻擊,或者你沒有意識到的但存在的安全漏洞。就像工具一樣,你要知道如何使用它,但即使你使用不正確,但潛在風險依然很低,並且是可容忍的。
誤用(濫用)
eval()之所以成為了魔鬼,是因為那些對JavaScript語言理解不夠深的人誤用的原因。你可能會奇怪,誤用跟安全和效能好型沒有關係吧。誤用是不理解如何在JavaScript中構造和使用引用。假設你有幾個表單input的名字包含數字,如:option1,option2,常見的程式碼實現如下:
function isChecked(optionNumber) {
return eval("forms[0].option" + optionNumber + ".checked");
}
var result = isChecked(1);
在這種情況下,開發人員在盡力嘗試寫forms[0].option1.checked,而沒有想我不用eval()該如何做。這樣的情況出現在很多10年左右工作經驗的開發者,它們不明白如何更好地使用。這裡也不是說eval()在這裡不合適,而是因為沒有必要,你完全可以更簡單的實現,用如下的程式碼:
function isChecked(optionNumber) {
return forms[0]["option" + optionNumber].checked;
}
var result = isChecked(1);
在很多情況下,你可以使用[]
表示法來替換eval()的使用去構造屬性名,這也是 []
存在的一個原因。包括道格拉斯在內的早期博主們都是在討論這個問題。
可調式性
不使用eval()的一個重要理由是為了達到除錯的目的。以前,如果出現問題,不可能進入eval()程式碼。這就意味著你的程式碼執行在一個黑盒中,然後從中取出。現在Chrome開發工具可以除錯eval()內的程式碼,但是有一個問題是,你必須等程式碼執行一次後才出現在源皮膚中。
不使用eval()可以令我們的程式碼除錯起來更容易,跟方便的檢視原始碼。但這並不能說明eval()是魔鬼,這只是開發工作流程中的一點問題。
效能
對eval()的另一個重要影響是它的效能。在舊的瀏覽器中,你遇到了雙重解釋懲罰,也就是說,你的程式碼被解釋,而eval()中的程式碼被解釋。在沒有編譯JavaScript引擎的瀏覽器中,結果可能會慢十倍(甚至更糟)。
在現代編譯JavaScript的引擎中,eval()仍然是一個問題。大多數引擎可以用兩種方式執行程式碼:快速路徑或慢路徑。快速路徑程式碼是一種穩定且可預測的程式碼,因此可以為更快的執行而編譯。緩慢的路徑程式碼是不可預測的,這使得編譯很難,並且可能仍然使用一個解釋程式執行。在你的程式碼中僅僅存在eval()意味著它是不可預測的,因此將在直譯器中執行它以“舊瀏覽器”的速度執行,而不是“新瀏覽器”的速度(10倍的差異)。
同樣的,eval()使YUI壓縮器不可能在呼叫eval()的範圍內munge變數名。由於eval()可以直接訪問任何這些變數,重新命名它們會引入錯誤(其他工具如閉包編譯器和UglifyJS可能仍然會矇混這些變數——最終導致錯誤)。
因此,在使用eval()時,效能仍然是一個大問題。但這很難讓它成為邪惡,但這是一個需要注意的警告。
安全
在說到eval()的安全時,這是大部分人認為eval是魔鬼的有力佐證。大多數情況下,就是說XSS攻擊,以及eval()如何向它們開啟程式碼。在表面上,這種混淆是可以理解的,因為eval()在頁面上下文中可執行任意程式碼。如果你接受使用者輸入並通過eval()執行它,這將是危險的。但是,如果你的輸入不是來自使用者,是否存在真正的危險?
我收到了不止一個的抱怨,在我的CSS解析器中使用eval()的一段程式碼中使用的程式碼使用eval()將字串標記從CSS轉換為JavaScript字串值。除了建立自己的字串解析器外,這是獲得token的正確字串值的最簡單方法。到目前為止,還沒有人能夠或願意提出一種攻擊方案,在這種情況下,這段程式碼會引起麻煩,因為:
- eval()的值來自於tokenizer
- tokenizer已經驗證了它是一個有效的字串
- 程式碼最常在命令列上執行。
- 即使在瀏覽器中執行,該程式碼也被封閉在閉包中,不能直接呼叫。
當然,由於這段程式碼的主要目標是命令列,所以這個故事有點不同。
設計用於瀏覽器的程式碼面臨不同的問題,但是eval()的安全性通常不是其中之一。同樣,如果你以某種方式接收使用者輸入並將其傳遞給eval(),那麼你就是在自找麻煩,不要這樣做。但是,如果你使用eval()的輸入,只有你控制並且不能被使用者修改,那麼就沒有安全風險。
最常見的攻擊是來自伺服器的eval()程式碼。這一模式以引入JSON而著名,它之所以流行,是因為它可以通過eval()快速轉換成JavaScript。實際上,Douglas Crockford自己在他的原始JSON實用程式中使用eval(),因為它可以轉換速度。他確實新增了檢查以確保沒有真正的可執行程式碼,但是實現從根本上是eval()。
現在,大多數人都使用瀏覽器內建的JSON解析功能來實現這一目的,儘管有些人仍然通過eval()來獲取任意的JavaScript,作為延遲載入策略的一部分。一些人認為,這才是真正的安全漏洞。如果正在進行中間人攻擊,那麼你將在頁面上執行任意攻擊程式碼。
中間人攻擊被認為是eval()的永遠存在的危險,會受到蠕蟲的的攻擊。但是,這是一個與我無關的場景,因為任何時候你不能相信你正在聯絡的伺服器,就意味著有可能出現很多不好的事情。中間人攻擊可以通過多種方式向頁面注入程式碼:
- 返回通過
<script></script>
載入的JavaScript程式碼。 - 通過返回攻擊者控制的JSON-P請求程式碼。
- 通過從一個Ajax請求返回attacker控制的程式碼,然後eval()。
此外,這樣的攻擊可以很容易地竊取cookie和使用者資料,而不會改變任何東西,更不用說通過返回攻擊者控制的HTML和CSS來進行網路釣魚的可能性了。
簡單地說,eval()不會像載入外部JavaScript那樣開啟中間人攻擊。如果你不能信任伺服器上的程式碼,那麼你將遇到比eval()呼叫更大的問題。
結論
我不是說你應該跑出去,開始使用eval()。實際上,很少有好的用例來執行eval()。對於程式碼清晰性、除錯性,以及不應該忽略的效能,確實存在一些問題。但是在eval()有意義的情況下,你不應該害怕使用它。不要第一次使用它,但是不要讓任何人嚇到你,認為你的程式碼在使用eval()時更加脆弱或不安全。
文章稍後可能還會繼續修改,也歡迎各位批評指正。有問題或者有其他想法的可以在我的GitHub上pr。
相關文章
- Yurii談翻譯(十一)怎樣翻譯更地道:and不是“和”
- Yurii談翻譯(七)怎樣翻譯更地道:被濫用的“被”
- QT TS檔案翻譯,部分不能正確被翻譯QT
- 為何HTTP被翻譯為“超文字傳輸協議”是一次歷史上的重大翻譯錯誤?HTTP協議
- 被嫌棄的eval和with
- 人們誤解了OOPOOP
- 【漢譯英】翻譯當然不是一成不變的啦
- 朱棣文演講的翻譯勘誤
- buffer busy waits你誤解了嗎?AI
- HTTP 是不是應該翻譯成超文字傳輸協議HTTP協議
- 【翻譯】在Spring WebFlux中處理錯誤SpringWebUX
- 中國遊戲眾籌:不是變味,只是變了遊戲
- 微服務不是全部,只是特定領域的子集微服務
- 翻譯
- Yurii談翻譯(五)怎樣翻譯更地道:so…that…的翻譯
- 如何完成中文翻譯日文線上翻譯
- Yurii談翻譯(四)怎樣翻譯更地道:翻譯如鋪路
- Yurii談翻譯(九)怎樣翻譯更地道:冠詞a的翻譯
- Yurii談翻譯(十)怎樣翻譯更地道:最高階的翻譯
- 翻譯的未來:翻譯機器和譯後編譯編譯
- Ubuntu安裝劃詞翻譯軟體Goldendict 單詞翻譯 句子翻譯UbuntuGo
- Yurii談翻譯(六)怎樣翻譯更地道:“as somebody said…”的翻譯AI
- Yurii談翻譯(十三)怎樣翻譯更地道:It is…that…句型諺語的翻譯
- Yurii談翻譯(十四)怎樣翻譯更地道:否定句的翻譯
- 蝴蝶書-task2: 文字推理、摘要、糾錯 transformers實現翻譯 OpenAI翻譯 PyDeepLX翻譯 DeepLpro翻譯ORMOpenAI
- (譯)React hooks:它不是一種魔法,只是一個陣列——使用圖表揭祕提案規則ReactHook陣列
- Javascript中的魔鬼JavaScript
- Nginx翻譯Nginx
- [翻譯] TransitionKit
- 翻譯篇
- OllDbg翻譯LLDB
- OpenCV翻譯專案總結二——Mat翻譯OpenCV
- 你誤解了Windows的檔案字尾名嗎?Windows
- Dapr 不是服務網格,只是我長的和他很像
- 【翻譯】卡通圖解DNS,你的資訊怎麼被洩露的?圖解DNS
- eval
- 文件翻譯器怎麼用?如何翻譯Word文件?
- Laravel 谷歌翻譯 /Bing 翻譯擴充套件包Laravel谷歌套件