JavaScriptμ—μ„œ eval()을 λŒ€μ²΄ν•  수 μžˆλŠ” 방법

JavaScriptμ—μ„œ eval()을 λŒ€μ²΄ν•  수 μžˆλŠ” 방법

D
dongAuthor
5 min read

JavaScript의 eval()이 μœ„ν—˜ν•œ μ΄μœ μ™€ new Function()으둜 μ•ˆμ „ν•˜κ²Œ λŒ€μ²΄ν•˜λŠ” 방법을 μ‹€μš©μ μΈ μ˜ˆμ œμ™€ ν•¨κ»˜ μ•Œμ•„λ³΄μ„Έμš”. λ³΄μ•ˆκ³Ό μ„±λŠ₯을 λͺ¨λ‘ μž‘λŠ” 팁!

JavaScriptλ₯Ό μ‚¬μš©ν•˜λ‹€ 보면 λ¬Έμžμ—΄λ‘œ 된 μ½”λ“œλ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•  λ•Œκ°€ μžˆμŠ΅λ‹ˆλ‹€. 이럴 λ•Œ κ°€μž₯ λ¨Όμ € λ– μ˜€λ₯΄λŠ” 것이 eval() ν•¨μˆ˜μ£ . ν•˜μ§€λ§Œ eval()은 λ³΄μ•ˆ 취약점과 μ„±λŠ₯ 문제둜 μ•…λͺ…이 λ†’μŠ΅λ‹ˆλ‹€.

이 κΈ€μ—μ„œλŠ” eval()의 μœ„ν—˜μ„±μ„ μ‚΄νŽ΄λ³΄κ³ , 더 μ•ˆμ „ν•œ λŒ€μ•ˆμΈ new Function()을 μ–΄λ–»κ²Œ ν™œμš©ν•  수 μžˆλŠ”μ§€ μ‹€μš©μ μΈ μ˜ˆμ œμ™€ ν•¨κ»˜ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

eval()μ΄λž€ 무엇인가?

MDN λ¬Έμ„œμ— λ”°λ₯΄λ©΄, eval()은 λ¬Έμžμ—΄λ‘œ ν‘œν˜„λœ JavaScript μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€. κ°„λ‹¨ν•œ 예제λ₯Ό 톡해 μž‘λ™ 방식을 μ‚΄νŽ΄λ³ΌκΉŒμš”?

let a = 3;
let b = 5;
eval('a += ' + b + ' + 2');
console.log(a); // 10

μœ„ μ½”λ“œμ—μ„œ eval()은 λ¬Έμžμ—΄ 'a += 5 + 2'λ₯Ό μ‹€μ œ JavaScript μ½”λ“œλ‘œ μ‹€ν–‰ν•©λ‹ˆλ‹€. 얼핏 보면 νŽΈλ¦¬ν•΄ λ³΄μ΄μ§€λ§Œ, 이 λ‹¨μˆœν•¨ λ’€μ—λŠ” μ‹¬κ°ν•œ λ¬Έμ œλ“€μ΄ μˆ¨μ–΄ μžˆμ–΄μš”.

eval()의 λ³΄μ•ˆ μœ„ν—˜μ„±

eval()은 caller의 κΆŒν•œμœΌλ‘œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€. 이게 무슨 μ˜λ―ΈμΌκΉŒμš”? λ§Œμ•½ μ‚¬μš©μž μž…λ ₯값을 eval()둜 μ‹€ν–‰ν•œλ‹€λ©΄, μ•…μ˜μ μΈ μ½”λ“œκ°€ κ·ΈλŒ€λ‘œ 싀행될 수 μžˆλ‹€λŠ” λœ»μž…λ‹ˆλ‹€.

var userContent = getUserInput(); // μ‚¬μš©μžλ‘œλΆ€ν„° μž…λ ₯받은 κ°’
eval(userContent); // μœ„ν—˜!

해컀가 "window.location = 'http://malicious-site.com'"와 같은 μ½”λ“œλ₯Ό μž…λ ₯ν•˜λ©΄ μ–΄λ–»κ²Œ λ κΉŒμš”? μ‚¬μš©μžλŠ” μ•…μ„± μ‚¬μ΄νŠΈλ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈλ˜κ±°λ‚˜, 더 μ‹¬κ°ν•œ 경우 λ―Όκ°ν•œ 데이터가 νƒˆμ·¨λ  수 μžˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ eval()은 호좜된 μŠ€μ½”ν”„(scope)에 직접 μ ‘κ·Όν•  수 μžˆμ–΄μš”. μ΄λŠ” 둜컬 λ³€μˆ˜λ₯Ό μ½κ±°λ‚˜ μˆ˜μ •ν•  수 μžˆλ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€:

