9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptで比較的安全にeval()する

Last updated at Posted at 2021-07-21

evalって?

文字列をJavaScriptのコードとして評価してくれます。
一見便利だけど、めっちゃ危険だから通常使うことはありません。
使ったらESLintにも怒られます。

でも、外部からコードを注入できるのはとても拡張性が高いので、クローズドなBotとかの開発に使いたい場面はあります。

eval的なやつ

eval()が一番有名だと思うけど、実は他にも同じようなことができる方法があります。
具体的には、グローバルオブジェクトのFunction()と、Node.jsのVMです。

安全性としては、たぶんeval() <<< Function() << Node.js VMなんだけれど、VMはちょっと敷居が高そうだったので、この記事ではFunction()を使った方法について紹介します。

ちなみに、npmにsafe-evalっていうNode.js VMのラッパーがあったので、使ってみてもいいかも。

Function()の使い方

基本はeval()と同じです。

const greeting = Function('console.log("hello.")')
greeting() // hello.

けど、ローカルスコープに閉じ込められてるので、eval()と比べるとかなり安全。
実行するコードを渡すとこんな感じ。

const runCode = (code) => Function(code)()
const code = 'return new Date()'
console.log(runCode(code)) // 2021-07-21T16:41:19.389Z

ただし、ローカルスコープで評価されているので、グローバルに定義した関数はもちろん呼び出せない。
以下のコードはエラーになります。

const greeting = (name) => `hello, ${name}.`
const runCode = (code) => Function(code)()
const code = 'return greeting("michinosuke")'
console.log(runCode(code)) // greeting is not defined

じゃあどうするかというと、ちょっと複雑だけど、こんな感じにします。

const greeting = (name) => `hello, ${name}.`
const runCode = (code) => Function(`return (greeting) => {${code}}`)()(greeting)
const code = 'return greeting("michinosuke")'
console.log(runCode(code)) // hello, michinosuke.

awaitするコードを実行したいときは次のようにかきます。

const runCode = (code) => Function(`return async (fetch) => {${code}}`)()(fetch)

const runCodeAndPrint = async (code) => {
  console.log(await runCode(code))
}

runCodeAndPrint(`
const html = await fetch("https://example.com/").then(res => res.text())
return html.match(/<title>.+/)[0]
`) // <title>Example Domain</title>

非同期なFunction()

一応、非同期関数もコンストラクタで生成できます。

const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
AsyncFunction('return "async hello"')().then(str => console.log(str))

AsyncFunction

まとめ

紹介したFunction()は、ローカルスコープで評価されるとはいえ、悪意のある人物なら容易に悪用できるので、不特定多数に公開するサービスでの使用はやめた方がいいと思います。

9
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?