スーパーグローバルを直接変更する


20

私は人々(一般に良いコードを書く人)$_POSTがこのようなコードで配列を直接変更するのを見ました:

// Add some value that wasn't actually posted
$_POST['last_activity'] = time();

// Alter an existing post value
$_POST['name'] = trim($_POST['name']);

// Our pretend function
// Pass the entire $_POST array as data to work with in the function
// The function update_record() will read only the values we actually need
update_record($_POST);

// ...That sure was easier than creating a new array 
//  with only the $_POST values we actually need.

update_record()$ _POSTに直接アクセスしてはいけないのは理にかなっているので、たとえば他のデータ配列をそれに渡すことができますが、これは怠zyで、デザインが悪いか、間違っているだけでしょうか?しかし、まだ有効な配列をに渡しているupdate_record()ので、なぜ新しい配列を作成するのですか?

これは問題のポイントではなく、単なる使用例です。しかし、多くの人がこれを$_REQUESTデータで行うべきではないと言うのを聞いたことがありますが、それは悪い習慣です。しかし、なぜ?無害に見えます。

例:

  • $_GET実際には存在しないデフォルト(または投稿)値を設定する

  • $_POSTフォーム送信後に実際に投稿されなかった値を追加する

  • スクリプトの非常に早い段階で$_GET配列の値またはキーを直接サニタイズまたはフィルタリングします(フォールバックサニテーション...どうして?)

  • $_POSTフォーム送信の前に手動で値を設定して、入力にデフォルト値を入力します(入力がデフォルト値を読み取るとき$_POST。これを実行しました)

  • 独自の$_SERVER価値を構成しますか?確かに、どうして?

  • どのように他の人について、同様$_COOKIE$_SESSION?もちろん、それらを直接修正する必要がありますか?それでは、なぜ他の人はいませんか?

スーパーグローバルの 直接的な変更は決して行われるべきではありません、それとも一部のインスタンスで実行して大丈夫ですか?


#1、#2、および#3に同意するのは、予期しない使用法であるためです(特に#1および#2)。
ケビンペノ

良い質問。グローバル配列の変更は、グローバル値を使用するのと同じように間違っています。また、これらの配列には目的(外部からパラメーターを渡す)があり、コード内で混乱させるための簡単な方法です。しかし、これらの配列の一部は、コード内で問題を引き起こさないために、スクリプトの開始時にサニタイズされる可能性があると考えています。
タデック

1
私は、OO入力配列ラッパー(暗黙的なフィルタリング)を使用しています。これは、$ _ GETまたは$ _POST変数が改ざんされたときに追加の通知を出力します。まだ可能ですが、狭い状況に限定する必要があります。(クロスモジュールシグナル、ディスパッチャ/フロントコントローラーのみが必要とするが。)
マリオ

@mario:私はあなたがこの質問を見てとることができるかどうかという達成方法についての詳細を聞いてみたい:stackoverflow.com/questions/12954656
ウェズリーマーチ

回答:


16

PHPがすでにこれらのスーパーグローバルを設定していることを考えると、それらを変更することは悪いことではないと思います。場合によっては、特に簡単に変更できないサードパーティのコードを扱う場合に、問題を解決するための最良の方法かもしれません。(それらは$_GET直接使用するか、キーがに存在すると仮定する$_SERVERなど)

ただし、一般的に言えば、独自のコードを作成するときは悪い習慣だと思います。$_REQUESTすべてのページで自動的に実行される背後のフィルターでデータを変更すると、副作用が発生する可能性があります。(「魔法の引用」が証明のために引き起こしたすべての問題を参照してください。)

そのため、スーパーグローバルを自動的にフィルター処理しない場合、次の利点はありません。

$_POST['foo'] = filter($_POST['foo']);

簡単にできるとき:

$foo = filter($_POST['foo']);

私はそれがサイト全体の区別をするためにはるかに明確だと思う$_POST$_GETされ、常に、信頼できないデータをフィルタリングされていない、と彼らはすべき決してそのまま使用しないこと。

フィルター処理された値を別の変数にコピーすることにより、「自分が何をしているのか理解しています...この入力をフィルター処理したので、安全に使用できる」という主張をしています。