function MyFunc() {
  let secretToken = "abc123";
  eval('console.log(secretToken); secretToken = "hacked";');
  console.log(secretToken); // "hacked"
}

eval()의 μ„±λŠ₯ 문제

eval()은 JavaScript 인터프리터λ₯Ό 직접 ν˜ΈμΆœν•˜κΈ° λ•Œλ¬Έμ— λ‹€λ₯Έ λŒ€μ•ˆλ“€λ³΄λ‹€ λŠλ¦½λ‹ˆλ‹€. ν˜„λŒ€ JavaScript 엔진듀은 μ½”λ“œλ₯Ό μ΅œμ ν™”ν•˜λŠ”λ°, eval()은 μ΄λŸ¬ν•œ μ΅œμ ν™”λ₯Ό λ°©ν•΄ν•©λ‹ˆλ‹€. 엔진이 μ–΄λ–€ μ½”λ“œκ°€ 싀행될지 미리 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ΄μ£ .

new Function()으둜 더 μ•ˆμ „ν•˜κ²Œ

new Function()은 eval()κ³Ό λΉ„μŠ·ν•˜κ²Œ λ¬Έμžμ—΄λ‘œλΆ€ν„° ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜μ§€λ§Œ, 훨씬 더 μ•ˆμ „ν•©λ‹ˆλ‹€. μ–΄λ–»κ²Œ μ‚¬μš©ν•˜λŠ”μ§€ μ‚΄νŽ΄λ³ΌκΉŒμš”?

const add = new Function('a', 'b', 'return a + b');
console.log(add(2, 3)); // 5

문법은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

let func = new Function([arg1, arg2, ...argN], functionBody);

μ—¬κΈ°μ„œ μ€‘μš”ν•œ 점은 new Function()으둜 μƒμ„±λœ ν•¨μˆ˜λŠ” 항상 μ „μ—­ μŠ€μ½”ν”„μ—μ„œ μ‹€ν–‰λœλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 둜컬 λ³€μˆ˜μ— μ ‘κ·Όν•  수 μ—†μ–΄μš”:

function MyFunc() {
  let b = 123;
  new Function('console.log(b);')(); // ReferenceError: b is not defined
}

이런 μ œμ•½μ΄ 였히렀 λ³΄μ•ˆμ„ κ°•ν™”ν•©λ‹ˆλ‹€. μ•…μ˜μ μΈ μ½”λ“œκ°€ ν•¨μˆ˜ λ‚΄λΆ€μ˜ λ―Όκ°ν•œ λ³€μˆ˜μ— μ ‘κ·Όν•˜λŠ” 것을 μ›μ²œμ μœΌλ‘œ μ°¨λ‹¨ν•˜λŠ” κ±°μ£ .

eval()κ³Ό new Function() λΉ„κ΅ν•˜κΈ°

두 λ°©μ‹μ˜ 핡심 차이점을 μ •λ¦¬ν•΄λ³Όκ²Œμš”.

μ‹€ν–‰ μ»¨ν…μŠ€νŠΈμ™€ μŠ€μ½”ν”„

eval()은 ν˜„μž¬ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈμ—μ„œ μ½”λ“œλ₯Ό ν‰κ°€ν•©λ‹ˆλ‹€:

function testEval() {
  let x = 10;
  eval('console.log(x); x = 20;');
  console.log(x); // 20 - λ³€μˆ˜κ°€ μˆ˜μ •λ¨
}
testEval();

반면 new Function()은 μ „μ—­ μ»¨ν…μŠ€νŠΈμ—μ„œλ§Œ μ‹€ν–‰λ©λ‹ˆλ‹€:

function testFunction() {
  let x = 10;
  const func = new Function('console.log(typeof x);'); // "undefined"
  func();
  console.log(x); // 10 - λ³€μˆ˜κ°€ μ•ˆμ „ν•˜κ²Œ 보호됨
}
testFunction();

