eval 함수는 코드를 동적으로 생성하는 강력하고 쉬운 방법이므로주의해야 할 점은 무엇입니까?
eval 함수는 코드를 동적으로 생성하는 강력하고 쉬운 방법이므로주의해야 할 점은 무엇입니까?
답변:
eval을 부적절하게 사용하면 주입 공격에 대한 코드가 열립니다.
디버깅 은 더 어려울 수 있습니다 (행 번호 등 없음)
eval'd 코드가 느리게 실행 됨 (eval'd 코드를 컴파일 / 캐시 할 기회 없음)
편집 : @ Jeff Walden이 의견에서 지적했듯이 # 3은 2008 년보다 오늘날에는 덜 사실입니다. 그러나 컴파일 된 스크립트의 일부 캐싱이 발생할 수 있지만 수정없이 반복되는 스크립트로만 제한됩니다. 더 가능성이 높은 시나리오는 매번 약간 씩 수정 된 스크립트를 평가하여 캐시 할 수 없다는 것입니다. 일부 평가 된 코드가 더 느리게 실행된다고 가정 해 봅시다.
badHackerGuy'); doMaliciousThings();
과 같이 설정할 수 있습니다. 사용자 이름을 가져 와서 다른 스크립트로 연결하여 다른 사람들의 브라우저에서 평가하면 컴퓨터에서 원하는 모든 자바 스크립트를 실행할 수 있습니다 (예 : 내 게시물을 +1하도록 강제, 내 서버 등에 데이터를 게시하십시오.)
debugger;
명령문을 소스 코드 에 추가하여 코드화를 위해 Chrome에서 생성 한 가상 파일에 액세스 할 수 있습니다 . 해당 라인에서 프로그램 실행이 중지됩니다. 그런 다음 다른 JS 파일과 같은 디버그 중단 점을 추가 할 수 있습니다.
평가가 항상 악한 것은 아닙니다. 완벽하게 적절한 때가 있습니다.
그러나 평가는 현재 그리고 역사적으로 자신이하는 일을 모르는 사람들에 의해 과도하게 사용되고 있습니다. 불행히도 JavaScript 자습서를 작성하는 사람들이 포함되며 경우에 따라 보안에 영향을 줄 수 있습니다. 따라서 eval에 물음표를 던질수록할수록 좋습니다. eval을 사용할 때마다 수행중인 작업의 위생 상태를 점검해야합니다. 더 안전하고 깔끔한 방법으로 수행 할 수 있기 때문입니다.
변수를 'potato'에 저장 한 id로 요소의 색상을 설정하려면 일반적인 예를 들자면 :
eval('document.' + potato + '.style.color = "red"');
위와 같은 코드의 작성자가 JavaScript 객체의 작동 원리에 대한 단서가 있었다면 리터럴 점 이름 대신 대괄호를 사용할 수 있다는 사실을 깨달았을 것입니다.
document[potato].style.color = 'red';
... 읽기가 훨씬 쉬우 며 버그가 적습니다.
(그러나, 자신이하고있는 일을 / 정말로 / 알고있는 사람은 :
document.getElementById(potato).style.color = 'red';
문서 객체에서 DOM 요소에 직접 액세스하는 오래된 트릭보다 더 안정적입니다.)
JSON.parse()
대신 사용하지 않아야 eval()
합니까?
문자열에서 JavaScript 함수를 실행할 수 있기 때문이라고 생각합니다. 이를 사용하면 사람들이 악성 코드를 응용 프로그램에 쉽게 삽입 할 수 있습니다.
두 가지 점이 생각납니다.
보안 (하지만 직접 평가할 문자열을 생성하는 한, 이것은 문제가되지 않을 수 있음)
성능 : 실행될 코드를 알 수 없을 때까지 최적화 할 수 없습니다. (자바 스크립트 및 성능, 확실히 Steve Yegge의 프레젠테이션 )
eval
, 다음 uated 해당 사용자의 B 브라우저에서 코드 실행의 작은 조각
사용자 입력을 eval ()에 전달하는 것은 보안 위험이지만 eval ()을 호출 할 때마다 JavaScript 인터프리터의 새 인스턴스가 작성됩니다. 이것은 자원 돼지 일 수 있습니다.
주로 유지 관리 및 디버그가 훨씬 어렵습니다. 그것은 같다 goto
. 사용할 수는 있지만 나중에 변경해야 할 사람들에게 문제를 찾기 어렵고 어렵게 만듭니다.
명심해야 할 것은 eval ()을 사용하여 제한된 환경에서 코드를 실행할 수 있다는 것입니다. 특정 JavaScript 함수를 차단하는 소셜 네트워킹 사이트는 eval 블록으로 분리하여 속일 수 있습니다.
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
따라서 허용되지 않을 수있는 JavaScript 코드를 실행하려는 경우 ( Myspace , 당신을보고 있습니다 ...) eval ()은 유용한 트릭입니다.
그러나 위에서 언급 한 모든 이유로 인해 완전한 제어 권한이있는 자체 코드에는이 코드를 사용해서는 안됩니다. 필요하지 않으며 'tricky JavaScript hacks'선반으로 강등됩니다.
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
eval ()에 cgi 또는 input을 통한 동적 내용을 허용하지 않으면 페이지의 다른 모든 JavaScript와 마찬가지로 안전하고 견고합니다.
나는이 토론이 오래 되었다는 것을 알고 있지만 Google 의이 접근법을 정말로 좋아 하며 다른 사람들과 그 느낌을 나누고 싶었습니다.)
다른 것은 더 나은 당신이 얻을 것이다 더 많이 이해하려고 노력 그리고 마지막으로 당신은 그냥 뭔가 누군가가 그렇게 :)이 매우 영감했다해서 좋거나 나쁜 생각하지 않는다 비디오 나 자신에 의해 더 생각하는 데 도움이 :) 좋은 연습은 좋지만 마음대로 사용하지 마십시오. :)
사용중인 컨텍스트를 알고 있다면 반드시 나쁘지는 않습니다.
애플리케이션이 XMLHttpRequesteval()
에서 돌아온 일부 JSON에서 오브젝트를 작성하는 데 사용중인 경우 에서 신뢰할 수있는 서버 측 코드로 생성 된 자신의 사이트로 경우 문제가되지 않습니다.
신뢰할 수없는 클라이언트 측 JavaScript 코드는 어쨌든 그렇게 할 수 없습니다. 실행중인 eval()
것이 합리적인 소스에서 온 것이라면 괜찮습니다.
보안에 대한 신뢰 수준이 크게 줄어 듭니다.
코드에서 eval ()의 사용을 발견하면“eval ()은 악하다”라는 진언을 기억하십시오.
이 함수는 임의의 문자열을 받아서 JavaScript 코드로 실행합니다. 문제의 코드를 미리 알고 있으면 (런타임에 결정되지 않음) eval ()을 사용할 이유가 없습니다. 런타임에 코드가 동적으로 생성되면 eval ()없이 목표를 달성하는 더 좋은 방법이 종종 있습니다. 예를 들어 대괄호 표기법을 사용하여 동적 속성에 액세스하는 것이 더 좋고 간단합니다.
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
사용 eval()
이 변조 된 (네트워크에서 오는 예를 들어) 코드를 실행 할 수 있기 때문에, 보안 문제를 가지고도. 이것은 Ajax 요청의 JSON 응답을 처리 할 때 일반적인 반 패턴입니다. 이러한 경우 브라우저의 내장 메소드를 사용하여 JSON 응답을 구문 분석하여 안전하고 유효한지 확인하는 것이 좋습니다. 지원하지 않는 브라우저JSON.parse()
기본적으로 JSON.org의 라이브러리를 사용할 수 있습니다.
문자열을 setInterval()
,에 전달 setTimeout()
하면 Function()
생성자가 대부분 사용 eval()
과 비슷 하므로 피해야합니다.
배후에서 JavaScript는 여전히 프로그래밍 코드로 전달한 문자열을 평가하고 실행해야합니다.
// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);
new Function () 생성자를 사용하는 것은 eval ()과 유사하며주의해서 접근해야합니다. 그것은 강력한 구조 일 수 있지만 종종 오용됩니다. 당신이 절대적으로 사용해야하는 경우eval()
대신 new Function () 사용을 고려할 수 있습니다.
new Function ()에서 평가 된 코드가 로컬 함수 범위에서 실행되므로 평가중인 코드에서 var로 정의 된 모든 변수가 자동으로 전역 변수가되지 않기 때문에 잠재적 인 이점이 거의 없습니다.
자동 전역을 방지하는 또 다른 방법은 eval()
통화를 즉시 기능 으로 묶는 것입니다
.
이것은 평가에 대해 이야기하고 악이 아닌 방법에 대해 이야기하는 좋은 기사 중 하나입니다 : http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
나는 당신이 밖으로 나가서 eval () 사용을 시작해야한다고 말하는 것이 아닙니다. 실제로 eval ()을 실행하기위한 좋은 사용 사례는 거의 없습니다. 코드 명확성, 디버깅 가능성 및 간과해서는 안되는 성능에 대한 우려가 있습니다. 그러나 eval ()이 의미가있는 경우이를 사용하는 것을 두려워해서는 안됩니다. 먼저 사용하지 말고 eval ()을 적절하게 사용할 때 코드가 더 취약하거나 안전하지 않다고 생각하지 않도록하십시오.
eval ()은 매우 강력하며 JS 문을 실행하거나 표현식을 평가하는 데 사용할 수 있습니다. 그러나 질문은 eval ()의 사용에 관한 것이 아니라 eval ()으로 실행하는 문자열이 악의적 인 당사자에 의해 어떻게 영향을 받는지 간단히 말해줍니다. 결국 악성 코드가 실행됩니다. 힘으로 큰 책임이 따른다. 따라서 현명하게 사용하십시오. 이것은 eval () 함수와 관련이 없지만이 기사에는 꽤 좋은 정보가 있습니다. http://blogs.popart.com/2009/07/javascript-injection-attacks/ eval ()의 기본을 찾고 있다면 여기를보십시오 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
JavaScript 엔진에는 컴파일 단계에서 수행하는 여러 가지 성능 최적화 기능이 있습니다. 이 중 일부는 코드가 실행될 때 기본적으로 코드를 정적으로 분석하고 모든 변수 및 함수 선언이있는 위치를 미리 결정하여 실행 중에 식별자를 확인하는 데 드는 노력을 덜하게됩니다.
그러나 엔진이 코드에서 eval (..)을 찾으면 본질적으로 식별자 위치에 대한 모든 인식이 유효하지 않은 것으로 가정해야합니다. 여기서 eval (..)에 전달할 코드를 정확히 알 수 없기 때문입니다. 어휘 범위 또는 수정하려는 새로운 어휘 범위를 만들기 위해 전달할 수있는 객체의 내용을 수정합니다.
다시 말해서 비관적 의미에서 eval (..)이 있으면 최적화하는 것이 무의미하므로 단순히 최적화를 전혀 수행하지 않습니다.
이것은 모든 것을 설명합니다.
참고 :
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
항상 나쁜 생각은 아닙니다. 코드 생성을 예로 들어 보겠습니다. 필자는 최근 virtual-dom 과 handlebars 의 격차를 해소하는 Hyperbars 라는 라이브러리를 작성했습니다 . 핸들 바 템플릿을 구문 분석하고 이후 가상 -dom에서 사용하는 하이퍼 스크립트 로 변환하여이를 수행합니다 . 하이퍼 스크립트는 먼저 문자열로 생성되며 반환하기 전에 실행 코드로 변환합니다. 나는 이 특정한 상황에서 악의 정반대를 발견 했다.eval()
eval()
기본적으로
<div>
{{#each names}}
<span>{{this}}</span>
{{/each}}
</div>
이에
(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({}))
eval()
생성 된 문자열을 한 번만 해석 한 다음 실행 가능한 출력을 여러 번 재사용해야하므로 이와 같은 상황에서는 성능이 문제가되지 않습니다.
당신은 당신이 궁금하면 코드 생성이 달성 방식을 볼 수 있습니다 여기에 .
eval()
브라우저에서 실행되는 자바 스크립트에서 사용하면 실제로 중요하지 않다고 말하면 됩니다. * (caveat)
모든 최신 브라우저에는 어쨌든 임의의 자바 스크립트를 실행할 수있는 개발자 콘솔이 있으며 모든 세미 스마트 개발자는 JS 소스를보고 개발자 콘솔에 필요한 비트를 넣어 원하는대로 할 수 있습니다.
* 서버 엔드 포인트가 사용자 제공 값의 올바른 유효성 검사 및 위생 처리를하는 한, 클라이언트 측 자바 스크립트에서 구문 분석되고 평가되는 내용은 중요하지 않습니다.
eval()
그러나 PHP 에서 사용하기 에 적합한 지 묻는다면 eval 문에 전달 될 수있는 값을 허용 하지 않는 한 대답은 NO 입니다.
나는 지금까지 언급 한 것을 반박하려고 시도하지는 않지만, 내가 알고있는 한 다른 방법으로는 할 수없는 eval () 사용을 제공 할 것입니다. 이것을 코딩하는 다른 방법과 아마도 그것을 최적화하는 방법이있을 수 있지만, 이것은 실제로 다른 대안이없는 eval의 사용을 설명하기 위해 명확하게하기 위해 종소리와 휘파람없이 오랫동안 수행되었습니다. 즉, 프로그래밍 방식으로 생성 된 동적으로 (보다 정확한) 객체 이름 (값이 아닌)입니다.
//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);
편집 : 그건 그렇고, 나는 (당신이 지적한 모든 보안상의 이유로) 사용자 입력에 객체 이름을 기반으로한다고 제안하지는 않습니다. 나는 당신이 그렇게하고 싶은 좋은 이유를 상상할 수 없습니다. 아직도, 나는 그것이 좋은 생각이 아니라고 지적 할 것이라고 생각했다 :)
namespaceToTest[namespaceParts[i]]
으로 eval을 수행 할 필요가 없습니다 if(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
.else namespaceToTest = namespaceToTest[namespaceParts[i]];
가비지 콜렉션
브라우저 가비지 콜렉션은 평가 된 코드를 메모리에서 제거 할 수 있는지 여부를 모릅니다. 따라서 페이지를 다시로드 할 때까지 코드를 저장 상태로 유지합니다. 사용자가 곧 페이지에 있으면 웹앱에 문제가 될 수 있습니다.
다음은 문제를 보여주는 스크립트입니다.
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
위의 코드만큼 간단한 것은 앱이 죽을 때까지 적은 양의 메모리가 저장되도록합니다. 회피 된 스크립트가 거대한 함수이고 간헐적으로 호출 될 때 이것은 더 나쁩니다.