eval() 과 new Function()
eval()
eval 함수는 String 형태의 소스 코드를 매개변수로, 해당 스크립트를 동적으로 실행시킬 수 있다. String 매개변수는 Javascript 파서에 의해 분석되고 실행된다.
하지만, 특별한 경우가 아닌 이상 eval 함수를 사용하지 않고도 동일한 로직을 구현할 수 있기도 하고 보안상의 이유로 사용하지 않는 것을 권장한다.
예시
1 2 | var property = "안녕!"; eval("alert(property)"); | cs |
이렇게만 보면 굳이 eval 함수를 쓰는 이유를 이해할 수 없을 것이다. 그렇다면 아래 예시를 한 번 보자!
예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | function notify_pickachu () { alert('피카츄'); } function notify_kkobugi () { alert('꼬부기'); } function notify_pairi () { alert('파이리'); } var randomNumber = Math.floor((Math.random() * 10) + 1); var functionName = "notify_"; switch (randomNumber % 3) { case 0 : functionName += "pickachu"; break; case 1 : functionName += "kkobugi"; break; case 2 : functionName += "pairi"; break; } eval(functionName + "();"); | cs |
난수를 발생시켜 나머지값에 따라 호출하는 함수명을 동적으로 변경해 eval 함수로 호출하고있다. 테스트하는 방법은 간단하다.
브라우저의 '개발자 도구'를 열어 콘솔에 위 코드를 붙여넣으면 끝!
그렇다면, 어떤 이유로 eval 함수가 보안에 취약하다고 하는 것일까? 위 코드를 보자. eval 함수에서 functionName 변수를 함수명으로써 동적으로 호출하고있다.
만약 eval 함수가 호출되기 전에 브레이크 포인트를 걸고 functionName 의 값을 바꾼다면? 원치않는 함수를 호출하거나 예외를 발생시킬 수 도 있다.
eval 함수로 실행되는 스크립트는 javascript 컴파일러가 미리 최적화할 수 없기 때문이다. 그리고 아래 코드를 예시로 들어보자.
1 | eval("var name = '피카츄';"); | cs |
이 때, name 변수는 eval 함수가 호출된 Scope에 남겨진다. 여기서 eval 함수가 어떤 함수 내부에서 실행된 것이 아니기 때문에 name는 전역변수가 된다.
그렇기 때문에 그 이후에 eval("var name = '꼬부기';"); 라는 스크립트가 실행될 경우 전역변수 name의 값이 꼬부기로 변경된다.
이렇게 eval 함수가 호출된 위치의 외부 유효범위에 접근, 수정이 가능해 값 변조가 가능한 것이다.
다만, 프레임워크 구현시에는 적절히, 유용히 쓰이므로 무조건 사용하지 않는 것이 정답은 아니다.
new Function()
eval 함수와 비슷하게, 문자열을 코드로 인식해 동적으로 수행한다.
다만, 함수 내부 Scope에만 접근과 수정이 가능해 내부에서 선언된 변수 등은 해당 Function 내에서만 유효해 유효범위 오염에 대한 문제도 없다.
위 예제를 new Function()으로 변경하면 아래처럼 사용하면 된다. 개발자 도구를 이용해 테스트해보길!
1 2 | var property = "안녕!"; (new Function("alert(property)"))(); | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | function notify_pickachu () { alert('피카츄'); } function notify_kkobugi () { alert('꼬부기'); } function notify_pairi () { alert('파이리'); } var randomNumber = Math.floor((Math.random() * 10) + 1); var functionName = "notify_"; switch (randomNumber % 3) { case 0 : functionName += "pickachu"; break; case 1 : functionName += "kkobugi"; break; case 2 : functionName += "pairi"; break; } (new Function(functionName + "();"))(); | cs |
비교하기
개발자 도구를 열어 아래 코드를 입력해보자. 1~3 라인에서 찍힌 로그는 모두 "undefined" 일 것이다.
그리고 세번의 동적 스크립트가 실행되고 14~16 라인에서 찍힌 로그는 좀 다를 것이다.
14 라인에서는 eval 함수의 유효범위 오염으로 String이, 15~16 라인에서는 "undefined" 이 찍힐 것이다.
예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | console.log(typeof blue); console.log(typeof white); console.log(typeof red); var script = "var blue = '파랑'; alert(blue);"; eval(script); script = "var white = '하양'; alert(white);"; new Function(script)(); script = "var red = '빨강'; alert(red);"; (function () { eval(script); })(); console.log(typeof blue); console.log(typeof white); console.log(typeof red); | cs |
자, 그럼 다른 측면에서 비교를 해보자. 성능면에서는 어떤 것이 더 나은 선택일까?
1 2 3 4 5 | var str = "", i = 0; for (; i<1000; i += 1) { str += "a"; } | cs |
간단한 for 문으로 테스트한 포스트가 있어 번역해보았다. 이 코드를 eval 함수를 이용해 동작시키려면 어떻게 해야할까?
1 2 | var strCode = 'var str="",i=0;for(;i<1000;i+=1){str+="a";}'; eval(strCode); | cs |
new Function()에서는 아래처럼 하면 될것이다.
1 2 | var strCode = 'var str="",i=0;for(;i<1000;i+=1){str+="a";}'; (new Function(strCode))(); | cs |
eval 함수 코드는 크롬 브라우저(21버전)에서 97 ops/sec 로 측정되었다고 한다. new Function() 코드는 동일 브라우저에서 5,256 ops/sec 로 측정되었다고 한다.
54.2배 개선된 성능이다. 아래는 위 성능 비교 포스트에서 첨부한 그래프이다.
이렇게 비교를 하면 new Function() 이 모든 것을 해결해주는듯 하다. 그런데 오히려 이런 유효범위의 오염이 필요로 한다면?
정답은 없다. 필요한 곳에 적절히 사용해야한다.
참고 사이트
JavaScript Performance Tips & Tricks (https://moduscreate.com/blog/javascript-performance-tips-tricks/)
Sam's Blog (http://blog.daum.net/sansamgu/33)
Web Club (http://webclub.tistory.com/512)
'☕️ JAVASCRIPT' 카테고리의 다른 글
HTML Entities 치환 라이브러리, html-entities (0) | 2022.07.19 |
---|---|
도로명주소 API 연동하기 (0) | 2019.10.05 |
자바스크립트에서 소수점 계산이 이상하다?? (0) | 2019.08.30 |
[TYPES & GRAMMAR] 챕터 2 : VALUES - Array (0) | 2019.08.22 |
[TYPES & GRAMMAR] 챕터 1 : TYPES (0) | 2019.02.06 |