it-swarm-vi.tech

Tại sao sử dụng hàm eval JavaScript là một ý tưởng tồi?

Hàm eval là một cách mạnh mẽ và dễ dàng để tạo mã động, vậy các cảnh báo là gì?

503
Brian Singh
  1. Việc sử dụng không đúng cách eval sẽ mở mã của bạn cho các cuộc tấn công tiêm chích

  2. Gỡ lỗi có thể khó khăn hơn (không có số dòng, v.v.)

  3. mã eval'd thực thi chậm hơn (không có cơ hội biên dịch/bộ đệm mã eval'd)

Chỉnh sửa: Như @Jeff Walden đã chỉ ra trong các nhận xét, # 3 ngày nay ít đúng hơn so với năm 2008. Tuy nhiên, trong khi một số bộ đệm của các tập lệnh được biên dịch có thể xảy ra, điều này sẽ chỉ giới hạn ở các tập lệnh được lặp đi lặp lại mà không sửa đổi. Một kịch bản có khả năng hơn là bạn đang đánh giá các tập lệnh đã trải qua sửa đổi nhỏ mỗi lần và như vậy không thể được lưu trữ. Chúng ta hãy nói rằng MỘT SỐ mã eval'd thực thi chậm hơn.

369
Prestaul

eval không phải lúc nào cũng xấu xa. Có những lúc nó hoàn toàn thích hợp.

Tuy nhiên, eval hiện đang được sử dụng một cách ồ ạt bởi những người không biết họ đang làm gì. Điều đó bao gồm những người viết hướng dẫn JavaScript, thật không may, và trong một số trường hợp, điều này thực sự có thể có hậu quả bảo mật - hoặc, thường xuyên hơn, các lỗi đơn giản. Vì vậy, chúng ta càng có thể làm để ném một dấu hỏi lên eval, thì càng tốt. Bất cứ khi nào bạn sử dụng eval, bạn cần phải tỉnh táo - kiểm tra xem bạn đang làm gì, bởi vì rất có thể bạn có thể thực hiện nó một cách tốt hơn, an toàn hơn, sạch sẽ hơn.

Để đưa ra một ví dụ quá điển hình, để đặt màu của một phần tử với id được lưu trong biến 'khoai tây':

eval('document.' + potato + '.style.color = "red"');

Nếu các tác giả của loại mã trên có manh mối về những điều cơ bản về cách thức hoạt động của các đối tượng JavaScript, thì họ đã nhận ra rằng dấu ngoặc vuông có thể được sử dụng thay cho tên dấu chấm bằng chữ, không cần phải sử dụng

document[potato].style.color = 'red';

... dễ đọc hơn cũng như ít lỗi hơn.

(Nhưng sau đó, một người/thực sự/biết những gì họ đang làm sẽ nói:

document.getElementById(potato).style.color = 'red';

đáng tin cậy hơn thủ thuật cũ tinh ranh khi truy cập các phần tử DOM ngay từ đối tượng tài liệu.)

343
bobince

Tôi tin rằng vì nó có thể thực thi bất kỳ hàm JavaScript nào từ một chuỗi. Sử dụng nó giúp mọi người dễ dàng tiêm mã lừa đảo vào ứng dụng.

36
kemiller2002

Hai điểm đến trong tâm trí:

  1. Bảo mật (nhưng miễn là bạn tạo chuỗi để tự đánh giá, đây có thể không phải là vấn đề)

  2. Hiệu suất: cho đến khi mã được thực thi là không xác định, nó không thể được tối ưu hóa. (về javascript và hiệu suất, chắc chắn bài thuyết trình của Steve Yegge )

26
xtofl

Truyền đầu vào của người dùng vào eval () là một rủi ro bảo mật, nhưng mỗi lần gọi eval () tạo ra một phiên bản mới của trình thông dịch JavaScript. Đây có thể là một con heo tài nguyên.

20
Andrew Hedges