λ³΄μ•ˆμ„±

new Function()의 μ œν•œλœ μŠ€μ½”ν”„ 접근은 λ³΄μ•ˆμƒ 큰 μž₯μ μž…λ‹ˆλ‹€. 둜컬 λ³€μˆ˜μ— μ ‘κ·Όν•  수 μ—†μœΌλ―€λ‘œ, μ•…μ˜μ μΈ μ½”λ“œκ°€ μ‹€ν–‰λ˜λ”λΌλ„ ν”Όν•΄ λ²”μœ„κ°€ μ œν•œμ μ΄μ—μš”.

μ‹€μ „ 예제둜 배우기

μ‹€μ œλ‘œ μ–΄λ–»κ²Œ eval()을 new Function()으둜 λŒ€μ²΄ν•  수 μžˆμ„κΉŒμš”?

κ°„λ‹¨ν•œ μˆ˜μ‹ 계산

// eval() μ‚¬μš© (ꢌμž₯ν•˜μ§€ μ•ŠμŒ)
const result1 = eval('2 + 3 * 4');

// new Function() μ‚¬μš© (ꢌμž₯)
const calculate = new Function('return 2 + 3 * 4');
const result2 = calculate();

동적 ν•¨μˆ˜ 생성

// μ‚¬μš©μžκ°€ μž…λ ₯ν•œ ν•¨μˆ˜ 본문으둜 ν•¨μˆ˜ 생성
const functionBody = 'return x * 2';
const double = new Function('x', functionBody);
console.log(double(5)); // 10

μ™ΈλΆ€ λ³€μˆ˜ μ•ˆμ „ν•˜κ²Œ μ „λ‹¬ν•˜κΈ°

μ „μ—­ λ³€μˆ˜λŠ” new Function()μ—μ„œλ„ μ ‘κ·Ό κ°€λŠ₯ν•˜μ§€λ§Œ, 더 μ•ˆμ „ν•œ 방법은 λ§€κ°œλ³€μˆ˜λ‘œ λͺ…μ‹œμ μœΌλ‘œ μ „λ‹¬ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€:

const multiplier = 3;

// λ§€κ°œλ³€μˆ˜λ‘œ λͺ…μ‹œμ  전달
const multiply = new Function('x', 'multiplier', 'return x * multiplier');
console.log(multiply(5, multiplier)); // 15

JSON νŒŒμ‹± λŒ€μ•ˆ

μ—„κ²©ν•œ JSON이 μ•„λ‹Œ JavaScript 객체 λ¦¬ν„°λŸ΄μ„ νŒŒμ‹±ν•  λ•Œλ„ ν™œμš©ν•  수 μžˆμ–΄μš”:

function looseJsonParse(obj) {
  return Function('"use strict";return (' + obj + ")")();
}

const result = looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}");
console.log(result.a); // 3

μ–Έμ œ new Function()을 μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

new Function()이 μœ μš©ν•œ μ‹œλ‚˜λ¦¬μ˜€λ“€μ„ μ‚΄νŽ΄λ³Όκ²Œμš”:

동적 μ½”λ“œ 생성

μ„€μ • νŒŒμΌμ΄λ‚˜ μ‚¬μš©μž μž…λ ₯을 기반으둜 ν•¨μˆ˜λ₯Ό λ™μ μœΌλ‘œ 생성해야 ν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ‚¬μš©μž μ •μ˜ ν•„ν„°λ‚˜ μ •λ ¬ λ‘œμ§μ„ κ΅¬ν˜„ν•  λ•Œμ£ .

μƒŒλ“œλ°•μŠ€ ν™˜κ²½

μ „μ—­ μŠ€μ½”ν”„μ—μ„œλ§Œ μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ—, ν”ŒλŸ¬κ·ΈμΈ μ‹œμŠ€ν…œμ΄λ‚˜ μ‚¬μš©μž 슀크립트 μ‹€ν–‰ ν™˜κ²½μ„ λ§Œλ“€ λ•Œ μ ν•©ν•©λ‹ˆλ‹€.

μ„±λŠ₯이 μ€‘μš”ν•œ 경우

