web-dev-qa-db-ja.com

特にeval(parse(...))の危険性は何ですか?

eval(parse(...))の使用を回避する方法についていくつかの質問があります

これが問題を引き起こします:

  • なぜ具体的にeval(parse())を避ける必要があるのですか?
  • そして最も重要なのは、何が危険か?
    • コードが本番環境で使用されない場合、危険はありますか? (私は、意図しない結果が返される危険性があると考えています。明らかに、何を解析しているか注意していないと、問題が発生します。しかし、get()
63
Ricardo Saporta

eval(parse(...))に対するほとんどの引数は発生しないセキュリティ上の懸念があるため、結局のところ、Rが安全なインターフェイスであるという主張はありません。インターネットに公開するのではなく、そのようなコードは一般的に、あまり目立たないメソッド、つまりより高速で人間が解析できるメソッドを使用して実行できることを行うためです。 R言語は高水準であることが想定されているため、コグノセンティ(および私はそのグループに所属しているとは考えていません)の好みは、コンパクトで表現力のあるコードを表示することです。

したがって、eval(parse(..))は知識の欠如を回避するためのバックドアメソッドであるという危険があり、その障壁を高める希望は、人々がR言語の使用を改善することです。扉は開いたままですが、他の機能のより表現力豊かな使用が望まれます。 本日以前のカールウィットフトの質問get関数が使用可能であることを知らないことを示し、そして 彼がリンクした質問 は_[[_関数の動作(および_$_が_[[_より制限されていた方法)。どちらの場合でもeval(parse(..))ソリューションを構築できますが、代替案よりも不格好で不明確でした。

41
42-

セキュリティの問題は、別のユーザーから渡された文字列に対してevalの呼び出しを開始した場合にのみ発生します。これは、Rをバックグラウンドで実行するアプリケーションを作成する場合には重要ですが、自分で実行するコードを記述しているデータ分析の場合は、evalがセキュリティに与える影響を心配する必要はありません。

ただし、_eval(parse(_に関するその他の問題がいくつかあります。

まず、通常、eval-parseを使用するコードは、解析されていないコードよりもデバッグがはるかに困難です。これは、ソフトウェアのデバッグが最初に書くのと同じように 2倍難しい であるため問題です。

ここに間違いのある関数があります。

_std <- function()
{
  mean(1to10)
}
_

ばかげた私は、コロン演算子のことを忘れて、私のベクトルを間違って作成しました。この関数を試してみた場合、Rは問題に気づき、エラーをスローして、間違いを指摘します。

これがeval-parseバージョンです。

_ep <- function()
{
  eval(parse(text = "mean(1to10)"))
}
_

エラーは有効な文字列内にあるため、これはソースになります。エラーがスローされるのは、後でコードを実行するときだけです。したがって、eval-parseを使用すると、ソース時のエラーチェック機能が失われます。

また、この2番目のバージョンの関数は、はるかに読みにくいと思います。

Eval-parseのもう1つの問題は、直接実行されるコードよりもはるかに遅いということです。比較する

_system.time(for(i in seq_len(1e4)) mean(1:10))
   user  system elapsed 
   0.08    0.00    0.07
_

そして

_system.time(for(i in seq_len(1e4)) eval(parse(text = "mean(1:10)")))
   user  system elapsed 
   1.54    0.14    1.69
_
34
Richie Cotton

通常、「文字列で計算する」には、コード文字列を使用するよりも優れた方法があります。 evalparseの重いコードでは、私の経験では、賢明な出力を保証するために多くの安全対策が必要です。

同じタスクは通常、Rオブジェクトを言語オブジェクトとして直接操作することで解決できます。 Hadley Wickhamには、Rでのメタプログラミングに関する役立つガイドがあります here

Gtoolsライブラリのdefmacro()関数は、evalparseコンストラクトの私のお気に入りの代用(意図されたハーフアサードRパン)です

require(gtools)

# both action_to_take & predicate will be subbed with code

F <- defmacro(predicate, action_to_take, expr = 
    if(predicate) action_to_take)

F(1 != 1, action_to_take = print('arithmetic doesnt work!'))

F(pi > 3, action_to_take = return('good!'))
[1] 'good!'

# the raw code for F
print(F)

function (predicate = stop("predicate not supplied"), action_to_take = stop("action_to_take not supplied")) 
{
    tmp <- substitute(if (predicate) action_to_take)
    eval(tmp, parent.frame())
}
<environment: 0x05ad5d3c> 

この方法の利点は、構文的に正当なRコードを確実に取り戻すことができるということです。この便利な関数の詳細については、 ここ を参照してください。

お役に立てば幸いです。

17
RyanGrannell

一部のプログラミング言語では、eval()は、文字列を式のように評価して結果を返す関数です。それ以外の場合は、evalを含む行の代わりに含まれているように、複数行のコードを実行します。 evalへの入力は必ずしも文字列である必要はありません。構文の抽象化をサポートする言語(LISPなど)では、evalの入力は抽象構文形式で構成されます。 http://en.wikipedia.org/wiki/Eval

Evalが不適切に使用された場合に利用できるあらゆる種類のエクスプロイトがあります。

攻撃者は、文字列「session.update(authenticated = True)」をデータとしてプログラムに提供する可能性があります。これにより、セッションディクショナリが更新され、認証済みのキーがTrueに設定されます。これを修正するには、evalで使用されるすべてのデータをエスケープするか、潜在的に有害な機能にアクセスせずに実行する必要があります。 http://en.wikipedia.org/wiki/Eval

つまり、eval()の最大の危険は、アプリケーションへのコードインジェクションの可能性です。一部の言語では、eval()を使用すると、使用目的によってはパフォーマンスの問題が発生する可能性があります。

特にRでは、get()の代わりにeval(parse())を使用できるため、eval()に頼らなくても結果は同じになります

8
ORION