Nó thường chỉ là một vấn đề nếu bạn chuyển đầu vào của người dùng tệ.

17
Mark Biek

Chủ yếu, nó khó hơn rất nhiều để duy trì và gỡ lỗi. Nó giống như một goto. Bạn có thể sử dụng nó, nhưng nó khiến việc tìm kiếm vấn đề trở nên khó khăn hơn và khó hơn đối với những người có thể cần thực hiện thay đổi sau này.

15
Brian

Một lưu ý là bạn thường có thể sử dụng eval () để thực thi mã trong môi trường bị hạn chế khác - các trang web mạng xã hội chặn các hàm JavaScript cụ thể đôi khi có thể bị đánh lừa bằng cách phá vỡ chúng trong một khối eval -

eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');

Vì vậy, nếu bạn đang tìm cách chạy một số mã JavaScript, nơi nó có thể không được phép ( Myspace , tôi đang nhìn bạn ...) thì eval () có thể là một mẹo hữu ích.

Tuy nhiên, vì tất cả các lý do đã đề cập ở trên, bạn không nên sử dụng nó cho mã của riêng mình, nơi bạn có toàn quyền kiểm soát - điều đó là không cần thiết và tốt hơn là nên chuyển sang kệ 'hack JavaScript lừa đảo'.

13
matt lohkamp

Trừ khi bạn để eval () một nội dung động (thông qua cgi hoặc đầu vào), nó sẽ an toàn và vững chắc như tất cả các JavaScript khác trong trang của bạn.

11
Thevs

Cùng với phần còn lại của câu trả lời, tôi không nghĩ các tuyên bố tệ hại có thể được giảm thiểu nâng cao.

7
Paul Mendoza

Đó là một rủi ro bảo mật có thể xảy ra, nó có phạm vi thực thi khác và khá kém hiệu quả, vì nó tạo ra một môi trường tập lệnh hoàn toàn mới để thực thi mã. Xem ở đây để biết thêm thông tin: eval .

Tuy nhiên, nó khá hữu ích và được sử dụng có chừng mực có thể bổ sung rất nhiều chức năng tốt.

6
Tom

Tôi biết cuộc thảo luận này đã cũ, nhưng tôi thực sự thích điều này cách tiếp cận của Google và muốn chia sẻ cảm giác đó với người khác;)

Một điều nữa là bạn càng hiểu rõ hơn Bạn càng cố gắng hiểu và cuối cùng Bạn không tin rằng điều gì đó tốt hay xấu chỉ vì ai đó nói vậy :) Đây là một điều rất truyền cảm video đó đã giúp tôi suy nghĩ nhiều hơn một mình :) THỰC HÀNH TỐT là tốt, nhưng đừng sử dụng chúng một cách thiếu suy nghĩ :)

5
op1ekun

Trừ khi bạn chắc chắn 100% rằng mã được đánh giá là từ một nguồn đáng tin cậy (thường là ứng dụng của riêng bạn) thì đó là cách chắc chắn để đưa hệ thống của bạn vào một cuộc tấn công kịch bản chéo trang.

5
John Topley

Nó không hẳn là xấu với điều kiện bạn biết bạn đang sử dụng bối cảnh nào.

Nếu ứng dụng của bạn đang sử dụng eval() để tạo một đối tượng từ một số JSON đã quay trở lại từ XMLHttpRequest đến trang web của riêng bạn, được tạo bởi mã phía máy chủ đáng tin cậy của bạn, thì đó có lẽ không phải là vấn đề.

Mã JavaScript phía máy khách không tin cậy dù sao cũng không thể làm được nhiều như vậy. Với điều kiện bạn đang thực hiện eval() trên đã đến từ một nguồn hợp lý, bạn vẫn ổn.

5
MarkR

Nó làm giảm đáng kể mức độ tự tin của bạn về bảo mật.

4
David Plumpton

