web-dev-qa-db-ja.com

extract()の何がそんなに悪いのですか?

私は最近読んでいた このスレッド 、いくつかの最悪のPHPプラクティス。2番目の回答では、extract()の使用に関するミニディスカッションがあります。 、そして私はただすべてのハフが何であるか疑問に思っています。

個人的には$_GET$_POSTなどの特定の配列を切り取るために個人的に使用しており、変数の名前はわかりやすいので、後で変数をサニタイズします。

これは悪い習慣ですか?ここでのリスクは何ですか? extract()の使用についてどう思いますか?

35
barfoon

将来のメンテナ(または数週間後には自分自身)がどこから来ているのかわからない多くの変数につながる可能性があるという点で、それは悪い習慣にすぎないことがわかりました。このシナリオを検討してください。

extract($someArray); // could be $_POST or anything

/* snip a dozen or more lines */

echo $someVariable;

$someVariableはどこから来たのですか?誰がどのように言うことができますか?

変数が開始された配列内から変数にアクセスする際の問題は見当たらないので、実際に良いケースを提示する必要がありますforextract()を使用して考える価値がある。余分な文字を入力することに本当に関心がある場合は、次のようにしてください。

$a = $someLongNameOfTheVariableArrayIDidntWantToType;

$a['myVariable'];

ここでのセキュリティ面に関するコメントはやや誇張されていると思います。この関数は、既存の変数を上書きしない(EXTR_SKIP)、既存の変数のみを上書きする(ホワイトリストを作成できるようにする)(EXTR_IF_EXISTS)、プレフィックスの追加など、新しく作成された変数を実際にかなり適切に制御できる2番目のパラメーターを取ることができます。変数(EXTR_PREFIX_ALL)。

64
nickf

さあさあ。人々はユーザーではなくツールのせいにします。

これは、unlink()と話しているようなものです。これを使用すると、ファイルを削除できるからです。 extract()は他の関数と同じように、賢明かつ責任を持って使用してください。しかし、それ自体が悪いと主張しないでください、それは単に無知です。

39

リスクは次のとおりです。ユーザーからのデータを信頼しないでください。現在のシンボルテーブルに抽出すると、ユーザーが提供するものによって変数が上書きされる可能性があります。

<?php
    $systemCall = 'ls -lh';
    $i = 0;

    extract($_GET);

    system($systemCall);

    do {
        print_r($data[$i];
        $i++;
    } while ($i != 3);

?>

(無意味な例)

しかし、今やコード呼び出しを推測または知っている悪意のあるユーザー:

yourscript.php?i=10&systemCall=rm%20-rf

の代わりに

yourscript.php?data[]=a&data[]=b&data[]=c

これで、$ systemCallと$ iが上書きされ、スクリプトが最初にデータを削除してからハングします。

17
stefs

何も悪いことはありません。そうでなければ、それは実装されません。多くの(MVC)フレームワークは、変数をビューに渡す(割り当てる)ときにそれを使用します。慎重に使用する必要があります。これらの配列をextract()に渡す前にサニタイズし、変数をオーバーライドしないようにします。この関数はさらにいくつかの引数を受け入れることを忘れないでください! 2番目と3番目の引数を使用すると、衝突が発生した場合の動作を制御できます。プレフィックスを上書き、スキップ、または追加できます。 http://www.php.net/extract

8
Jamol

注意深く使用しないと、一緒に仕事をしている他の人と混同する可能性があります。

<?php

    $array = array('huh' => 'var_dump', 'whatThe' => 'It\'s tricky!', 'iDontGetIt' => 'This Extract Function');
    extract($array);
    $huh($whatThe, $iDontGetIt);


?>

収量:

string(12) "It's tricky!"
string(21) "This Extract Function"

難読化で使用すると便利です。しかし、「その変数はどこから来たのか」を乗り越えることはできません。私が遭遇する問題。

6
SeanDowney

多くの人がそれを使用することを推奨しない理由は、$_GET$_POST$_REQUESTでさえ)のスーパーグローバルを抽出すると、各キーと同じ名前でグローバル名前空間に変数が登録されるためだと思いますこれらの配列内では、基本的にREGISTER_GLOBALS = 1をエミュレートしています。

5
karim79

potentialが誤用されているため、人々は抽出物についてすべてを理解しています。 extract($ _ POST)のようなことをすることは、たとえあなたが何をしているのか知っていても、どんな場合でも良い考えではありません。ただし、ビューテンプレートなどに変数を公開するような場合に使用します。基本的に、そうする正当な理由があると確信している場合にのみ使用し、$ _POSTのようなクレイジーなものを渡すというアイデアが得られた場合は、extracttypeパラメーターの使用方法を理解してください。

