web-dev-qa-db-ja.com

PHP __PHP_Incomplete_Classオブジェクトと$ _SESSIONデータ

ページの読み込み時に、ユーザーが送信したすべての文字列をSafeStringオブジェクトに変換するサイトのセットアップがあります。 SafeStringに不慣れな人のために、基本的にはユーザーにサニタイズされたデータをエコーアウトさせ、XSSなどを阻止します。

とにかく、問題があります。 $ _SESSION配列に__PHP_Incomplete_Class Objectが入力されています。私が読んだことから、これはセッションの前にクラスを初期化せず、クラスオブジェクトをセッションに保存しないためです。

私のコードは次のとおりです。

require_once __WEBROOT__ . '/includes/safestring.class.php'; 

$temp = array
(
   &$_SERVER, &$_GET, &$_POST, &$_COOKIE,
   &$_SESSION, &$_ENV, &$_REQUEST, &$_FILES,
   &$HTTP_SERVER_VARS, &$HTTP_GET_VARS,
   &$HTTP_POST_VARS, &$HTTP_COOKIE_VARS,
   &$HTTP_POST_FILES, &$HTTP_ENV_VARS
); 

function StringsToSafeString(&$array)
{
   foreach ($array as $key => $value)
   {
      if (is_string($array[$key]))
      {
         $array[$key] = new SafeString($value);
      } 

      if (is_array($array[$key]))
      {
         StringsToSafeString($array[$key]);
      }
   }
}

StringsToSafeString($temp);

unset($temp);

問題を解決するこれを書き換える方法を考えることはできません:/

何か案は?

38
dave

$_SESSIONにアクセスしているときは、現在のスクリプトのセッションから読み取ったデータのコピーを変更するだけでなく、SafeStringオブジェクトをアクティブなセッションに書き戻します。

しかし、セッションにカスタムオブジェクトを置くことは危険であり、私は一般的に避けようとするものです。それを行うには、session_startを呼び出す前に問題のクラスを定義しておく必要があります。そうしないと、PHPのセッションハンドラーはそのクラスのインスタンスを逆シリアル化する方法を認識できず、__PHP_Incomplete_Class Objectになります。

そのため、セッションをフロビングすることは避けてください。このアプローチを取る必要がある場合は、$_SESSIONからローカル$mysession配列にデータのコピーを作成します。ただし、SafeStringの考え全体は危険で実行不可能だと思います。このアプローチが水密になるとは思わない。未加工テキストの文字列が「安全」であるかどうかは、それがどこから来たのかとは関係なく、ターゲットコンテキスト用にエンコードする方法のプロパティです。

データベースやファイルなどの異なるソースから別のテキスト文字列を取得する場合、またはスクリプト自体で計算する場合、ユーザーからの文字列とまったく同じ処理が必要です。htmlspecialcharsである必要があります編とにかくそのエスケープを書く必要があります。セーフストリングは何も得ません。文字列を別の宛先形式に送信する必要がある場合は、別のエスケープが必要になります。

すべての文字列処理の問題を1つの便利なボックスにカプセル化することはできません。それは文字列の仕組みではありません。

62
bobince

これが尋ねられてから何年も経ちますが、上記の回答はどれも実際にOPに実際に間違っていることを説明していないため、回答を投稿しています。

PHPは、組み込みの serialize および unserialize メソッドを使用してセッションをシリアル化します。 serialize of PHPには、PHPオブジェクト(別名クラスインスタンス)をシリアル化し、文字列に変換する機能があります。これらの文字列を unserialize すると、それらの値を持つ同じクラスに変換されます。いくつかのプライベートプロパティがあり、それをエンコード/デコードするか、シリアライズ/デシリアライズで複雑な処理を行うクラスは、 Serializable クラスを実装し、 serialize および unserialize メソッドをクラスに追加します。

PHPの unserialize がクラスオブジェクトのシリアル化を解除しようとしたが、クラス名が宣言/要求されず、警告を出すか Exception 、それを__PHP_Incomplete_Classのオブジェクトに変換します。

セッションオブジェクトを__PHP_Incomplete_Classに変換したくない場合は、 session_start を呼び出す前にクラスファイルを要求するか、オートロード機能を登録します。

25
Steel Brain

_safestring.class.php_変数からSafeStringオブジェクトを読み取る場合は、session_start()を呼び出す前に_$_SESSION_を含める必要があります。

_<?php

require_once __WEBROOT__ . '/includes/safestring.class.php';    
session_start();

print_r($_SESSION);
_

ええ、もしPHPフレームワーク(おそらく)を内部的にsession_start()を呼び出すフレームワークを使用している場合は、事前にクラスファイル_require_once_フレームワークが提供するメカニズム)。

6
Lukman

ルクマンの答えは正しい。しかし、あなたはすでにあなたの質問でそれを言及しているので、どういうわけか、セッションが始まる前にクラスをインスタンス化することはできないようです。

Php configでセッションが自動的に開始するかどうかを確認できます。 http://www.php.net/manual/en/session.configuration.php#ini.session.auto-start

もしそれらが助けにならないなら、その前にクラスをオートロードできるかどうかチェックしたいかもしれません: http://php.net/manual/en/language.oop5.autoload.php =

