JavaScript >> Javascript 文檔 >  >> JavaScript

eval() 不是邪惡的,只是被誤解了

在所有的 JavaScript 中,我不確定還有比 eval() 更受詬病的部分 .這個簡單的函數旨在將字符串作為 JavaScript 代碼執行,在我的職業生涯中,它比其他任何事情都更容易引起更多的審查和誤解。短語“eval() is evil”最常被歸因於道格拉斯·克羅克福德,他曾說過 1

由於道格拉斯沒有在他的大部分著作上註明日期,因此不清楚他是否真的在 2003 年將這個詞作為一篇文章創造出來 2 也用了這句話沒提他。無論如何,它已成為任何看到 eval() 的人的首選短語 在代碼中,無論他們是否真正了解它的用途。

儘管有流行的理論(以及 Crockford 的堅持),eval() 的存在 不表示有問題。使用 eval() 不會自動讓您面臨跨站腳本 (XSS) 攻擊,也不意味著存在一些您不知道的揮之不去的安全漏洞。就像任何工具一樣,您需要知道如何正確使用它,但即使您使用不當,造成損壞的可能性仍然很低且可控。

誤用

在“eval() is evil”起源的時候,它是那些不理解 JavaScript 作為一種語言的人經常濫用的一個來源。可能會讓您感到驚訝的是,這種濫用與性能或安全性無關,而是與不了解如何在 JavaScript 中構造和使用引用有關。假設您有幾個表單輸入,其名稱包含一個數字,例如“option1”和“option2”,通常會看到這種情況:

function isChecked(optionNumber) {
    return eval("forms[0].option" + optionNumber + ".checked");
}

var result = isChecked(1);

在這種情況下,開發人員正在嘗試編寫 forms[0].option1.checked 但不知道如何在不使用 eval() 的情況下做到這一點 .您在大約十年或更早的代碼中經常看到這種模式,因為當時的開發人員只是不了解如何正確使用該語言。 eval()的使用 在這裡是不合適的,因為它是不必要的,不是因為它不好。您可以輕鬆地將此函數重寫為:

function isChecked(optionNumber) {
    return forms[0]["option" + optionNumber].checked;
}

var result = isChecked(1);

在這種性質的大多數情況下,您可以替換對 eval() 的調用 通過使用括號表示法來構造屬性名稱(畢竟,它存在的原因之一)。那些談論濫用的早期博主,包括 Crockford,大多都在談論這種模式。

可調試性

避免使用 eval() 的好理由 用於調試目的。直到最近,還無法踏入eval() 如果出現問題,請編輯代碼。這意味著您正在將代碼運行到一個黑匣子中,然後再從中退出。 Chrome 開發者工具現在可以調試 eval() 編輯代碼,但它仍然很痛苦。您必須等到代碼執行一次後才會顯示在“源”面板中。

避免 eval() ed 代碼使調試更容易,使您可以輕鬆地查看和單步調試代碼。這不會使 eval() 邪惡,必然,只是在正常的開發工作流程中有點問題。

性能

eval() 的另一個重大打擊 是它的性能影響。在較舊的瀏覽器中,您遇到了雙重解釋懲罰,也就是說您的代碼被解釋並且 eval() 中的代碼 被解釋。在不編譯 JavaScript 引擎的情況下,結果在瀏覽器中可能會慢十倍(或更糟)。

對於當今的現代編譯 JavaScript 引擎,eval() 仍然存在問題。大多數引擎可以通過以下兩種方式之一運行代碼:快速路徑或慢速路徑。快速路徑代碼是穩定且可預測的代碼,因此可以編譯以加快執行速度。慢速路徑代碼是不可預測的,因此難以編譯,並且仍然可以使用解釋器運行 3 . eval() 的存在 在您的代碼中意味著它是不可預測的,因此將在解釋器中運行 - 使其以“舊瀏覽器”速度而不是“新瀏覽器”速度運行(再次,10 倍的差異)。

另外值得注意的是,eval() 使 YUI Compressor 無法處理對 eval() 的調用範圍內的變量名稱 .自 eval() 可以直接訪問這些變量中的任何一個,重命名它們會引入錯誤(其他工具,如 Closure Compiler 和 UglifyJS 可能仍會處理這些變量 - 最終導致錯誤)。

所以使用 eval() 時性能仍然是一個大問題 .再一次,這幾乎不會使它變得邪惡,但要記住一個警告。

安全

許多人在討論 eval() 時拿出的王牌 是安全。最常見的話題是 XSS 攻擊領域以及 eval() 如何進行 向他們打開您的代碼。從表面上看,這種混淆是可以理解的,因為根據其定義 eval() 在頁面上下文中執行任意代碼。如果您接受用戶輸入並通過 eval() 運行它,這可能會很危險 .但是,如果你的輸入不是來自用戶,有什麼真正的危險嗎?