반볡적으둜 μ‹€ν–‰λ˜λŠ” μ½”λ“œλΌλ©΄, ν•œ 번 new Function()으둜 μ»΄νŒŒμΌν•œ ν›„ μž¬μ‚¬μš©ν•˜λŠ” 것이 eval()을 맀번 ν˜ΈμΆœν•˜λŠ” 것보닀 νš¨μœ¨μ μž…λ‹ˆλ‹€.

μ£Όμ˜μ‚¬ν•­κ³Ό ν•œκ³„

new Function()도 μ™„λ²½ν•œ 해결책은 μ•„λ‹™λ‹ˆλ‹€. μ—¬μ „νžˆ λ¬Έμžμ—΄λ‘œλΆ€ν„° μ½”λ“œλ₯Ό μƒμ„±ν•˜λŠ” κ²ƒμ΄λ―€λ‘œ, μ‹ λ’°ν•  수 μ—†λŠ” μž…λ ₯은 μ² μ €νžˆ 검증해야 ν•΄μš”.

λ˜ν•œ 일뢀 μ—£μ§€ μΌ€μ΄μŠ€μ—μ„œλŠ” eval()μ΄λ‚˜ new Function() λͺ¨λ‘ μ ν•©ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. κ°€λŠ₯ν•˜λ‹€λ©΄ λ‹€μŒκ³Ό 같은 λŒ€μ•ˆμ„ λ¨Όμ € κ³ λ €ν•΄λ³΄μ„Έμš”:

객체 속성 μ ‘κ·Ό μ‹œ:

// eval() μ‚¬μš© (λ‚˜μ¨)
const propName = 'username';
const value = eval('user.' + propName);

// λΈŒλΌμΌ“ ν‘œκΈ°λ²• μ‚¬μš© (μ’‹μŒ)
const value = user[propName];

이벀트 ν•Έλ“€λŸ¬ 등둝 μ‹œ:

// 인라인 λ¬Έμžμ—΄ (λ‚˜μ¨)
element.setAttribute("onclick", "handleClick()");

// 이벀트 λ¦¬μŠ€λ„ˆ (μ’‹μŒ)
element.addEventListener("click", handleClick);

타이머 ν•¨μˆ˜ μ‚¬μš© μ‹œ:

// λ¬Έμžμ—΄ μ½”λ“œ (λ‚˜μ¨)
setTimeout("console.log('Hello')", 1000);

// ν•¨μˆ˜ 전달 (μ’‹μŒ)
setTimeout(() => console.log('Hello'), 1000);

더 μ•ˆμ „ν•œ μ½”λ“œλ₯Ό ν–₯ν•΄

eval()은 νŽΈλ¦¬ν•΄ λ³΄μ΄μ§€λ§Œ, λ³΄μ•ˆκ³Ό μ„±λŠ₯ μΈ‘λ©΄μ—μ„œ μ‹¬κ°ν•œ 문제λ₯Ό μ•ΌκΈ°ν•  수 μžˆμŠ΅λ‹ˆλ‹€. new Function()은 μ „μ—­ μŠ€μ½”ν”„ μ œν•œμ„ 톡해 μ΄λŸ¬ν•œ μœ„ν—˜μ„ 크게 μ€„μ—¬μ£ΌλŠ” 더 λ‚˜μ€ λŒ€μ•ˆμ΄μ—μš”.

λ¬Όλ‘  κ°€μž₯ 쒋은 방법은 동적 μ½”λ“œ μ‹€ν–‰ 자체λ₯Ό ν”Όν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. ν•˜μ§€λ§Œ λΆˆκ°€ν”Όν•œ 상황이라면, new Function()을 μ„ νƒν•˜λ˜ μž…λ ₯κ°’ 검증을 μ² μ €νžˆ ν•˜λŠ” 것을 μžŠμ§€ λ§ˆμ„Έμš”.

λ³΄μ•ˆμ€ νƒ€ν˜‘ν•  수 μ—†λŠ” μ˜μ—­μž…λ‹ˆλ‹€. μ½”λ“œ ν•œ 쀄이 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ•ˆμ „μ„ μ’Œμš°ν•  수 μžˆλ‹€λŠ” 점을 항상 κΈ°μ–΅ν•˜λ©΄μ„œ κ°œλ°œν•˜μ‹œκΈΈ λ°”λžλ‹ˆλ‹€ :)

JavaScriptμ—μ„œ eval()을 λŒ€μ²΄ν•  수 μžˆλŠ” 방법 | devdong