Nếu bạn muốn người dùng nhập một số hàm logic và đánh giá cho AND OR thì hàm eval JavaScript là hoàn hảo. Tôi có thể chấp nhận hai chuỗi và eval(uate) string1 === string2, v.v.

4
Ian

Nếu bạn phát hiện ra việc sử dụng eval () trong mã của mình, hãy nhớ câu thần chú là eval () là ác.

Hàm này lấy một chuỗi tùy ý và thực thi nó dưới dạng mã JavaScript. Khi mã được đề cập đã được biết trước (không được xác định trong thời gian chạy), thì không có lý do gì để sử dụng eval (). Nếu mã được tạo động khi chạy, thì thường có một cách tốt hơn để đạt được mục tiêu mà không cần eval (). Ví dụ: chỉ cần sử dụng ký hiệu ngoặc vuông để truy cập các thuộc tính động là tốt hơn và đơn giản hơn:

// antipattern
var property = "name";
alert(eval("obj." + property));

// preferred
var property = "name";
alert(obj[property]);

Sử dụng eval() cũng có ý nghĩa bảo mật, bởi vì bạn có thể đang thực thi mã (ví dụ: đến từ mạng) đã bị giả mạo. Đây là một phản mẫu phổ biến khi xử lý phản hồi JSON từ yêu cầu Ajax. Trong những trường hợp đó, tốt hơn hết là sử dụng các trình duyệt Các phương thức tích hợp sẵn để phân tích phản hồi JSON để đảm bảo rằng nó an toàn và hợp lệ. Đối với các trình duyệt không hỗ trợ JSON.parse(), bạn có thể sử dụng thư viện từ JSON.org.

Nó cũng rất quan trọng để nhớ rằng việc chuyển các chuỗi tới setInterval(), setTimeout() và phần lớn hàm tạo Function() tương tự như sử dụng eval() và do đó nên tránh.

Đằng sau hậu trường, JavaScript vẫn phải đánh giá và thực thi chuỗi bạn truyền dưới dạng mã lập trình:

// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

Sử dụng hàm tạo Hàm () mới tương tự như eval () và cần được tiếp cận cẩn thận. Nó có thể là một cấu trúc mạnh mẽ nhưng thường bị sử dụng sai. Nếu bạn hoàn toàn phải sử dụng eval(), bạn có thể xem xét sử dụng Hàm mới () thay thế.

Có một lợi ích tiềm năng nhỏ vì mã được đánh giá trong Hàm mới () sẽ chạy trong phạm vi hàm cục bộ, do đó, bất kỳ biến nào được xác định bằng var trong mã được đánh giá sẽ không tự động trở thành toàn cục.

Một cách khác để ngăn chặn toàn cầu tự động là bọc cuộc gọi eval() thành một chức năng ngay lập tức.

3
12345678

Bên cạnh các vấn đề bảo mật có thể xảy ra nếu bạn đang thực thi mã do người dùng gửi, hầu hết thời gian có một cách tốt hơn không liên quan đến việc phân tích lại mã mỗi khi mã được thực thi. Các hàm hoặc thuộc tính đối tượng ẩn danh có thể thay thế hầu hết việc sử dụng eval và an toàn hơn và nhanh hơn nhiều.

2
Matthew Crumley

Điều này có thể trở thành một vấn đề khi thế hệ trình duyệt tiếp theo xuất hiện với một số hương vị của trình biên dịch JavaScript. Mã được thực thi thông qua Eval có thể không hoạt động tốt như phần còn lại của JavaScript đối với các trình duyệt mới hơn này. Ai đó nên làm một số hồ sơ.

2
Brian