我收到了不止一個人對我的 CSS 解析器中使用 eval() 的一段代碼的投訴 4 .有問題的代碼使用 eval() 將字符串標記從 CSS 轉換為 JavaScript 字符串值。除了創建我自己的字符串解析器之外,這是獲取令牌的真實字符串值的最簡單方法。迄今為止,沒有人能夠或願意產生這種代碼引起麻煩的攻擊場景,因為:

  1. 值為eval() ed 來自分詞器。
  2. 分詞器已經驗證它是一個有效的字符串。
  3. 代碼最常在命令行上運行。
  4. 即使在瀏覽器中運行,這段代碼也被封閉在一個閉包中,不能直接調用。

當然,由於這段代碼的主要目的地是命令行,所以情況有點不同。

設計用於瀏覽器的代碼面臨不同的問題,然而,eval() 的安全性 通常不是其中之一。再一次,如果您正在接受用戶輸入並將其傳遞給 eval() 在某種程度上,你是在自找麻煩。永遠不要那樣做。但是,如果您使用 eval() 輸入只有你控制,用戶不能修改,不存在安全隱患。

這些天引用的最常見的攻擊向量是 eval() 從服務器返回的代碼。這種模式以 JSON 的引入而聞名,它之所以流行,是因為它可以通過使用 eval() 快速轉換為 JavaScript .確實,Douglas Crockford 自己使用了 eval() 在他原來的 JSON 實用程序中,因為它可以轉換的速度。他確實添加了檢查以確保沒有真正的可執行代碼,但實現基本上是 eval() .

如今,大多數使用瀏覽器的內置 JSON 解析功能來實現此目的,儘管有些仍然獲取任意 JavaScript 以通過 eval() 執行 作為延遲加載策略的一部分。有人認為,這才是真正的安全漏洞。如果中間人攻擊正在進行中,那麼您將在頁面上執行任意攻擊者代碼。

中間人攻擊被認為是 eval() 永遠存在的危險 ,打開蠕蟲的安全罐。然而,這是我最不關心的一種情況,因為任何時候你不能信任你正在聯繫的服務器,這意味著任何數量的壞事都是可能的。中間人攻擊可以通過多種方式將代碼注入頁面:

  1. 返回攻擊者控制的代碼,用於通過 <script src=""> 加載的 JavaScript .
  2. 通過為 JSON-P 請求返回攻擊者控制的代碼。
  3. 通過從 Ajax 請求返回攻擊者控制的代碼,然後是 eval() 編輯。

此外,這種攻擊可以輕鬆竊取 cookie 和用戶數據而無需更改任何內容,更不用說通過返回攻擊者控制的 HTML 和 CSS 進行網絡釣魚的可能性了。

簡而言之,eval() 不會像加載外部 JavaScript 那樣讓您面臨中間人攻擊。如果您不能信任來自服務器的代碼,那麼您將面臨比 eval() 更大的問題 在這里或那裡打電話。

結論

我不是說你應該用完並開始使用 eval() 到處。事實上,運行 eval() 的好的用例很少 一點也不。代碼清晰度、可調試性和性能肯定存在不容忽視的問題。但是當你遇到 eval() 的情況時,你不應該害怕使用它 說得通。嘗試先不要使用它,但不要讓任何人嚇到你認為你的代碼在 eval() 時更脆弱或更不安全 使用得當。

參考

  1. Douglas Crockford (JSLint) 的 JSLint 簡介
  2. Eval 是邪惡的,第一部分,Eric Lippert(Eric 的博客)
  3. David Mandelin (SlideShare) 了解您的引擎
  4. 我在 CSS 解析器中的 eval() 用法 (GitHub)

Tutorial JavaScript 教程
  1. window.ready 和 document.ready 有什麼區別

  2. 在 React 組件中訪問類實例道具?

  3. LitElement 和 TypeScript 入門

  4. 學習編碼和成為更好的開發人員的 29 個最佳 YouTube 頻道

  5. 構建 WebRTC 視頻聊天應用程序

  6. 讓我們用 React 做一個井字遊戲!

  7. 擲骰子:Redux 中的隨機數

  1. 為什麼 canvas.toDataURL() 會拋出安全異常?

  2. 當你的簡歷很糟糕時如何找到一份編程工作

  3. 初學者的 ReactJS 服務器端渲染

  4. 數據庫說不

  5. 如何在靜態 Nuxt 應用程序中使用平面文件數據

  6. JavaScript 程序找出低於一個給定數的所有奇數之和

  7. 你現在需要知道的 Javascript 片段🔥 - #2

  1. 使用 Puppeteer 和 Node.js 抓取(幾乎)任何東西的簡介

  2. 每個開發人員都應該為 ES2019 JavaScript 新特性感到興奮

  3. 每個有抱負的 Javascript 開發人員必不可少的 5 個 Github 項目

  4. 使用 Webpack 和 Babel 設置 React 應用程序