5
stephenminded

関数で抽出する場合、変数はそのスコープでのみ使用できます。これは、ビューでよく使用されます。簡単な例:

//View.php
class View {
    function render($filename = null) {
        if ($filename !== null) {
            $this->filename = $filename;
        }
        unset($filename);
        extract($this->variables);
        ob_start();
        $this->returned = include($this->dir . $this->filename);
        return ob_get_clean();
    }
}

//test.php
$view = new View;
$view->filename = 'test.phtml';
$view->dir = './';
$view->variables = array('test' => 'tset');
echo $view->render('test.phtml');
var_dump($view->returned);

//test.phtml
<p><?php echo $test; ?></p>

いくつかの代替ディレクトリを使用して、ファイルが存在するかどうかを確認し、変数とメソッドを定義します。これで、Zend_Viewはほぼ複製されました。

インクルードの後に​​$ this-> outVariables = get_defined_vars();を追加して、特定のバリアベルでコードを実行し、古いphpコードで使用するためにこれらの結果を取得することもできます。

4
OIS

PHPマニュアル が私に代わって話させます。

背景:extract($_REQUEST)は、php.iniでregister_globals = Onを設定するのと同じです。

3
Powerlord

エキスは、安全に使用する限り安全です。実行したいのは、配列のキーを使用する予定のキーのみにフィルタリングし、シナリオでそれらのキーが必要な場合は、それらのキーがすべて存在することを確認することです。

#Extract only the specified keys.
$extract=array_intersect_key(
    get_data()
    ,$keys=array_flip(['key1','key2','key3','key4','key5'])
);

#Make sure all the keys exist.
if ($missing=array_keys(array_diff_key($keys,$extract))) {
    throw new Exception('Missing variables: '.implode(', ',$missing));
}

#Everything is good to go, you may proceed.
extract($extract);

または

#If you don't care to check that all keys exist, you could just do this.
extract(array_intersect_key(
    get_data()
    ,array_flip(['key1','key2','key3','key4','key5'])
));
2
Chinoto Vokro

グローバルスコープで抽出($ _GET)しないでください。それ以外にも、(潜在的に)多くのオプションの引数を持つことができる関数を呼び出すなどの用途があります。

これはWordPress開発者にはおなじみのはずです:

function widget (Array $args = NULL)
{
    extract($args);

    if($before_widget) echo $before_widget;

    // do the widget stuff

    if($after_widget) echo $after_widget;
}

widget(array(
    'before_widget' => '<div class="widget">',
    'after_widget' => '</div>'
));
2
James Socol

リスクはregister_globalsの場合と同じです。リクエストを改ざんするだけで、攻撃者がスクリプトに変数を設定できるようになります。

1
Tomalak

別のスレッドで誰かが指摘したように、 extractを使用するより安全な方法はここにあります 、配列に含まれるすべてのものではなく、指定した変数の抽出のみを許可することによって。

これは、どの変数が変数から出てくるかを文書化するという2つの目的を果たすため、変数を追跡することはそれほど難しくありません。

1
user10306

メソッドを使用するたびに、アプリケーションの障害点になる可能性のあるいくつかの状態が発生する可能性があります。個人的に、extract()はユーザー入力(予測不可能)やサニタイズされていないデータには使用しないでください。

CodeIgniterコアコードでさえextractを使用しているため、データがサニタイズされて適切に処理されていれば、このメソッドを使用しても害はありません。

EXTR_IF_EXISTSスイッチを使用してCodeIgniterモデルで抽出を使用し、変数の数を制限したところ、かなりうまく機能しました。

1
Dixita

以前の回答について少し説明します...入力を正しくフィルタリングする限り、extract()に問題はありません(他の人が述べているように)。そうしないと、次のような巨大なセキュリティ問題が発生する可能性があります。

<?php

// http://foobar.doo?isLoggedIn=1

$isLoggedIn = (new AdminLogin())->isLoggedIn(); // Let's assume this returns FALSE

extract($_GET);

if ($isLoggedIn) {
    echo "Okay, Houston, we've had a problem here.";
} else {
    echo "This is Houston. Say again, please.";
}
0
Tell

ユーザーデータ(リクエストの結果など)を操作している場合、extract()は安全ではないことに注意してください。したがって、この関数はフラグEXTR_IF_EXISTSおよびEXTR_PREFIX_ALLとともに使用することをお勧めします。

正しく使えば安全に使えます

0