https://www.php-rocks.de/mitglieder/28-till.html
Registriert seit: 31.12.2015 | Themen: 49 | Beiträge: 194
10.01.2017, 01:27
Dieser Beitrag wurde zuletzt bearbeitet: 10.01.2017, 23:50 von Till.
Ich möchte API Ergebnisse mit var_export cachen.
Es gibt leider Fehler beim include, z.B. bei folgender Datei: https://github.com/frdl/webfan/blob/master/composer.json bzw. dem entsprechenden Suchergebnis ( nach var_exports(json_decode()) )
Der (JSON) token
\ wirdfälschlicherweise als escape von \ interpretiert (?) und verursacht den Fehler:
Fatal Error syntax error, unexpected T_STRING, expecting ')'
cache.php (nur noch ein \ wieder ein falsches Escape):
PHP-Code:
... 'psr-4' => (object)(array( 'frdl\' => '.ApplicationComposer/lib/frdl/', 'webfan\' => '.ApplicationComposer/lib/webfan/', 'webdof\' => '.ApplicationComposer/lib/webdof/', '_empty_\' => '.ApplicationComposer/lib/', )), ...
Folgendes habe ich bisher versucht:
PHP-Code:
$code = str_replace(array("stdClass::__set_state", 'o::__set_state', '\o::__set_state', 'O::__set_state', '\O::__set_state'), array("(object)", "(object)", "(object)", "(object)", "(object)"), var_export($value, true)); $code = str_replace("\' => '", "\\' => '", $code);
Klappt aber irgendwie nicht (mehr).
Und gefällt mir auch noch überhaupt nicht, da ich die gleiche Klasse auch für andere Daten verwende bei denen theorertisch das str_replace Fehler verursachen könnte.
EDIT:
Das json_decode funktioniert, der Fehler tritt nur auf wenn aus dem cache.
Ich habe das str_replace vom php code entfernt und es versucht mit
PHP-Code:
$body = str_replace('\\":"', '\\\":"', $body);
vor dem json_decode, leider ohne Erfolg.
https://www.php-rocks.de/mitglieder/2-arne-drews.html
Registriert seit: 18.03.2015 | Themen: 59 | Beiträge: 364
Bewertung:
14
PHP Selbsteinschätzung: Fortgeschrittene Kenntnisse
Hi,
Ich denke, Du benötigst einen Slash mehr:
PHP-Code:
$body = str_replace('\\":"', '\\\\":"', $body);
https://www.php-rocks.de/mitglieder/28-till.html
Registriert seit: 31.12.2015 | Themen: 49 | Beiträge: 194
Mh, damit kommen keine Ergebnisse bzw. sie werden getilgt:
packageinfo : null
https://www.php-rocks.de/mitglieder/16-mermshaus.html
Registriert seit: 20.03.2015 | Themen: 3 | Beiträge: 50
Bewertung:
6
PHP Selbsteinschätzung: Fortgeschrittene Kenntnisse
Ich steige nicht durch die Fragestellung bzw. kann das Verhalten nicht reproduzieren. Welche PHP-Version ist das?
Es gibt prinzipiell diesen Bug, dass stdClass kein __set_state implementiert:
- https://bugs.php.net/bug.php?id=48016
Wäre ein assoziatives Array (zweiter Parameter von json_decode) vielleicht erst mal die bessere Option?
https://www.php-rocks.de/mitglieder/28-till.html
Registriert seit: 31.12.2015 | Themen: 49 | Beiträge: 194
Hallo mermshaus,
Zitat:Es gibt prinzipiell diesen Bug, dass stdClass kein __set_state implementiert:
Dies versuche ich mit der Zeile zu beheben:
Zitat: $code = str_replace(array("stdClass::__set_state", 'o::__set_state', '\o::__set_state', 'O::__set_state', '\O::__set_state'), array("(object)", "(object)", "(object)", "(object)", "(object)"), var_export($value, true));
Ich glaube nicht das dies das Problem ist (?), var_exported wird:
PHP-Code:
'psr-4' => (object)(array( 'frdl\' => '.ApplicationComposer/lib/frdl/', 'webfan\' => '.ApplicationComposer/lib/webfan/', 'webdof\' => '.ApplicationComposer/lib/webdof/', '_empty_\' => '.ApplicationComposer/lib/', )),
richtig wäre m.e.
PHP-Code:
'psr-4' => (object)(array( 'frdl\\' => '.ApplicationComposer/lib/frdl/', 'webfan\\' => '.ApplicationComposer/lib/webfan/', 'webdof\\' => '.ApplicationComposer/lib/webdof/', '_empty_\\' => '.ApplicationComposer/lib/', )),
Zitat:Wäre ein assoziatives Array (zweiter Parameter von json_decode) vielleicht erst mal die bessere Option?
Daran hab ich auch schon gedacht, ich hätte zwar psr-4 lieber "original" als Object, werde das aber später oder morgen mal probieren (im Moment bin ich zu müde).
Zitat: Welche PHP-Version ist das?
5.3.3
Zitat:Ich steige nicht durch die Fragestellung bzw. kann das Verhalten nicht reproduzieren.
Ok, hier nochmal mein kompletter relevanter code, in der ersten box die methoden der API (requerst) in er zweiten box die cache_class
PHP-Code:
protected function url($base, $uri){ return $base.$uri; }
protected function _search_packagist($query, $willCacheThisFor = '600 seconds', &$error = null, array $search = array()) { $http_opts = array('method'=>'GET','X-frdl-proxy-will-cache-for'=>$willCacheThisFor); $result = array();
$search['q'] = $query; $url = $this->url('https://packagist.org', '/search.json?' . http_build_query($search));
$r = $this->request($url, $http_opts, $body, $error); if(true !== $r)return false; try{ $r = json_decode($body); $result = $r->results; $last = $url; if(isset($r->next)) { do { $_opts = array_merge($http_opts, array('referer'=>$last)); usleep(5000); $_r = $this->request($r->next, $_opts, $body, $error); $last = $r->next; if(true !== $_r)return $result; $r = json_decode($body); $result = array_merge($result, $r->results); } while (isset($r->next)); }
}catch(\Exception $e){ if(MX_IS_ADMIN)trigger_error($e->getMessage(), E_USER_ERROR); return false; } return $result; } protected function _search_phpclasses($query, $willCacheThisFor = '600 seconds', &$error = null){ $http_opts = array('method'=>'GET','X-frdl-proxy-will-cache-for'=>$willCacheThisFor); $result = array(); $this->_get_phpclasses_packages_json($body, $error, $willCacheThisFor);
if('' !== $error)return false; try{ $r = json_decode($body); foreach($r->packages as $package => $p){ $_c = explode('/', $package); $found = false; if(false===strpos($query, '*') && false===strpos($query, '/'))if(preg_match("/".preg_quote($query)."/", $_c[1])) $found = true; if(false!==strpos($query, '/')){ $qs = explode('/', $query); if('*'!==$qs[0])if($qs[0]===$_c[1] || preg_match("/".preg_quote($qs[0])."/", $_c[1])) $found = true; if($qs[1]===$_c[1] || preg_match("/".preg_quote($qs[1])."/", $_c[1])) $found = true; } if(true!==$found)continue; foreach($p as $_v => $v){ $result = array_merge($result, array($v)); }
} }catch(\Exception $e){ if(MX_IS_ADMIN)trigger_error($e->getMessage(), E_USER_ERROR); return false; } return $result; }
protected function request($url, $options=array(), &$body = null, &$error = null) { $def_opts = array( 'method'=>'GET', 'user_agent'=>'Webfan.de API', 'referer'=>'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'], 'debug'=>0, 'html_debug'=>0, 'follow_redirect'=>1, 'X-frdl-proxy-will-cache-for'=>'600 seconds' ); $opts = array_merge($def_opts, $options); $cache_prefix = "proxy GET 29"; $cachekey = $url; $nlimit = 30 * 60; $cache = new \class_cache(); $body = $cache -> load($cachekey, $cache_prefix);
if( $opts['method'] !=='GET' || true !== $cache->use_cache()){ $classname = get_class($this->http_client); $http = new $classname; $http->debug=$opts['debug']; $http->html_debug=$opts['html_debug']; $http->follow_redirect=$opts['follow_redirect']; // $http->user_agent = $_SERVER['HTTP_USE_AGENT']; $http->user_agent =$opts['user_agent']; // $arguments['Headers']['Referer']= $_SERVER['HTTP_REFERER']; $arguments['Headers']['Referer']= $opts['referer']; $error=$http->GetRequestArguments($url,$arguments);
if($error)return false;
$error=$http->Open($arguments); if($error)return false;
$arguments['Headers']['X-Forwarded-For']= $_SERVER['REMOTE_ADDR']; $arguments['Headers']['X-frdl-proxy']= 'web+fan://interface.api.webfan.de/v1/public/software#proxy:'.$url; $arguments['Headers']['X-frdl-proxy-will-cache-for']= $opts['X-frdl-proxy-will-cache-for']; $arguments['Headers']['X-frdl-github-app-page']= 'http://frdl.github.io/webfan/'; $arguments['RequestMethod']=$opts['method']; $error=$http->SendRequest($arguments); if($error)return false; $error=$http->ReadReplyHeaders($headers); if($error)return false; $error = $http->ReadWholeReplyBody($body); if($error)return false; // $body = str_replace('\\":"', '\\\\":"', $body); $cache->save($cachekey, $body, $nlimit, $cache_prefix); }
return true; } public function func_php_search(){ $this->api->data = false; $query = ''; if(0<count($this->api->args)){ $args = $this->api->args; // array_shift($args); $query = join('/', $args); }else{ if(''===$query && isset($this->api->request['q'])){ $query = $this->api->request['q']; }elseif(''===$query && isset($this->api->request['query'])){ $query = $this->api->request['query']; }elseif(''===$query && isset($this->api->request['search'])){ $query = $this->api->request['search']; }elseif(''===$query && isset($this->api->request['package'])){ $query = $this->api->request['package']; } } if(false===strpos($query, '/')){ $query='*/'.$query; }
$cache_prefix = "proxy-php_search-9"; $cachekey = $query; $nlimit = 3 * 60; $cache = new \class_cache();
try{ $result = $cache -> load($cachekey, $cache_prefix); }catch(\Exception $e){ }
if( !is_array($result) || true !== $cache->use_cache()){ $result = array(); $repository = 'packagist'; $website = 'http://packagist.org'; $SUB_cache_prefix = "web+fan://api.webfan.de.proxy#php_search@1.29/".$repository; $SUB_cachekey = $query.'?website='.$website; $SUB_nlimit = 30 * 60; $SUB_cache = new \class_cache(); $r =$SUB_cache -> load($SUB_cachekey, $SUB_cache_prefix); if( true !== $SUB_cache->use_cache()){ $r = new \stdclass; $r->repository =$repository; $r->website = $website; $r->results = $this->_search_packagist($query, $nlimit.' seconds', $error); if($error) $r->error = $error; $SUB_cache->save($SUB_cachekey, $r, $SUB_nlimit, $SUB_cache_prefix); } $result[]=$r;
$repository = 'phpclasses'; $website = 'http://phpclasses.org'; $SUB_cache_prefix = "web+fan://api.webfan.de.proxy#php_search@1.29/".$repository; $SUB_cachekey = $query.'?website='.$website; $SUB_nlimit = 30 * 60; $SUB_cache = new \class_cache(); $r =$SUB_cache -> load($SUB_cachekey, $SUB_cache_prefix); if( true !== $SUB_cache->use_cache()){ $r = new \stdclass; $r->repository =$repository; $r->website = $website; $r->results = $this->_search_phpclasses($query, $nlimit.' seconds', $error); if($error) $r->error = $error; $SUB_cache->save($SUB_cachekey, $r, $SUB_nlimit, $SUB_cache_prefix); } $result[]=$r; $cache->save($cachekey, $result, $nlimit, $cache_prefix); }
if(false===$result){ if(''===$query) return $this->api->_response(self::E_404, 404); return $this->api->_response(self::E_404, 200); }else{ $this->api->data = $result; }
return $this->api->_response(self::E_OK, 200); }
public function _package_packagist($vendor, $packagename, $willCacheThisFor = '600 seconds', &$error = null){ $result = false; $url = $this->url('https://packagist.org', '/packages/'.$vendor.'/'.$packagename.'.json'); $http_opts = array('method'=>'GET','X-frdl-proxy-will-cache-for'=>$willCacheThisFor); $this->request($url, $http_opts, $body, $error); if($error)return false; try{ $r = json_decode($body); $result = $r; if(isset($result->package->versions))$result->package->versions=(array)$result->package->versions; }catch(\Exception $e){ if(MX_IS_ADMIN)trigger_error($e->getMessage(), E_USER_ERROR); return false; } return $result; }
protected function _package_phpclasses($vendor, $packagename, $willCacheThisFor = '600 seconds', &$error = null){ $result = false;
$this->_get_phpclasses_packages_json($body, $error, $willCacheThisFor); if($error)return false; try{ $r = json_decode($body); foreach($r->packages as $package => $p){ $_c = explode('/', $package); if(2!== count($_c) || $_c[0] !== $vendor || $_c[1] !== $packagename)continue; foreach($p as $_v => $v){ $package = new \stdclass; $package->package = new \stdclass; $package->package->name = $vendor.'/'.$packagename; $package->package->versions = array($_v => $v); $result = $package; } break; } }catch(\Exception $e){ if(MX_IS_ADMIN)trigger_error($e->getMessage(), E_USER_ERROR); return false; } return $result; } protected function _get_phpclasses_packages_json(&$body, &$error, $willCacheThisFor = '600 seconds'){ $SUB_cache_prefix = "web+fan://api.webfan.de.proxy#get_phpclasses_packages_json"; $SUB_cachekey = 'v9'; $SUB_nlimit = 30 * 60; $SUB_cache = new \class_cache(); $r =$SUB_cache -> load($SUB_cachekey, $SUB_cache_prefix); if(true !== $SUB_cache->use_cache()){ $url = $this->url('http://www.phpclasses.org', '/packages.json'); $http_opts = array('method'=>'GET','X-frdl-proxy-will-cache-for'=>$SUB_nlimit. ' seconds or '.$willCacheThisFor); $_r = $this->request($url, $http_opts, $_body, $_error); $r = array( 'body' => $_body, 'error'=>$_error ); $SUB_cache->save($SUB_cachekey, $r, $SUB_nlimit, $SUB_cache_prefix); }
$body=$r['body']; $error=$r['error']; } public function func_php_package(){ $this->api->data = false; $query = ''; if(0<count($this->api->args)){ $args = $this->api->args; // array_shift($args); $query = join('/', $args); }else{ if(''===$query && isset($this->api->request['package'])){ $query = $this->api->request['package']; } } $req = explode('/', $query, 2); if(2!==count($req)){ return $this->api->_response('uri must contain vendor/package', 400); }
$vendor=$req[0]; $packagename=$req[1];
$cache_prefix = "api.webfan.de.proxy-php_package@29"; $cachekey = '--vendor="'.$vendor.'" --packagename="'.$packagename.'" --softgroup="php" '; $nlimit = 3 * 60; $cache = new \class_cache(); try{ $result = $cache -> load($cachekey, $cache_prefix); }catch(\Exception $e){ }
if(!is_array($result) || true !== $cache->use_cache()){ $result = array(); $repository = 'packagist'; $website = 'http://packagist.org'; $SUB_cache_prefix = $cache_prefix."/".$repository; $SUB_cachekey = $cachekey.' --repository="'.$website.'" 19'; $SUB_nlimit = 30 * 60; $SUB_cache = new \class_cache(); $r =$SUB_cache -> load($SUB_cachekey, $SUB_cache_prefix); if( true !== $SUB_cache->use_cache()){ $r = new \stdclass; $r->repository =$repository; $r->website = $website; $r->packageinfo = $this->_package_packagist($vendor, $packagename, $SUB_nlimit.' seconds', $error); if($error) $r->error = $error; $SUB_cache->save($SUB_cachekey, $r, $SUB_nlimit, $SUB_cache_prefix); } $result[]=$r;
$repository = 'phpclasses'; $website = 'http://phpclasses.org'; $SUB_cache_prefix = $cache_prefix."/".$repository; $SUB_cachekey = $cachekey.' --repository="'.$website.'" 19'; $SUB_nlimit = 30 * 60; $SUB_cache = new \class_cache(); $r =$SUB_cache -> load($SUB_cachekey, $SUB_cache_prefix); if(true !== $SUB_cache->use_cache()){ $r = new \stdclass; $r->repository =$repository; $r->website = $website; $r->packageinfo = $this->_package_phpclasses($vendor, $packagename, $SUB_nlimit.' seconds', $error); if($error) $r->error = $error; $SUB_cache->save($SUB_cachekey, $r, $SUB_nlimit, $SUB_cache_prefix); } $result[]=$r; $cache->save($cachekey, $result, $nlimit, $cache_prefix); }
if(false===$result){ if(''===implode('', $req)) return $this->api->_response(self::E_404, 404); return $this->api->_response(self::E_404, 200); }else{ $this->api->data = $result; }
return $this->api->_response(self::E_OK, 200); }
PHP-Code:
<?PHP /** * @autor Piotr Miloszewicz * @www FiveGroup * @name Cache variables */
//define cache path if(!defined('CACHE_PATH'))define( 'CACHE_PATH', dirname(__FILE__).'/../../../cache-public-hour/cache-hour-not-public/');
class class_cache {
private $FILENAME_PREFIX = 'CACHE.H.';
public $check_cache = false;
/** * save data to cache * * @param: mixed $name * @param: mixed $value * @param: (int) $timeout */
public function name($prefix_extra = null, $name){ if($prefix_extra === null)$prefix_extra = substr($name, 0, 16); $prefix_extra = preg_replace("/[^A-Za-z0-9]/","-", $prefix_extra); $name = $this->FILENAME_PREFIX.$prefix_extra.'.'.strlen($prefix_extra).'.'.strlen($name).'.'.sha1($name); return $name; }
public function save( $name, $value, $timeout = 0, $prefix_extra = null) {
$name = $this->name($prefix_extra, $name);
//start cache file $cache_file = "<?PHP\n\n";
//set description to cache file $cache_file .= "/*\n CACHE FILE NAME: ". $name ."\n GENERATE TIME: ". date('c') ."\n*/\n\n";
//set data to cache file $timeout = $timeout + time(); $cache_file .= "//cache timeout\n" . '$timeout = '. $timeout . ";\n\n";
//set variable to cache $code = str_replace(array("stdClass::__set_state", 'o::__set_state', '\o::__set_state', 'O::__set_state', '\O::__set_state'), array("(object)", "(object)", "(object)", "(object)", "(object)"), var_export($value, true)); // $code = str_replace("\' => '", "\\' => '", $code); $cache_file .= "//cache content\n" . '$cache_variable = '. $code .';';
//end cache $cache_file .= "\n\n?>";
//return cache to save $r = file_put_contents( CACHE_PATH .'/'. $name .'.php', $cache_file); chmod(CACHE_PATH .'/'. $name .'.php', 0775); return $r; }
/** * load cache * * @return: mixed */
public function load( $name, $prefix_extra = null, &$time = null, &$tempfilename = null) {
$name = $this->name($prefix_extra, $name); $tempfilename = CACHE_PATH .'/'. $name .'.php'; //chech was cache exists if( file_exists($tempfilename)) {
//load cache include $tempfilename;
$time = $timeout; //chech was cache no expired if($timeout > time()) {
$this->check_cache = true; }else{ $this->check_cache = false; }
// return cache return $cache_variable;
} else {
// return error //return "<b>Error</b>\n\n Cache file does not exists."; $this->check_cache = false; } }
/** * check value of check_cache field * */
public function use_cache() { //return $this->check_cache; if(true === $this->check_cache)return true; return false; }
/** * delete cache * * @param: mixed $name; */
public function flush( $name, $prefix_extra = null) {
$name = $this->name($prefix_extra, $name);
if( file_exists( CACHE_PATH .'/'. $name .'.php')) { chmod(CACHE_PATH .'/'. $name .'.php', 0775); unlink( CACHE_PATH .'/'. $name .'.php'); } } }
https://www.php-rocks.de/mitglieder/16-mermshaus.html
Registriert seit: 20.03.2015 | Themen: 3 | Beiträge: 50
Bewertung:
6
PHP Selbsteinschätzung: Fortgeschrittene Kenntnisse
11.01.2017, 11:31
Dieser Beitrag wurde zuletzt bearbeitet: 11.01.2017, 12:17 von mermshaus.
Zitat:5.3.3
- https://3v4l.org/ENgTk
Zitat:$code = str_replace(array("stdClass::__set_state"…
Wobei du dir damit eben eine potenzielle (wenn auch sicherlich unwahrscheinliche) Fehlerquelle einhandelst, indem du die genauen Syntaxregeln von PHP nicht beachtest. So was sind primäre Ziele, über die dann am Ende Software angegriffen und gehackt wird. Du erzeugst ja ausführbaren PHP-Code.
Ich wäre extrem vorsichtig und zurückhaltend mit so was.
Zitat:Ok, hier nochmal mein kompletter relevanter code
Ist nett, aber ~570 Zeilen, die in sich nicht mal lauffähig sind, sind etwas too much als erklärendes Beispiel. Aber es liegt ja an deiner sehr alten PHP-Version.
https://www.php-rocks.de/mitglieder/28-till.html
Registriert seit: 31.12.2015 | Themen: 49 | Beiträge: 194
Zitat:Du erzeugst ja ausführbaren PHP-Code.
Ja, ich dachte das wäre der Zweck von var_export?
Zitat:Ich wäre extrem vorsichtig und zurückhaltend mit so was.
Ok, ich werde über Deine Anregung nachdenken
Die Idee dahinter war, das ausführbarer code schneller wieder zu laden ist, als wenn er erst de-serialisiert werden müßte.
Zitat: Aber es liegt ja an deiner sehr alten PHP-Version.
Ok, schade. Dann muß ich das "Problem" erstmal hinten anstellen.
Vielen Dank für Eure Hilfe!
https://3v4l.org/ - kannte ich noch nicht, nice!
https://www.php-rocks.de/mitglieder/28-till.html
Registriert seit: 31.12.2015 | Themen: 49 | Beiträge: 194
Zitat:Ist nett, aber ~570 Zeilen, die in sich nicht mal lauffähig sind, sind etwas too much als erklärendes Beispiel.
Anmerkung am Rande, falls es jamand braucht/interessiert:
Der zugrunde liegende Code ist auch hier verfügbar: https://github.com/frdl/package-fetcher/
Die Frage ansich ist beantwortet, danke nochmal!
https://www.php-rocks.de/mitglieder/16-mermshaus.html
Registriert seit: 20.03.2015 | Themen: 3 | Beiträge: 50
Bewertung:
6
PHP Selbsteinschätzung: Fortgeschrittene Kenntnisse
13.01.2017, 12:05
Dieser Beitrag wurde zuletzt bearbeitet: 13.01.2017, 13:00 von mermshaus.
Von 5.3.3 kommst du wirklich nicht irgendwie weg? Du musst ja theoretisch nur bis 5.3.7 upgraden. (Mehr wäre natürlich besser, aber da muss man dann schon eher mal schauen, was Kompatibilität angeht.)
Zitat:Die Idee dahinter war, das ausführbarer code schneller wieder zu laden ist, als wenn er erst de-serialisiert werden müßte.
Ja, das kann grundsätzlich sicher ein Argument sein. (Müsste man im Zweifel irgendwie messen, ob und wie viel es ausmacht.)
Mir ging es konkret um deine Ersetzungen von PHP-Syntax als simplen Strings.
Also das hier:
PHP-Code:
$code = str_replace(array("stdClass::__set_state", 'o::__set_state', '\o::__set_state', 'O::__set_state', '\O::__set_state'), array("(object)", "(object)", "(object)", "(object)", "(object)"), var_export($value, true)); $code = str_replace("\' => '", "\\' => '", $code);
Bei der ersten Zeile habe ich jetzt spontan keine Idee, wie man diese Ersetzungen mit Schadabsicht ausnutzen könnte, aber wenn du in einem String in den Eingabedaten "stdClass::__set_state" stehen hättest, würde das auch dort fälschlicherweise durch "(object)" ersetzt. Einen Forenpost wie diesen, der diese Strings enthält, könntest du also zum Beispiel nicht korrekt cachen.
Die zweite Zeile kann man im Grunde ausklammern, weil das Verhalten, das dort gefixt werden soll, ja ein PHP-Bug ist. Ansonsten taugt das prinzipiell aber schon eher, um aus einem Kontext auszubrechen.
(Für das Beispiel lege ich mir jetzt einige Dinge mehr oder weniger passend zurecht, aber ich versuche dennoch, es mal durchzuexerzieren.)
Eigentlich gemeint ist diese Zeile:
PHP-Code:
$code = str_replace("\\' => '", "\\\\' => '", $code);
Das behebt unter 5.3.3 das zu Beginn des Threads besprochene Problem.
PHP-Code:
<?php
$data = <<<'EOT' { "frdl\\" : ".ApplicationComposer/lib/frdl/" } EOT;
$code = var_export(json_decode($data), true);
echo str_replace("\\' => '", "\\\\' => '", $code);
Ausgabe:
Code:
stdClass::__set_state(array(
'frdl\\' => '.ApplicationComposer/lib/frdl/',
))
Sagen wir jetzt mal, dass das Key/Value-Paar in der JSON-Eingabe beliebig vom Nutzer bestimmt werden kann. Er kann beide Strings festlegen. Dann ist folgender Angriff möglich:
PHP-Code:
<?php
$payload = <<<'EOT' for ($i = 1; $i <= 10; $i++) { echo "Code injection *** "; } EOT;
$key = 'frdl\\\\'; $value = '));' . $payload . 'exit;?>';
// $key und $value sind reguläre Strings, wie ein Nutzer sie irgendwo eingeben könnte.
$foo = new stdClass(); $foo->$key = $value;
// JSON erzeugen
$data = json_encode($foo);
// Und wieder auswerten
$code = var_export(json_decode($data), true);
$code = str_replace(array("stdClass::__set_state", 'o::__set_state', '\o::__set_state', 'O::__set_state', '\O::__set_state'), array("(object)", "(object)", "(object)", "(object)", "(object)"), $code); $code = str_replace("\\' => '", "\\\\' => '", $code);
eval($code);
Ausgabe unter PHP 5.3.3 (kann wieder mit 3v4l.org nachvollzogen werden, als Preview-Version 5.3.3 einstellen):
Code:
Code injection *** Code injection *** Code injection *** Code injection *** Code injection *** Code injection *** Code injection *** Code injection *** Code injection *** Code injection ***
Es ist also trotz deiner Ersetzungen unter gewissen Bedingungen noch möglich, mit Eingaben aus dem String-Kontext auszubrechen und so (mehr oder weniger) beliebigen PHP-Code in die Anwendung einzuschleusen.
Fairerweise kann man aber wohl sagen, dass das an der Stelle wohl tendenziell nicht an deinen Ersetzungen liegt, sondern an den var_export-Bugs in PHP. Ganz sicher bin ich mir nicht.
(Deine zweite Ersetzung ist „zufällig“ (?) safe (hinsichtlich meiner bisherigen Bemühungen), weil var_export anscheinend nur Single-Quote-Strings erzeugt (?). Darin werden Single-Quotes aus Nutzereingaben dann als \' escapet. Deshalb lässt sich wahrscheinlich keine Nutzereingabe konstruieren, die von der zweiten str_replace-Zeile betroffen wäre. Weil es nicht möglich ist, das alleinstehende und nicht escapete Single-Quote im Suchstring zu erzeugen.)
Aber vielleicht konnte ich etwas verdeutlichen, dass man mit so was echt aufpassen muss.
https://www.php-rocks.de/mitglieder/28-till.html
Registriert seit: 31.12.2015 | Themen: 49 | Beiträge: 194
Zitat:Aber vielleicht konnte ich etwas verdeutlichen, dass man mit so was echt aufpassen muss.
Danke das Du Dir die Zeit genommen hast!
Ich habe den str_replace Quatsch entfernt.
Statt die aufbereiteten Ergebnisse cache ich im Moment nur die request-response.
Zitat:Von 5.3.3 kommst du wirklich nicht irgendwie weg?
Langfristig ja, kurzfristig nein:
Auf dem Server sind (quantitativ) sehr umfangreiche und teilweise zusammenhängende "stable" Projekte ("never change running systems") in production mode, da wird ein update nicht an einem Tag getan sein. Ich werde sehen ob ich auf dem Server irgendwie 2 php versionen installieren und nach und nach updaten kann (?).
Zudem habe ich noch genug andere "offene Baustellen" auch mit "neuem" code die ich alsbald "fertig" bekommen möchte.
Zitat:Du musst ja theoretisch nur bis 5.3.7 upgraden. (Mehr wäre natürlich besser, aber da muss man dann schon eher mal schauen, was Kompatibilität angeht.)
Nur für diesen einen Bug mache ich kein Update auf 5.3.7, wenn dann "Nägel mit Köpfen".
|