eval () rất mạnh và có thể được sử dụng để thực thi câu lệnh JS hoặc đánh giá một biểu thức. Nhưng câu hỏi không phải là về việc sử dụng eval () mà chỉ nói một số cách chuỗi bạn chạy với eval () bị ảnh hưởng bởi một bên độc hại. Cuối cùng, bạn sẽ chạy mã độc. Với quyền lực đi kèm là trách nhiệm lớn. Vì vậy, sử dụng nó một cách khôn ngoan là bạn đang sử dụng nó. Điều này không liên quan nhiều đến chức năng eval () nhưng bài viết này có thông tin khá tốt: http://bloss.popart.com/2009/07/javascript-injection-attacks/ Nếu bạn đang tìm kiếm để biết những điều cơ bản về eval () hãy xem tại đây: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

2
Phi

Đây là một trong những bài viết hay nói về eval và làm thế nào nó không phải là xấu xa: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunder Hiểu /

Tôi không nói bạn nên chạy ra ngoài và bắt đầu sử dụng eval () ở mọi nơi. Trong thực tế, có rất ít trường hợp sử dụng tốt để chạy eval () cả. Chắc chắn có những lo ngại về tính rõ ràng của mã, khả năng gỡ lỗi và hiệu năng chắc chắn không nên bỏ qua. Nhưng bạn không nên sợ sử dụng nó khi bạn gặp trường hợp eval () có ý nghĩa. Hãy thử không sử dụng nó trước, nhưng don không cho phép bất cứ ai sợ bạn nghĩ rằng mã của bạn mỏng manh hơn hoặc kém an toàn hơn khi eval () được sử dụng một cách thích hợp.

2
Amr Elgarhy

Nó không phải luôn luôn là một ý tưởng tồi. Lấy ví dụ, tạo mã. Gần đây tôi đã viết một thư viện có tên Hyperbars để thu hẹp khoảng cách giữa virtual-domtay cầm . Nó thực hiện điều này bằng cách phân tích một mẫu tay lái và chuyển đổi nó thành hyperscript mà sau đó được sử dụng bởi virtual-dom. Siêu ký tự được tạo dưới dạng một chuỗi trước tiên và trước khi trả lại chuỗi, eval() để biến nó thành mã thực thi. Tôi đã tìm thấy eval() trong tình huống cụ thể này trái ngược hoàn toàn với cái ác.

Cơ bản từ

<div>
    {{#each names}}
        <span>{{this}}</span>
    {{/each}}
</div>

Để điều này

(function (state) {
    var Runtime = Hyperbars.Runtime;
    var context = state;
    return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
        return [h('span', {}, [options['@index'], context])]
    })])
}.bind({}))

Hiệu suất của eval() không phải là vấn đề trong tình huống như thế này vì bạn chỉ cần diễn giải chuỗi được tạo một lần và sau đó sử dụng lại đầu ra thực thi nhiều lần.

Bạn có thể thấy cách tạo mã đã đạt được nếu bạn tò mò tại đây .

1
Wikened

Tôi sẽ không cố gắng bác bỏ bất cứ điều gì đã nói ở đây, nhưng tôi sẽ đề nghị sử dụng eval () mà theo như tôi biết) không thể được thực hiện bằng bất kỳ cách nào khác. Có lẽ có nhiều cách khác để mã hóa điều này, và có thể là cách để tối ưu hóa nó, nhưng điều này được thực hiện từ lâu và không có bất kỳ tiếng chuông và tiếng huýt sáo nào để minh họa cho việc sử dụng eval mà thực sự không có lựa chọn thay thế nào khác. Đó là: tên đối tượng động (hoặc chính xác hơn) được tạo theo chương trình (trái ngược với giá trị).

//Place this in a common/global JS lib:
var NS = function(namespace){
    var namespaceParts = String(namespace).split(".");
    var namespaceToTest = "";
    for(var i = 0; i < namespaceParts.length; i++){
        if(i === 0){
            namespaceToTest = namespaceParts[i];
        }
        else{
            namespaceToTest = namespaceToTest + "." + namespaceParts[i];
        }

        if(eval('typeof ' + namespaceToTest) === "undefined"){
            eval(namespaceToTest + ' = {}');
        }
    }
    return eval(namespace);
}