他のすべてが失敗した場合でも、セッションに保存する前にオブジェクトをシリアル化して、オブジェクトを取得するたびにシリアル化を解除できます。 http://php.net/manual/en/function.serialize.php

私はあなたのコードに変数を保存する場所を見ませんが、それは次のようなものになります

$mystuff = unserialize($_SESSION["mystuff"]);
$mystuff->dostuff();
$_SESSION["mystuff"] = serialize($mystuff);

変数をシリアル化解除する前に、クラス定義を必ずロードしてください

$ 2c、*-パイク

3
commonpike

私はこのようなものを扱っただけです。私の注文がどのようにねじ込まれたかを最終的に見つけるために何時間もかかった。

非同期で呼び出されるファイルがありました。

myFile.php

そのファイルには次のものが含まれていました。

$result = include ("myOtherFile.php");
return $result;

Myotherfile.phpには次のようなものがあります

require_once "lib/myClassLibs.php";
require_once "webconfig.php";

webconfig.phpにはsession_start()呼び出しが含まれていました。

Lib/myClassLibsには、すべてのクラス情報の初期化があります。 webconfig呼び出しの前に確認すると、クラスが利用可能であることがわかります。

Webconfig呼び出しの前に確認すると、セッションが既に開始されていることもわかります。 lib/myClassLibs.phpの前にチェックすると、セッションがすでに開始されていることがわかります。

MyOtherFile.phpをインクルードする前にmyFile.phpをチェックすると、セッションが開始されていません。

これは、私が気にせずに過去8年間機能してきたレガシーコードを表しています。 「MyOtherFile.php」からインクルードを取り出しました。現在、私のセッションは適切に同期しています。

3
brad tittle

json_encodeおよびjson_decode関数を使用して問題を解決しました。

これは、セッションに値を割り当てたい場所です。

$user_json              = json_encode($user);
$_SESSION['user']           = $user_json;

これは、jsonをデコードした後にユーザーに表示する場所です

session_start();

$user_json= $_SESSION['user'];
$user = json_decode($user_json);

これで問題は解決しましたが、パフォーマンスやセキュリティについてはわかりません。私はそれらをチェックしていません。

2
Ariful Haque

Phpファイルの先頭に __ autoload 関数を含めることで、この問題を解決しました。そのため、次のようになります。

<?php 
require_once("path/to/include.inc");

//Needed for serialization/deserialization
function __autoload($class_name) {
    include "path/to/". $class_name . '.php';
}

PHP 5では、この関数は必要ありませんが、この関数を使用するまで行き詰まりました。

1
janoulle

これは非常に古い質問ですが、この問題に遭遇しました。さらに調査と実験を重ねた結果、セッションにクラスを保存するのに受け入れられる代替案だと思いました。それは少しハックかもしれませんが、私の現在のプロジェクトで動作します。

注:この回避策は、ユーザーがログインしたときにセッションを開始し、ユーザーがセッション中に遭遇する可能性のあるすべてのクラスを含めたくないため、私にとってはうまくいきます。すべてのクラスを含めることは実用的でも効率的でもないようです(しかし、これはこれ以上良いことではないかもしれません???)。

まず、基本クラスには、特定の配列からオブジェクト属性を自動的に取り込む次のコードが含まれています。

class BaseClass {

    public function __construct($properties=[]){
        if (!empty($properties)) {
            array_walk($properties, function ($val, $key) {
                $this->fromArray($key, $val);
            });
        }
    }

    public function fromArray($property, $value){
        return (property_exists($this, $property)) ? $this->$property = $value : null;
    }

    public function toArray(){
        return get_object_vars($this);
    }

}

回避策: toArray()メソッドを使用して、クラスインスタンスをセッションに移動する前に配列に変換し、セッションから取得するときにクラスの新しいインスタンスを作成します。

$_SESSION['user'] = $userInstance->toArray();

// ... do stuff ...

$userInstance = new User($_SESSION['user']);

これは、クラスをデータベースに書き込み、JSONに変換する場合にも非常に便利です。 PHP配列を使用すると、どちらも簡単になります。

上で言ったように、これはこの問題を処理する最も効率的な方法かもしれませんし、そうでないかもしれません。また、「配列に変換するだけなら、PHPクラスを使用する必要がありますか?」

1
CheddarMonkey

ここでの私の間違いは、session.auto_startonに設定します。セッションは、コード行(オートローダーを含む)が呼び出される前に初期化されます。

0
Tim

私は同じ問題に遭遇し、解決策は@bobinceの答えに触発されました

それを行うには、session_startを呼び出す前に問題のクラスを定義しておく必要があります

まず、私のセッションは次のように設定されました。

_$_SESSION["customer"] = $customerObj;
_

次に、session_start()を呼び出す前に、まずクラスをインポートしてクラスをロードまたは定義し、その後すぐにsession_start()を呼び出す必要があります

_require 'entity/Customer.php';

ob_start();
session_start();
$customer = new Customer();
if (isset($_SESSION["customer"]))
{
    $customer = $_SESSION["customer"];
    echo $customer->getCustomerName();
}
_
0
Storage Lenovo