入力のおかげで、私のインターネットはほぼ2日間公開されているので、誰にも返信する機会がありませんでした。この例では、$ _ POSTを変更し、それを更新関数に渡すデータの配列として使用しました。他のいくつかの$ _POSTキーがその関数で読み込まれることを前提としています。私は新しい配列を作成したいと思いますが、代わりにこれを行う人を見てきましたので、それを「悪いコード」と呼ぶかどうかはまだ少しわかりませんが、少なくともそのように傾いていると思います。これを行う必要があると感じるときはいつでも、常により良い方法があると思います。
ウェスリーマーチ

1
@Wesley、それが「悪い」主な理由は、ユーザーデータのサニタイズを忘れる可能性がはるかに高くなることです。この例では、1つのキーを変更し、配列全体を渡します。そのデータの一部に、処理されない悪意のある入力が含まれている場合はどうなりますか?その新しい配列を手作業で構築し、必要なものだけ$_POSTをそこにコピーし、作業中にサニタイズする方がはるかに優れています。そして、これを行う他の人々について...まあ、多くの人々は非常に悪いPHPコードを書いていますが、それはあなたにも言い訳にはなりません。:)
konforce

データを無害化するだけでなく、スーパーグローバル虐待の他のアプリケーションを強調すべきだったと思います。私はおそらくそれを完全に省き、より明確な質問を書くべきでした。人々は特にセキュリティの側面を取り上げ、残りの部分を無視することが特に好きです。恩知らずに聞こえないように、フィードバックを本当に感謝しています。あなたはいくつかの良い点に対処したが、質問が新しい3分間に投票パーティーを逃したので、私は1日待ってこれにチェックマークを付けます。
ウェズリーマーチ

9

一般に、事前定義されたスーパーグローバルを変更して、サニタイズされたデータと未加工/信頼されていないデータを明確にすることはお勧めしません。

他の人は、リクエストサイクルの開始時にスーパーグローバルをクリーンアップすれば、他の場所でそれらを心配する必要がないことを提案するかもしれません。

あなたがそれらを必要とするとき、私は常にそれらをマッチさせます:

$id = (int)$_POST['id'];

または類似。

他の変数の観点では、のいずれかに書き込みをしないようにするとよいでしょう$_GET$_POST$_REQUEST$_SERVERまたは$_COOKIE$_SESSIONただし、多くの場合、セッションにデータを書き込みたいと思うため、セッション内の異なるリクエストにまたがって永続化されるため、異なります。


2
これらの種類のグローバルが、必要なときにそれらを取得するために呼び出すことができるオブジェクト/メソッドに取って代わられるべきであるさらなる理由。なぜsetcookie存在しますが、私たちは経由でクッキーを取得し$_COOKIEますか?また、$_COOKIEは現在のセッションの開始時にのみ設定され、更新されることはないため、コードの以降の領域に最新の情報が含まれるように、両方の領域でCookieを変更/設定する必要があります。
ケビンペノ

ジェームズのおかげで、しばらくオフラインになっていたので、応答できませんでした。長い話を短くするために-私はあなたに同意します。post / get / etcに書き込むよりも常に良い解決策がありますが、「絶対にやらない」のように、それが厳密に悪い考えと見なされるかどうかはまだわかりません。したがって、このタイプのコードに再び出くわした場合、私は、ずさんなコードで「それらを呼び出す」権利を持っていると思いますか?
ウェスリーマーチ

@Wesley「これまでにこれを実行しない」場合、スーパーグローバルはおそらく厳密に読み取り専用になります-そうではありません。前述の理由から、アプリケーションコードでそれらを設定または上書きするのは悪い習慣だと思います。
ミシェルフェルドハイム14

3

避けるべきです。何かをサニタイズするのを忘れたのに、危険なデータを取得できるかもしれません。サニタイズ中にデータを新しい構造にコピーした場合

  • あなたが欲しいもの/必要なものだけを手に入れ、何も入ってい$_POSTない
  • 新しく作成された配列にいくつかのキーがないか、まったくない場合は、おそらくエラーが発生します

