본문 바로가기

프로그래밍/JS&TS

안전?하게 외부 코드를 실행해보자!

반응형

stackoverflow 등의 웹사이트에서는 본문에 첨부된 코드를 실행시킬 수 있는 기능이 있다.
만약 이러한 기능을 필요로 할 때, 외부 코드를 안전하게 실행시킬 수 있는 방법들은 크게 다음과 같다.
 
1. JS 처리기를 JS 내부에서 구현...
2. 서버의 격리공간에서 실행 후 결과를 반환
3. 클라이언트의 격리공간에서 실행 후 결과를 반환
 
첫 번째 방법은 매우 고된 작업이고... 두 번째 방법은 서버에 부하가 클 수 있기에 주로 세 번째 방법을 사용한다.

// worker.js
const [global, permits] = [this, new Set([
	// variables
	"self",
	"global",
	"permits",
	// functions
	"eval",
	"console",
	"onmessage",
	"postMessage",
	// classes
	"Date",
	"Array",
	"Object",
	"Number",
	"String",
	"Boolean",
	"Function",
	"RegExp",
	// errors
	"Error",
	"URIError",
	"EvalError",
	"TypeError",
	"RangeError",
	"SyntaxError",
	"ReferenceError",
	// URI utils
	"decodeURI",
	"decodeURIComponent",
	"encodeURI",
	"encodeURIComponent",
	// type check
	"isNaN",
	"isArray",
	"isFinite",
	// type converstion
	"parseInt",
	"parseFloat",
	// libs
	"Math",
	"JSON",
	// values
	"NaN",
	"Infinity",
	"undefined",
])];

for (const property of Object.getOwnPropertyNames(global))
{
	if (!permits.has(property)) secure(global, property);
}

for (const property of Object.getOwnPropertyNames(global.__proto__))
{
	if (!permits.has(property)) secure(global.__proto__, property);
}

global.addEventListener("message", (event) =>
{
	for (const [key, value] of Object.entries(event.data.variables))
	{
		Object.defineProperty(global, key,
		{
			get()
			{
				return value;
			},
			configurable: true,
		});
	}
	try
	{
		postMessage(eval("\"use strict\";" + event.data.script)); // or new Function
	}
	catch (error)
	{
		postMessage(error);
	}
});

function secure(target, property)
{
	const descriptor = Object.getOwnPropertyDescriptor(target, property);

	if (!descriptor?.configurable ?? true) return;

	Object.defineProperty(target, property,
	{
		get()
		{
			throw new Error(`Security Eerror: cannot access ${property}`);
		},
		configurable: false,
	});
}

...

import workerJS from "file-loader?name=[name].js!@/prototypes/functions/execute/worker.js";

/** @see https://stackoverflow.com/questions/10653809/making-webworkers-a-safe-environment/10796616 */
export function execute(script: string, variables: Record<string, unknown> = {})
{
	return new Promise((resolve, reject) => {
		let timeout: NodeJS.Timeout;

		const worker = new Worker(workerJS);

		worker.addEventListener("message", (event) =>
		{
			// bye bye
			worker.terminate();
			// sayonara
			clearTimeout(timeout);

			if (event.data instanceof Error)
			{
				reject(event.data);
			}
			else
			{
				resolve(event.data);
			}
		});

		worker.postMessage({ script, variables });

		timeout = setTimeout(() => { worker.terminate(); return reject(); }, 1000 * 60);
	});
}

Object.defineProperty(window, "execute", { value: execute });

 
(JS를 이용한 보안 취약점 공격은 매우 다양하고 중학생때 짠 코드라서... 간단한 예시로만 봐주세요)

반응형

'프로그래밍 > JS&TS' 카테고리의 다른 글

불편한 WebWorker 통신  (0) 2024.09.10