Web,Mobile/Tech

eval()과 Function 생성자의 Scope

LimeLee 2024. 1. 12. 19:26

 

eval을 절대 사용하지 말 것!

 

eval() - JavaScript | MDN

**eval()**은 문자로 표현된 JavaScript 코드를 실행하는 함수입니다.

developer.mozilla.org

 
eval()은 인자로 받은 코드를 caller의 권한으로 수행하는 위험한 함수입니다. Function으로는 실현할 수 없는 공격이 가능합니다.
라고 작성되어있는데 정확히 이게 무슨 소린가 싶다. 그래서 좀 찾아봤다.


 
자바스크립트에는 스코프(Scope)라는게 있는데 전역 스코프, 블록 스코프, 함수 스코프가 존재한다. 

 

JavaScript Scope

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

  let const var
유효범위 Block Scope Block Scope Function Scope
값 재정의 O X O
재선언 X X O

 
출처: https://joooing.tistory.com/entry/Scope

function func() {
    var a = 1;
}

console.log(a);

예로 들면 var a는 func의 함수 스코프에서 유효하므로 func 함수 밖에서 a 변수에 접근할 수 없다.
 
Function 생성자나 eval 함수를 통해 생성된 함수에도 이런 스코프가 존재한다.
Function 생성자는 전역 스코프에서만 실행되는 함수를 만들게 된다.

 
반면 eval()는 caller 권한 즉, 함수 자신을 호출한 함수에 따라 전역 함수가 될 지 내부 함수가 될지 달라진다.
 
 
예시로 사용자의 입력 값이 func()이라는 함수 내 반영되고 eval 또는 Function 생성자를 통해 스크립트를 실행한다면 이런 차이가 발생하게 된다.

eval은 func의 a변수에 접근할 수 있지만 Function은 a변수에 접근할 수 없다.

 

eval("console.log(a)")의 Scope

 

Function("console.log(a)")()의 Scope

 
 


Function 생성자로 실현할 수 없는 특수한 상황이 뭔지는 알겠는데 
 
사용자의 입력 값이

  • 함수 내에 반영되고
  • 스크립트를 임의 실행이 가능하고
  • 함수 내 공격자 관점에서 유의미하게 활용할 수 있는 데이터가 존재하는 경우

같은 제한적인 상황이라서 Function 생성자에 비해 엄청나게 위험한 것 처럼 작성된 이유도 모르겠고 (eval을 절대 사용하지 않아야한다기보다 사용자 입력 값이 스크립트로 해석되지 않는 게 중요한게 아닌지) 자주 써먹을 수 있을지도 잘 모르겠다.

 


++

https://dreamhack.io/wargame/challenges/1578

 

JS is the best

JavaScript is the best programming language :)

dreamhack.io

Funtion 생성자로는 실현할 수 없고 유의미하게 활용될 수 있는 데이터를 조작 가능한 상황과 관련된 wargame
정확히 말하면 출제자의 인텐대로라면 Function 생성자로도 가능하지만 언인텐은 Function 생성자로는 불가능하다.

해당 문제를 풀면서 추가로 알게된 지식은

1. node.js 은 파일마다 모듈 스코프를 가지고 있음
node server.js 로 실행했더라도 server.js 안에서 정의된 변수들은 전역 스코프에 해당하지 않는다.
html에서 <script> 태그의 최상위에 정의하면 전역 스코프에 속했지만 node.js 는 그게 아닌가봄

2. eval을 간접 호출 시 전역 스코프에서 실행됨
eval을 직접 호출하면 자신을 호출한 함수의 스코프에서 실행되지만 z=eval; z(...) 와 같이 간접 호출로 할 시 전역 스코프에서 실행된다.

<script>
function y(s) {

  const c = 3;
  function x() {
     eval('console.log(`직접 호출 : ${c}`)');
     eval('z=eval;e="console.log(`간접 호출 : ${c}`)";z(e);');
  };

  x();
}
</script>



// eval('console.log(`직접 호출 : ${c}`)');
직접 호출 : 3
// eval('z=eval;e="console.log(`간접 호출 : ${c}`)";z(e);');
VM985:1 Uncaught ReferenceError: c is not defined
    at eval (eval at <anonymous> (eval at x (file:///***/test.html:7:4)), <anonymous>:1:24)
    at eval (<anonymous>)
    at eval (eval at x (file:///***/test.html:7:4), <anonymous>:1:40)
    at x (file:///***/test.html:8:4)
    at y (file:///***/test.html:11:3)
    at <anonymous>:1:1


그래서 wargame 문제의 소스코드를 직관적으로 봤을 때 조작하고 싶게끔 생긴 변수는 실제 조작이 어려울 것이다.

언인텐으로 풀고 나선 다른 변수를 조작할 수 있었으니 같은 Scope에 있는 그 변수 역시 조작이 가능한게 아닌가 쉽게 생각했지만 위의 2가지 지식을 알고 나서 왜 안되었는지 이해했음.