//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
  //Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
    //Code goes here
    //this.MyOtherMethod("foo"));  // => "foo"
    return true;
}


//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);

EDIT: nhân tiện, tôi sẽ không đề xuất (vì tất cả các lý do bảo mật đã chỉ ra ở đây) rằng bạn căn cứ vào tên đối tượng của bạn trên đầu vào của người dùng. Tôi không thể tưởng tượng bất kỳ lý do tốt mà bạn muốn làm điều đó mặc dù. Tuy nhiên, tôi nghĩ rằng tôi sẽ chỉ ra rằng nó sẽ không phải là một ý tưởng tốt :)

1
Carnix

Tôi sẽ đi xa hơn để nói rằng nó không thực sự quan trọng nếu bạn sử dụng eval() trong javascript được chạy trong trình duyệt. * (Hãy cẩn thận)

Tất cả các trình duyệt hiện đại đều có bảng điều khiển dành cho nhà phát triển nơi bạn có thể thực thi javascript tùy ý và bất kỳ nhà phát triển bán thông minh nào cũng có thể xem nguồn JS của bạn và đặt bất kỳ bit nào của nó vào bảng điều khiển dev để làm những gì họ muốn.

* Miễn là các điểm cuối máy chủ của bạn có xác thực & vệ sinh chính xác các giá trị do người dùng cung cấp, không có vấn đề gì được phân tích cú pháp và đánh giá trong javascript phía máy khách của bạn.

Tuy nhiên, nếu bạn hỏi liệu có phù hợp để sử dụng eval() trong PHP không, câu trả lời là NO, trừ khi bạn danh sách trắng bất kỳ giá trị nào có thể là thông qua tuyên bố eval của bạn.

1
Adam Copley

Công cụ JavaScript có một số tối ưu hóa hiệu suất mà nó thực hiện trong giai đoạn biên dịch. Một số trong số này có khả năng phân tích tĩnh mã khi nó lexes và xác định trước vị trí của tất cả các khai báo biến và hàm, do đó, sẽ mất ít nỗ lực hơn để giải quyết định danh trong khi thực thi.

Nhưng nếu Engine tìm thấy một eval (..) trong mã, thì về cơ bản, nó phải thừa nhận rằng tất cả nhận thức về vị trí định danh của nó có thể không hợp lệ, bởi vì nó không thể biết chính xác thời gian mà bạn có thể chuyển cho eval (..) để sửa đổi phạm vi từ vựng hoặc nội dung của đối tượng bạn có thể chuyển qua để tạo phạm vi từ vựng mới cần tham khảo.

Nói cách khác, theo nghĩa bi quan, hầu hết những tối ưu hóa đó sẽ trở nên vô nghĩa nếu eval (..) có mặt, vì vậy đơn giản là nó không thực hiện tối ưu hóa.

Điều này giải thích tất cả.

Tài liệu tham khảo :

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval

https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance

1
hkasera

Garbage collection

Bộ sưu tập rác của trình duyệt không biết liệu mã đó có thể bị xóa khỏi bộ nhớ hay không để nó được lưu trữ cho đến khi trang được tải lại. Không quá tệ nếu người dùng của bạn chỉ ở trên trang của bạn trong thời gian ngắn, nhưng nó có thể là một vấn đề đối với webapp.

Đây là một kịch bản để demo vấn đề

https://jsfiddle.net/CynderRnAsh/qux1osnw/

document.getElementById("evalLeak").onclick = (e) => {
  for(let x = 0; x < 100; x++) {
    eval(x.toString());
  }
};

Một cái gì đó đơn giản như đoạn mã trên khiến một lượng nhỏ bộ nhớ được lưu trữ cho đến khi ứng dụng chết. Điều này tồi tệ hơn khi tập lệnh bị loại bỏ là một hàm khổng lồ và được gọi là khoảng thời gian.

0
J D