追加のその他のスクリプトは、配列が変更されておらず、好奇心が強いかもしれないと仮定するかもしれません。


2

スーパーグローバルを変更するという考えは、誤解を招くので好きではありませんでした。これは、何かを行うための簡単なハック方法であり、おそらくより良い方法があります。

$_POSTたとえば、の値を変更する場合、ソフトウェアは受信したデータを受信したと言っています。

本当の問題

これが大きな問題になる現実の状況があります。

あなたがチームで働いていると想像してください。理想的な世界では、誰もが同じ構文を使用しますが、理想的な世界には住んでいません。開発者の1人であるJohnは、を使用して投稿データにアクセスすることを好みます$_POST。彼はポスト変数で何かを変更します:

$_POST['ranking'] = 2; // John has changed ranking from 1 to 2 for whatever reason

次にfilter_input、ユーザーが改ざんできるデータを処理する際にソフトウェアを保護するために、入力(GET、POST、SERVER、COOKIE)データへのアクセスに使用する別の開発者Chrisがいます。ソフトウェアの彼の部分で、彼はの投稿値を取得する必要がありrankingます。コードの彼の部分があるAFTERジョンの。

$ranking = filter_input(INPUT_POST, 'ranking', FILTER_SANITIZE_NUMBER_INT);
// $ranking = 1

上記の例から、スーパーグローバルを変更することにより、PHPが壊れています。ジョンは$_POST['ranking']何らかの理由で値を2に設定しましたが、クリスは値1を受け取りました

私はそれを行う他の方法を見ていなかったとき:

AWSロードバランサーの背後にあるブログとしてwordpressを使用するプロジェクトに取り組みました。これにより、の値が変更されます$_SERVER['remote_address']。この場合、他の開発者は次のことを行うしかありませんでした。

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    $_SERVER['REMOTE_ADDR'] = $parts[0];
}

結論

ほぼ確実に、スーパーグローバルを変更するよりも良い方法があります


1

ここでの本当の質問は、「テーマを変更する理由」だと思います。そうする正当な理由は見当たりません。入力をサニタイズする必要がある場合は、ローカル変数を使用できます…

コードが十分に短い(たとえば、50行未満)場合を除き、これらのスーパーグローバルを変更すると、コードの保守と理解が難しくなります。

ちなみに、$ _ POSTを関数に渡す必要はありません。これは、関数のローカルスコープ内でもアクセスできるスーパーグローバル配列であるためです。


3
しかし、彼それ渡す必要があります。それ以外の場合、テストするのが非常に難しく、(evenい)ハッキングなしで他の値で関数/メソッドを呼び出すことはできません
-KingCrunch

まあ、それは彼の方法が何をするかにかかっています。$ _POST配列にあるものを解析するためだけに設計されている場合、彼はそれを渡す必要はありません。もちろん、それがあなたよりも一般的/抽象的な目的に役立つなら、あなたは正しいです。

2
@トーマス、私はここでキングに同意します。それらがグローバルであっても、密結合を引き起こすため、他のスコープ内でグローバルなものを使用しないでください(関数を再利用できない理由です)。あなたの例を考えると、関数がデータをサニタイズする場合、なぜ$_POSTデータのみをサニタイズするのですか?を渡す$_POSTと、関数はすべてのデータをサニタイズます。
ケビンペノ

0

スーパーグローバルを変更する理由はないはずであると最初にこの質問に答えた後、私が決めた時の例を使ってこの答えを編集しています。

現在、request列をユーザーに対応するtarget列にリダイレクトするURL書き換えデータベーステーブルで作業しています。

たとえば、がであるrequest可能性がblog/title-hereありtargetますblog.php?id=1

変数をblog.php期待しているので$_GET、変更したくないのでheader("Location:")、私はこのようなことをしなければなりません:

$uri    = explode('?', $uri_request)[0];
$params = explode('?', $uri_request)[1];
parse_str($params, $_GET);

これ$_GETにより、target列によって渡される目的のパラメーターを含む配列が作成されます。

最終的に、絶対に必要な場合を除き、スーパーグローバルを変更しないことを強くお勧めします

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.