PHP rocks! wünscht allen Mitgliedern einen guten Rutsch ins neue Jahr 2017 !!!
Hinweis: Das Forum zieht um! Um keine Datenverluste zu haben, schalten wir zwecks Übernahme der Daten das Forum am Sonntag, den 24.04.2016 um ca. 21:00 Uhr offline und passen anschliessend die DNS-Einträge an.
www.php-rocks.de wird euch dann nach den Aktualisierungen der DNS-Server wieder wie gewohnt uneingeschränkt zur Verfügung stehen.
Danke für euer Verständnis!

Themabewertung:
  • 0 Bewertung(en) - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Eval is a nicve feature - Eine Provokation
#1
Kann mir jemand erklären warum eval grundsätzlich böse sein soll?

Eine mögliche Gefahr sei, es könnten Benutzereingaben injeziert werden, aber liegt das an eval ansich?
Ist es nicht eher ein Fehler des Programmierers, als das es ein Fehler der Funktion selbst ist?

Wir haben folgenden konkreten Anwendungsfall 1):
http://www.php-rocks.de/thema/64-welchen-sinn-hat-eval-im-templating-.html
Hier macht es für mich keinen Sinn eval zu benutzen, ein z.B. include wäre vl. naheliegender!?!
Eval=evil=einverstanden!

Wir haben folgenden konkreten Anwendungsfall 2):
http://www.phpclasses.org/package/9071-PHP-Autoload-classes-with-PSR-0-PSR-4-mapping-etc-.html#view_files/files/60161
PHP-Code:
if(eval('$data = '.$source.';')===false
Dies ist der einzige Fall in welchem ich jemals "produktiv" eval einsetze, eingesetzt habe(!). Es wird remote Code geladen, geprüft und dann  ausgeführt.

- Würde ein abspeichern als datei und ein include den code sicherer machen? (Ich denke nicht!?!)
- Gibt es in meinem Anwendungsfall eine (sichere) Alternative zu eval?

These 1: eval ist entsprechend sicher oder unsicher, als wie es vom Programmierer verwendet wird.

------------------------------------------

These 2/Idee: Wann wird eval evaluiert, beim parsen oder zur Laufzeit, kann man "lazy loading" damit realisieren?


------------------------------------------

These 3: Eval is a nicve feature.


------------------------------------------

So, nun bitte ich die Experten um eine Erklärung (damit auch ich es verstehe)...
Antworten
#2
Hallo,

Ich bin jetzt auch nicht der große Experte, aber bei Dir ist $source imho der Knackpunkt.
Das wird zwar aus den Zeilen einer Datei generiert, aber die Datei-Herkunft ist evtl. manipulierbar.
Du weißt also am Ende nicht, ob sich in $source nicht Code versteckt.

Ausgeführt wird das allerdings nur, weil Du eval() benutzt => eval() = evil

Gruß Arne
Antworten
#3
Zitat:Ich bin jetzt auch nicht der große Experte, aber bei Dir ist $source imho der Knackpunkt.
Das wird zwar aus den Zeilen einer Datei generiert, aber die Datei-Herkunft ist evtl. manipulierbar.
Du weißt also am Ende nicht, ob sich in $source nicht Code versteckt.

Ausgeführt wird das allerdings nur, weil Du eval() benutzt => eval() = evil

Aber das ist imho hier doch gar kein Argument...:
Zitat:$source... Das wird zwar aus den Zeilen einer Datei generiert
Nein, viel schlimmer: Es wird von einer externen Url geladen!

Zitat:Du weißt also am Ende nicht, ob sich in $source nicht Code versteckt.
Ich gehe sogar davon aus das $source Code enthält, im vorliegenden Fall sollte es die Deklaration einer bestimmten Klasse sein.
Hier nochmal der komplette Code:
PHP-Code:
<?php
/**
 *
 * Copyright (c) 2015, Till Wehowski
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 * This product includes software developed by the frdl/webfan.
 * 4. Neither the name of frdl/webfan nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY frdl/webfan ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL frdl/webfan BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @author Till Wehowski <php.support@webfan.de>
 * @package frdl\webfan\Autoloading\SourceLoader
 * @uri /v1/public/software/class/webfan/frdl.webfan.Autoloading.SourceLoader/source.php
 * @file frdl\webfan\Autoloading\SourceLoader.php
 * @role Autoloader
 * @copyright 2015 Copyright (c) Till Wehowski
 * @license http://look-up.webfan.de/bsd-license bsd-License 1.3.6.1.4.1.37553.8.1.8.4.9
 * @license http://look-up.webfan.de/webdof-license webdof-license 1.3.6.1.4.1.37553.8.1.8.4.5
 * @link http://interface.api.webfan.de/v1/public/software/class/webfan/frdl.webfan.Autoloading.SourceLoader/doc.html
 * @OID 1.3.6.1.4.1.37553.8.1.8.8 webfan-software
 * @requires PHP_VERSION 5.3 >=
 * @requires webfan://frdl.webfan.App.code
 * @api http://interface.api.webfan.de/v1/public/software/class/webfan/
 * @reference http://www.webfan.de/install/
 * @implements StreamWrapper
 *
 */
namespace frdl\webfan\Autoloading;
use 
frdl\common;


class 
SourceLoader
{
    const 
NS __NAMESPACE__;
    const 
DS DIRECTORY_SEPARATOR;
    const 
SESSKEY __CLASS__;
    
/**
     * PKI
     */
    
const DISABLED 0;
    const 
OPENSSL 1;
    const 
PHPSECLIB 2;

    const 
E_NORSA 'No RSA library selected or supported';
    const 
E_NOTIMPLEMENTED 'Sorry thisd is not implemented yet';
   

    const 
B_SIGNATURE "-----BEGIN SIGNATURE-----\r\n";
    const 
E_SIGNATURE "-----END SIGNATURE-----";

    const 
B_CERTIFICATE "-----BEGIN CERTIFICATE-----\r\n";
    const 
E_CERTIFICATE "-----END CERTIFICATE-----";

    const 
B_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----\r\n";
    const 
E_PUBLIC_KEY "-----END PUBLIC KEY-----";

    const 
B_RSA_PRIVATE_KEY "-----BEGIN RSA PRIVATE KEY-----\r\n";
    const 
E_RSA_PRIVATE_KEY "-----END RSA PRIVATE KEY-----";

    const 
B_KEY "-----BEGIN KEY-----\r\n";
    const 
E_KEY "-----END KEY-----";
 
    const 
B_LICENSEKEY "-----BEGIN LICENSEKEY-----\r\n";
    const 
E_LICENSEKEY "-----END LICENSEKEY-----";


    public 
$sid;
   
    protected 
$lib;
   
   
    
    
/**
     * Stream Properties
     */
    
protected $Client;
    public 
$context = array();
    protected 
$data;
    protected 
$chunk;
    public 
$buflen;
    protected 
$pos 0;
    protected 
$read 0;
    public static 
$id_repositroy;
    public static 
$id_interface;
    public static 
$api_user;
    public static 
$api_pass;
    protected 
$eof false;
    protected 
$mode;
   
   
        protected 
$dir_autoload;
    protected static 
$config_source;
        protected 
$autoloaders = array();
        protected 
$autoloadersPsr0 = array();
    protected 
$classmap = array();
    protected 
$isAutoloadersRegistered false;
       
    protected 
$interface;
   
    
/**
     * "Run Time Cache" / Buffer
     */
    
protected static $rtc;
   
    protected static 
$instances = array();
   
    
    protected 
$buf = array(
      
'config' => array(),
          
'opt' => array(),
          
'sources' => array(),
    );
   
    function 
__construct($pass null)
     {
       
$this->sid count(self::$instances);
       
self::$instances[$this->sid] = &$this;
      
       
$this->interface null;
       
self::$config_source = array(
     
'install' => false,
         
'dir_lib' => false,
         
'session' => false,
         
'zip_stream' => false,
         
'append_eval_to_file' => false,
        
       );
       
$this->dir_autoload '';
       
self::repository(((!isset($_SESSION[self::SESSKEY]['id_repository']))?'frdl':$_SESSION[self::SESSKEY]['id_repository']));
       
self::$id_interface 'public';
       
self::$api_user '';
       
self::$api_pass '';
       
$this->Defaults(true);
       
$this->set_pass($pass);
     }


  public function 
j(){
       return \
webfan\App::God();
  }
    
    
  public static function 
top(){
        if(
=== count(self::$instances))return new self;
        return 
self::getStream(0);
  }
    
    
  public static function 
getStream($sid){
        return (isset(
self::$instances[$sid])) ? self::$instances[$sid] : null;
  }
    
  public static function 
repository($id null){
      if(
$id !== null)$_SESSION[self::SESSKEY]['id_repository'] = $id;
    
self::$id_repositroy = &$_SESSION[self::SESSKEY]['id_repository'];
    return 
self::$id_repositroy;
  }
    
  public function 
set_interface(Array &$interface null){
       
$this->interface = (is_array($interface)) ? $interface null;
     return 
$this;
  }
    
  public function 
config_source($key null$value null){
         if(!
is_string($key))return self::$config_source;
       if(!isset(
self::$config_source[$key]))return false;
       
self::$config_source[$key] = $value;
       return 
true;
  }
    

    
  public function 
Defaults($set false){
          
$config = array(
  
'host' => null,
  
'IP' => null,
  
'uid' => 0,
  
'encrypted' => true,
  
'e_method' => 2,
  
'c_method' => 1,
  
'source' => $this->config_source(),
  
'ERROR' => E_USER_WARNING,
  
'ini' => array(
      
'display_errors_details' => false,
      
'pev' => array(
           
'CUSTOM' => null,
           
'REQUEST' => true,
           
'HOST' => $_SERVER['SERVER_NAME'],
         
// 'IPs' => $App->getServerIp(),
           
'PATH' => null,
       ),
      ),
 
    
    );
   
   
         
          if(
$set === true){
                
$this->set_config($config);
          }
         
          return array(
             
'config' => $config,
          );
       
    }

    protected function 
set_pass($pass null){
       
$this->pass = (is_string($pass)) ? $pass mt_rand(10000,9999999).sha1($_SERVER['SERVER_NAME']).'faldsghdfshfdshjfdhjr5nq7q78bg2nda jgf jtrfun56m8rtjgfjtfjtzurtnmrt tr765 $bbg r57skgmhmh';
    }
   
    public function 
mkp(){
       
        
$this->set_pass(null);
         return 
$this;
    }
   
    public function 
set_config(&$config){
        
$this->config = (is_array($config)) ? $config $this->buf['config'];
        if(isset(
$this->config['source']) && is_array($this->config['source']))self::$config_source = &$this->config['source'];
        return 
$this;
    }
    



    public function 
installSource($class,&$code, &$error ='', &$config_source null){
        if(
$config_source === null)$config_source = &self::$config_source;
        if(
$config_source['install'] !== true)return null;
        if(!isset(
$code['php']))return false;
        if(isset(
$code['installed']) && $code['installed'] === true)return true;
       
         
$bs = new \frdl\webfan\Serialize\Binary\bin();
         
$code['doc'] = $bs->unserialize($this->unpack_license($code['d']));
                    
         
$error '';
         
$r false;
        
        if(isset(
$config_source['dir_lib']) && is_string($config_source['dir_lib']) && is_dir($config_source['dir_lib'])){
             
$dir rtrim($config_source['dir_lib'], self::DS ' '). self::DS ;
             
$filename $dir.str_replace('\\'self::DS$class).'.php';
             
$dir realpath(dirname($filename)).self::DS;
             if(!
is_dir($dir)){
               if(!
mkdir($dir0755true)){
                     
$error 'Cannot create directory '.$dir.' and cannot save class '.$class.' in '.__METHOD__.' '.__LINE__;
                     
trigger_error($error,E_USER_WARNING);
               }
             }
            
             if(
$error === ''){
               
$file_header "/**\n* File generated by frdl Application Composer : class : ".__CLASS__."\n**/\n";
               
$code '<?php '."\n".$file_header."\n\$filemtime = ".time().";\n\$class_documentation = ".var_export($code['doc'], true).";\n".$code['php']."\n";
            
               
$fp fopen($filename'wb+');
               
fwrite($fp,$code);
               
fclose($fp);
               if(
file_exists($filename)){
                 
$code['installed'] = true;
                 
$r true;
               }else{
                  
$error 'Cannot create file '.$filename.' and cannot save class '.$class.' in '.__METHOD__.' '.__LINE__;
                     
trigger_error($error,E_USER_WARNING);
               }
             }
        }
            
            
            
            
       return 
$r;
    }



   
    public function 
patch_autoload_function($class){
        if(
function_exists('__autoload'))return __autoload($class);
    }
        
    public function 
autoload_register(){
        if(
false !== $this->isAutoloadersRegistered){
              
trigger_error('Autoloadermapping is already registered.',E_USER_NOTICE);
              return 
$this;
        }
        
$this->addLoader(array($this,'Psr4'), truetrue);
        
$this->addLoader(array($this,'Psr0'), truefalse);
        
$this->addLoader(array($this,'classMapping'), truefalse);
        
$this->addLoader(array($this,'patch_autoload_function'), truefalse);
        
$this->addLoader(array($this,'autoloadClassFromServer'), truefalse);
        
$this->isAutoloadersRegistered true;
        return 
$this;
    }
   
    public function 
addLoader($Autoloader$throw true$prepend false){
       
spl_autoload_register($Autoloader$throw$prepend);
       return 
$this;
    }

    public function 
unregister$Autoloader)
     {
        
spl_autoload_unregister($Autoloader);
        return 
$this;
     }
    
    
    
/**
     * Psr-0
     */
    
public function addPsr0($prefix$base_dir$prepend false)
    {
       
$prefix trim($prefix'\\') . '\\';
       
$base_dir rtrim($base_dirself::DS) . self::DS;
       if(isset(
$this->autoloadersPsr0[$prefix]) === false) {
            
$this->autoloadersPsr0[$prefix] = array();
        }

      if(
$prepend) {
            
array_unshift($this->autoloadersPsr0[$prefix], $base_dir);
        } else {
            
array_push($this->autoloadersPsr0[$prefix], $base_dir);
        }
       
        return 
$this;
    }
   
    
/**
     * Psr-4
     */
    
public function addNamespace($prefix$base_dir$prepend false)
    {
       return 
$this->addPsr4($prefix$base_dir$prepend);
    }
    public function 
addPsr4($prefix$base_dir$prepend false)
    {
   
       
$prefix trim($prefix'\\') . '\\';
       
$base_dir rtrim($base_dirself::DS) . self::DS;
       if(isset(
$this->autoloaders[$prefix]) === false) {
            
$this->autoloaders[$prefix] = array();
        }
   
      if(
$prepend) {
            
array_unshift($this->autoloaders[$prefix], $base_dir);
        } else {
            
array_push($this->autoloaders[$prefix], $base_dir);
        }
       
        return 
$this;
    }
   


   
    public function 
Psr4($class)
    {
   
        
$prefix $class;
        while (
false !== $pos strrpos($prefix'\\')) {
            
$prefix substr($class0$pos 1);
            
$relative_class substr($class$pos 1);
            
$file $this->routeLoaders($prefix$relative_class);
            if (
$file) {
                return 
$file;
            }
            
$prefix rtrim($prefix'\\');
        }
       
        return 
false;
    }
    public function 
loadClass($class)
    {
       return 
$this->Psr4($class);
    }
   
   
   
   public function 
Psr0($class)
    {
        
$prefix $class;
        while (
false !== $pos strrpos($prefix'\\')) {
            
$prefix substr($class0$pos 1);
            
$relative_class substr($class$pos 1);
            
$file $this->routeLoadersPsr0($prefix$relative_class);
            if (
$file) {
                return 
$file;
            }
            
$prefix rtrim($prefix'\\');
        }
        return 
false;
    }
         
    protected function 
routeLoadersPsr0($prefix$relative_class)
    {
        if (!isset(
$this->autoloadersPsr0[$prefix])) {
            return 
false;
        }
        foreach (
$this->autoloadersPsr0[$prefix] as $base_dir) {
          if (
null === $prefix || $prefix.'\\' === substr($relative_class0strlen($prefix.'\\'))) {
            
$fileName '';
            
$namespace '';
            if (
false !== ($lastNsPos strripos($relative_class'\\'))) {
                
$namespace substr($relative_class0$lastNsPos);
                
$relative_class substr($relative_class$lastNsPos 1);
                
$fileName str_replace('\\'self::DS$namespace) . self::DS;
            }
            
$fileName .= str_replace('_'self::DS$relative_class/* . '.php' */;
            
$file = ($base_dir !== null $base_dir self::DS '') . $fileName;
            if (
$this->inc($file)) {
                return 
$file;
            }
          }
        }
       return 
false;
    }


    public function 
setAutloadDirectory($dir){
         if(!
is_dir($dir))return false;
       
$this->dir_autoload $dir;
       if(
substr($this->dir_autoload,-1,1) !== self::DS)$this->dir_autoload.=self::DS;
       return 
true;
    }
         
    protected function 
routeLoaders($prefix$relative_class)
    {

        if (!isset(
$this->autoloaders[$prefix])) {
            return 
false;
        }
        foreach (
$this->autoloaders[$prefix] as $base_dir) {
           
            
$file $base_dir
                  
str_replace('\\'self::DS$relative_class)
                  
/* . '.php' */
                   
;

       
            if (
$this->inc($file)) {
                return 
$file;
            }
        }
        return 
false;
    }
   
    protected function 
inc($file)
    {
        if(
substr($file,-4,4) === '.php'){
            
$file $file;
        }else{
            
$file.= '.php';
        }
        
$file2substr($file,0,-4).'.inc';
   
       if(
file_exists($file)) {
             require 
$file;
            return 
true;
        }elseif(
file_exists($file2)) {
             require 
$file2;
            return 
true;
        }
       
       
        return 
false;
    }
        
        
   
    public function 
classMapping($class){
        if(isset(
$this->classmap[$class])){
            if (
$this->inc($this->classmap[$class])) {
                return 
$this->classmap[$class];
            }
        }
       
        return 
false;
    }
   
   
    public function 
class_mapping_add($class$file, &$success null){
        if(
file_exists($file)){
            
$this->classmap[$class] = $file;
            
$success true;
        }else{
            
$success false;
        }
       
       return 
$this;
    }
   
    public function 
class_mapping_remove($class){
        if(isset(
$this->classmap[$class]))unset($this->classmap[$class]);
        return 
$this;
    }
            
        
        
    protected function 
source_check($str){
         
$start 'array';
         
$badwords = array('$',';''?''_''function ''class ');
   
         foreach(
$badwords as $num => $s){
             if(
strpos($str$s)!== false)return false;
         }
        
         if(
substr($str,0,strlen($start)) !== $start)return false;
         if(!
preg_match('/[a-f0-9]{40}/'$str))return false;
        
        
         return 
true;
    }
    public function 
autoloadClassFromServer($className){
   
          
$classNameOrig $className;
          if(
class_exists($className))return;
          if (!
in_array('webfan'stream_get_wrappers())){
              
trigger_error('Streamwrapper webfan is not registered. Call to webfan\App::init() required.'E_USER_ERROR);
            return;
          }
          
$className str_replace('\\''.'$className);
          
$className ltrim($className' .');
          
$RessourceFileName 'webfan://'.$className.'.code';
          
          
$fp fopen($RessourceFileName'r');
          
$source '';
          if(
$fp){
              
clearstatcache();
            
clearstatcache(true,$RessourceFileName);
            
$stat fstat($fp);
            
$bufsize = ($stat['size'] <= 8192) ? $stat['size'] : 8192;
              while(!
feof($fp) ){
                
$source .= fread($fp$bufsize);
            }
             
fclose($fp);
          }else{
              return 
false;
          }
         
         
        if(
$source ===false || $source ==='' ){
                   
trigger_error('Cannot get source from the webfan code server ('.$RessourceFileName.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
            }
                 
        
$scheck $this->source_check($source);
        if(
$scheck !== true){
                   
trigger_error('The source loaded from the code server looks malicious ('.$scheck.' '.$RessourceFileName.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
        }
       
        if(eval(
'$data = '.$source.';')===false){
                   
trigger_error('Cannot process the request to the source server by APIDClient ('.$RessourceFileName.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
            }
       
        
$_defaults $this->Defaults();
        
$config $_defaults["config"];
        
$opt = (isset($data['opt'])) ? $data['opt'] : $this->getOpt();
        
$code $data['source'];

        
$sources = array();
        
$sources[$classNameOrig] = $code;

        if(
is_array($this->interface)){
            
$opt['pass'] = $this->interface['API_SECRET'];
            
$opt['rot1'] = $this->interface['rot1'];
            
$opt['rot2'] = $this->interface['rot2'];
        }
       

        if(
$this->loadSources($sources,$opt$config )===false){
                   
trigger_error('Cannot process the request to the source server by APIDClient ('.$className.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
        }
       
        return 
$RessourceFileName;
    }
    

    public function 
make_pass_3(&$opt){
             if(isset(
$opt['pwdstate']) && $opt['pwdstate'] === 'decrypted')return true;
             if(isset(
$opt['pwdstate']) && $opt['pwdstate'] === 'error')return false;
            if(!isset(
self::$rtc['CERTS']))self::$rtc['CERTS'] = array();
            
$hash sha1($opt['CERT']);
            
$u parse_url($opt['CERT']);
          
$url $opt['CERT'];
           if(!isset(
self::$rtc['CERTS'][$hash]) && ($u === false || !isset(self::$rtc['CERTS'][$url])))
               {
                    if(
$u !== false && count($u) >&& !preg_match("/CERTIFICATE/"$opt['CERT']) ){
                     if(isset(
$u['scheme']) && isset($u['host'])){
                
$h explode('.',$u['host']);
                 
$h array_reverse($h);
                 if(
$h[0] === 'de' && ($h[1] === 'webfan' || $h[1] === 'frdl' )){
                 if(
class_exists('\webdof\Http\Client')){
                 
$Http = new \webdof\Http\Client();
                
$post = array();
                
$send_cookies = array();
                
$r $Http->request($opt['CERT'], 'GET'$post$send_cookiesE_USER_WARNING);
                }else{
                    
$c file_get_contents($opt['CERT']);
                    
$r = array();
                    
$r['status'] = (preg_match("/CERTIFICATE/",$c)) ? 200 400;
                    
$r['body'] = $c;
                }
               
                if(
intval($r['status'])===200){
               
$CERT trim($r['body']);
               }else{
                 
$opt['pwdstate'] = '404';
                return 
false;
              }
               }
           }else{
                   
$CERT trim(file_get_contents($opt['CERT']));
                }
                   
$key $url;
                  if(!isset(
self::$rtc['CERTS'][$key]))self::$rtc['CERTS'][$key] = array();
                 
self::$rtc['CERTS'][$key]['crt'] = $CERT;
             }elseif(
preg_match("/CERTIFICATE/"$opt['CERT'])){
                     
$key $hash;
                    if(!isset(
self::$rtc['CERTS'][$key]))self::$rtc['CERTS'][$key] = array();
                    
$CERT utf8_encode($opt['CERT']);
                    
$CERT=$this->loadPK($CERT);
                    if(
$CERT===false){
                         
trigger_error('Cannot procces certificate info in '.__METHOD__.' line '.__LINE__E_USER_WARNING);
                      return 
false;
                   }
                    
$CERT=$this->save($CERTself::B_CERTIFICATEself::E_CERTIFICATE);
                    
self::$rtc['CERTS'][$key]['crt'] =$CERT;
                  
              }else{
                         
trigger_error('Cannot procces certificate info in '.__METHOD__.' line '.__LINE__E_USER_WARNING);
                      return 
false;
                   }
                 }elseif(isset(
self::$rtc['CERTS'][$hash])){
                     
$key $hash;
                  }elseif(isset(
self::$rtc['CERTS'][$url])){
                      
$key $url;
                  }else{
                       
trigger_error('Cannot procces certificate info in '.__METHOD__.' line '.__LINE__E_USER_WARNING);
                     return 
false;
                  }


            
$this->setLib(1);
        if(!isset(
self::$rtc['CERTS'][$key]['PublicKey'])){
              
$PublicKey $this->getPublKeyByCRT(self::$rtc['CERTS'][$key]['crt']);
             
self::$rtc['CERTS'][$key]['PublicKey'] = $PublicKey;
           }
            
$success $this->decrypt($opt['pass'],self::$rtc['CERTS'][$key]['PublicKey'],$new_pass) ;
          if(
$success === true){
            
$opt['pass'] = $new_pass;
           
$opt['pwdstate'] = 'decrypted';
            }else{
               
$opt['pwdstate'] = 'error';
              
// unset(self::$rtc['CERTS'][$key]);
            
}
           return 
$success;
    }

    protected function 
load(&$code, Array &$config null, &$opt = array('pass' => null'rot1' => 5'rot2' => 3), $class null){
          
$p $this->_unwrap_code(((is_string($code)) ? $code $code['c']));
         
          if(isset(
$opt['e']) && is_bool($opt['e']))$config['encrypted'] = $opt['e'];
          if(isset(
$opt['m']))$config['e_method'] = $opt['m'];
         
           if(
$config['encrypted'] === true && intval($config['e_method']) === 1){
                 
trigger_error('The options encryption method is deprecated in '.__METHOD__.' '.__LINE__,$config['ERROR']);
             return 
false;
           }
         
           if(
$config['encrypted'] === true && intval($config['e_method']) === 2){
              
$p trim($this->crypt($p'decrypt'$opt['pass'], $opt['rot1'], $opt['rot2']));
           }
         
           if(
$config['encrypted'] === true && intval($config['e_method']) === 3){
                if(
$this->make_pass_3($opt) == false){
                
trigger_error('Cannot decrypt password properly [1] from '.self::$id_repositroy.' for '.$class.' in '.__METHOD__.' '.__LINE__,$config['ERROR']);
               return 
false;
                }
              
$p trim($this->crypt($p'decrypt'$opt['pass'], $opt['rot1'], $opt['rot2']));
           }
                                 
           if(isset(
$code['s']) && $code['s'] !== sha1($p)){
                   
$errordetail = ($config['ini']['display_errors_details'] === true)
                              ? 
'<pre>'.sha1($p).'</pre><pre>'.$code['s'].'</pre><pre>'.$opt['pass'].' '.$opt['rot1'].' '.$opt['rot2'].'</pre>'
                              
'';
                                                     
                  
trigger_error('Cannot decrypt source properly [2] from '.self::$id_repositroy.' for '.$class.' in '.__METHOD__.' '.__LINE__.$errordetail,$config['ERROR']);

               return 
false;
           }
         
            
$p $this->unwrap_namespace($p);
           
$code['php'] = $p;
           try{
                 
$parsed = eval($p);
           }catch(
Exception $e){
                 
$parsed false;
           }
          if(
$parsed === false){
               
$errordetail = ($config['ini']['display_errors_details'] === true)
                              ? 
'<pre>'.htmlentities($p).'</pre>'
                              
'';
             
trigger_error('Parse Error in '.__METHOD__.' '.__LINE__.$errordetail,$config['ERROR']);
             return 
false;
          } else {
               unset(
$code['c']);
          }
         
          
$error '';
          
$config_source = (isset($config['source'])) ? $config['source'] : self::$config_source;
          
$installed $this->installSource($class,$code$error$config_source);
         
          
usleep(75);
          return 
true;
    }


    public function 
loadSource(&$code, Array &$config null, &$opt = array('pass' => null'rot1' => 5'rot2' => 3), $class null){
         return 
$this->load($code$config$opt$class );
    }

    public function 
loadSources(&$sources, &$opt = array('pass' => null'rot1' => 5'rot2' => 3), Array &$config null){
       
$this->set_config($config);
       
$this->mkp($config);
       foreach(
$sources as $class => $code){
             if(
class_exists($class))continue;
          if(
$this->load($code$config$opt$class) === false){
              return 
false;
          }
       }
       
       return 
true;
    }
   
   
    public function 
crypt($data$command 'encrypt'$Key NULL$offset 5$chiffreBlockSize 3)
    {
       if(
$command === 'encrypt'){
            
$data sha1(trim($data)).$data;
           
                
$k sha1($Key).$Key;
               
                
$str $data;
                
$data '';
               
               
                for(
$i=1$i<=strlen($str); $i++)
                {
                    
$char substr($str$i-11);
                    
$keychar substr($k, ($i strlen($k))-11);
                    
$char chr(ord($char)+ord($keychar));
                    
$data .= $char;
                }
       }
       if(!
is_numeric($offset)||$offset<0)$offset=0;if(!isset($data)||$data==""||!isset($Key)||$Key==""){return FALSE;}$pos="0";for($i=0;$i<=(strlen($data)-1);$i++){$shift=($offset+$i)*$chiffreBlockSize;while($shift>=256){$shift-=256;}$char=substr($data,$i,1);$char=ord($char)+$shift;if($pos>=strlen($Key)){$pos="0";}$key=substr($Key,$pos,1);$key=ord($key)+$shift;if($command=="decrypt"){$key=256-$key;}$dataBlock=$char+$key;if($dataBlock>=256){$dataBlock=$dataBlock-256;}$dataBlock=chr(($dataBlock-$shift));if(!isset($crypted)){$crypted=$dataBlock;}else{$crypted.=$dataBlock;}$pos++;}
       if(
$command === 'decrypt'){
                 
$decrypt '';
                
$k sha1($Key).$Key;
           
                for(
$i=1$i<=strlen($crypted); $i++)
                {
                    
$char substr($crypted$i-11);
                    
$keychar substr($k, ($i strlen($k))-11);
                    
$char chr(ord($char)-ord($keychar));
                    
$decrypt .= $char;
                }
              
$crypted substr($decrypt,strlen(sha1("1")),strlen($decrypt));
           
$hash_check substr($decrypt,0,strlen(sha1("1")));
           if(
trim($hash_check) !== sha1($crypted) || sha1($crypted)==='da39a3ee5e6b4b0d3255bfef95601890afd80709'){
                
$crypted false;
                
trigger_error('Broken data consistence in '.__METHOD__E_USER_NOTICE);
           }
       }
       return 
$crypted;
    }

  
    public function 
unwrap_namespace($s){
        
$s preg_replace("/^(namespace ([A-Za-z0-9\_".preg_quote('\\')."]+);){1}/"'${1}'."\n"$s);
        return 
preg_replace("/(\nuse ([A-Za-z0-9\_".preg_quote('\\')."]+);)/"'${1}'."\n"$s);
    }
       
    public function 
_unwrap_code($c){return trim(gzuncompress(gzuncompress(base64_decode(str_replace("\r\n\t",""$c))))," \r\n");}
    public function 
unpack_license($l){return gzuncompress(gzuncompress(base64_decode(str_replace("\r\n"""$l))));}
    function 
__destruct() {foreach(array_keys(get_object_vars($this)) as $value){unset($this->$value);}}
   
   
    
/**
     * PKI
     */

   
public function setLib($lib)
     {
        
$this->lib $lib;
       return 
$this;
     }

   public function 
save($data$begin "-----BEGIN SIGNATURE-----\r\n"$end '-----END SIGNATURE-----')
     {
        return 
$begin chunk_split(base64_encode($data)) . $end;
     }


   public function 
loadPK($str)
     {
       
$data preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]#'''$str);
       return 
preg_match('#^[a-zA-Z\d/+]*={0,2}$#'$data) ? utf8_decode (base64_decode($data) ) : false;
     }

  public function 
error($error$mod E_USER_ERROR$info TRUE)
    {
      
trigger_error($error.(($info === TRUE) ? ' in '.__METHOD__.' line '.__LINE__ ''), $mod);
      return 
FALSE;
    }
   
   
  public function 
verify($data$sigBin$publickey$algo 'sha256WithRSAEncryption')
     {
        switch(
$this->lib)
          {
           case 
self::OPENSSL :
                  return 
$this->verify_openssl($data$sigBin$publickey$algo);
                break;

           case 
self::PHPSECLIB :
                  return 
$this->verify_phpseclib($data$sigBin$publickey$algo);
                break;
           case 
self::DISABLED :
           default :
                  return 
$this->error(self::E_NORSAE_USER_ERROR);
                break;

          }

     }
   
       
  public function 
getPublKeyByCRT($cert)
     {
        switch(
$this->lib)
          {
           case 
self::OPENSSL :
                  return 
$this->getPublKeyByCRT_openssl($cert);
                break;

           case 
self::PHPSECLIB :
                  return 
$this->error(self::E_NOTIMPLEMENTEDE_USER_ERROR);
                break;
           case 
self::DISABLED :
           default :
                  return 
$this->error(self::E_NORSAE_USER_ERROR);
                break;

          }

     }
    
  public function 
encrypt($data,$PrivateKey,&$out)
     {
        switch(
$this->lib)
          {
           case 
self::OPENSSL :
                  return 
$this->encrypt_openssl($data,$PrivateKey,$out);
                break;
        case 
self::PHPSECLIB :
                  return 
$this->error(self::E_NOTIMPLEMENTEDE_USER_ERROR);
                break;
           case 
self::DISABLED :
           default :
                  return 
$this->error(self::E_NORSAE_USER_ERROR);
                break;

          }

     }
    

  public function 
decrypt($decrypted,$PublicKey,&$out)
     {
        switch(
$this->lib)
          {
           case 
self::OPENSSL :
                  return 
$this->decrypt_openssl($decrypted,$PublicKey,$out);
                break;
        case 
self::PHPSECLIB :
                  return 
$this->error(self::E_NOTIMPLEMENTEDE_USER_ERROR);
                break;
           case 
self::DISABLED :
           default :
                  return 
$this->error(self::E_NORSAE_USER_ERROR);
                break;

          }

     }
         
  protected function 
encrypt_openssl($data,$PrivateKey,&$out) {
     
$PrivKeyRes openssl_pkey_get_private($PrivateKey);
     return 
openssl_private_encrypt($data,$out,$PrivKeyRes);
  }
 
  protected function 
decrypt_openssl($decrypted,$PublicKey,&$out) {
        
$pub_key openssl_get_publickey($PublicKey);
        
$keyData openssl_pkey_get_details($pub_key);
        
$pub $keyData['key'];
        
$successDecrypted openssl_public_decrypt(base64_decode($decrypted),$out,$PublicKeyOPENSSL_PKCS1_PADDING);
        return 
$successDecrypted;
  }
 


  protected function 
getPublKeyByCRT_openssl($cert)
    {
       
$res openssl_pkey_get_public($cert);
       
$keyDetails openssl_pkey_get_details($res);
       return 
$keyDetails['key'];
    }

    

  protected function 
verify_phpseclib($data$sigBin$publickey$algo 'sha256WithRSAEncryption')
      {
         
$isHash preg_match("/^([a-z]+[0-9]).+/"$algo$hashinfo);
         
$hash = ($isHash) ? $hashinfo[1] : 'sha256';

         
$rsa = new Crypt_RSA();
         
$rsa->setHash($hash);
         
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
         
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
         
$rsa->loadKey($publickey);
         return ((
$rsa->verify($data$sigBin) === TRUE) ? TRUE FALSE);
      }


   protected function 
verify_openssl($data$sigBin$publickey$algo 'sha256WithRSAEncryption')
      {
        return ((
openssl_verify($data$sigBin$publickey$algo) == 1) ? TRUE FALSE);
      }
     
     
         
   
    
/**
     * Streaming Methods
     */
    
public function init(){$args func_get_args(); /** todo ... */ return $this;}
    public function 
DEFRAG(){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
stream_open($url$mode$options STREAM_REPORT_ERRORS, &$opened_path null){
        
$u parse_url($url);
        
$c explode('.',$u['host']);
        
$c array_reverse($c);
       
        
$this->mode $mode;
       
        if(
$c[0]==='code')$tld array_shift($c);
       
       
       
        
/**
         * ToDo: APICLient
         * $this->Client = new \frdl\Client\RESTapi();
         *
         * URL Pattern / e.g. this Class:
         * http://interface.api.webfan.de/v1/public/software/class/webfan/frdl.webfan.Autoloading.SourceLoader/source.php
         *
         */
        
if(class_exists('\webdof\wHTTP') && class_exists('\webdof\Http\Client') && class_exists('\webdof\Webfan\APIClient')){
          
$this->Client = new \webdof\Webfan\APIClient();
          
$this->Client->prepare'http',
                          
'interface.api.webfan.de',
                          
'GET',
                          
self::$id_interface// i1234
                          
'software',
                          array(), 
//post
                          
array(), //cookie
                          
self::$api_user,
                          
self::$api_pass,
                          
'class',
                          
'php'//format ->hier: "php"
                          
'source',
                           array(
self::$id_repositroy,implode(".",array_reverse($c))),
                           array(), 
//get
                          
1,
                          
E_USER_WARNING);
                         
         
$this->eof false;
         
$this->pos 0;
         try{
               
$r $this->Client->request();
               if(
intval($r['status']) !== 200)return false;
               
$this->data $r['body'];
   
         }catch(
Exception $e){
            
trigger_error('Cannot process the request to '.$urlE_USER_WARNING);
            return 
false;
         }
       }else{
          
$url 'http://interface.api.webfan.de/v1/'.self::$id_interface.'/software/class/'.self::$id_repositroy.'/'.implode(".",array_reverse($c)).'/source.php';
          
$data file_get_contents($url);
          if(
false === $data){
               return 
false;
          }else{
               
$this->data $data;
          }
       }
                 
        return 
true;
    }
    public function 
dir_closedir(){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
dir_opendir($path $options){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
dir_readdir(){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
dir_rewinddir(){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
mkdir($path $mode $options){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
rename($path_from $path_to){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
rmdir($path $options){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
     public function 
stream_cast($cast_as){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
     public function 
stream_close(){
         
$this->Client null;
    }
    public function 
stream_eof(){
        
$this->eof = ($this->pos >= strlen($this->data));
        return 
$this->eof;
    }
    public function 
stream_flush(){
        
//echo $this->data;
        
$this->pos strlen($this->data);
        return 
$this->data;
    }
    public function 
stream_lock($operation){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
stream_set_option($option $arg1 $arg2){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
stream_stat(){
         return array(
                  
'mode' => $this->mode,
                  
'size' => strlen($this->data) * 8,
         );
    }
    public function 
unlink($path){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
url_stat($path $flags){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
stream_read($count){
         if(
$this->stream_eof())return '';
       
         
$maxReadLength strlen($this->data) - $this->pos;
         
$readLength min($count$maxReadLength);

        
$p=&$this->pos;
        
$ret substr($this->data$p$readLength);
        
$p += $readLength;
        return (!empty(
$ret)) ? $ret '';
    }
    public function 
stream_write($data){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
    public function 
stream_tell(){return $this->pos;}
    public function 
stream_seek($offset$whence){
       
       
       
        
$l=strlen($this->data);
        
$p=&$this->pos;
        switch (
$whence) {
            case 
SEEK_SET$newPos $offset; break;
            case 
SEEK_CUR$newPos $p $offset; break;
            case 
SEEK_END$newPos $l $offset; break;
            default: return 
false;
        }
        
$ret = ($newPos >=&& $newPos <=$l);
        if (
$ret$p=$newPos;
        return 
$ret;
    }
    public function 
stream_metadata($path$option$var){trigger_error('Not implemented yet: '.get_class($this).' '.__METHOD__E_USER_ERROR);}
  


Meine Beschreibung war nicht ganz vollständig:
Es wird ein Autoloader registriert...:
PHP-Code:
$this->addLoader(array($this,'autoloadClassFromServer'), truefalse); 
...welcher Content von einem remote server lädt, das erste mal prüft, und der Variable $data zuweist.
PHP-Code:
public function autoloadClassFromServer($className){
   
          
$classNameOrig $className;
          if(
class_exists($className))return;
          if (!
in_array('webfan'stream_get_wrappers())){
              
trigger_error('Streamwrapper webfan is not registered. Call to webfan\App::init() required.'E_USER_ERROR);
            return;
          }
          
$className str_replace('\\''.'$className);
          
$className ltrim($className' .');
          
$RessourceFileName 'webfan://'.$className.'.code';
          
          
$fp fopen($RessourceFileName'r');
          
$source '';
          if(
$fp){
              
clearstatcache();
            
clearstatcache(true,$RessourceFileName);
            
$stat fstat($fp);
            
$bufsize = ($stat['size'] <= 8192) ? $stat['size'] : 8192;
              while(!
feof($fp) ){
                
$source .= fread($fp$bufsize);
            }
             
fclose($fp);
          }else{
              return 
false;
          }
         
         
        if(
$source ===false || $source ==='' ){
                   
trigger_error('Cannot get source from the webfan code server ('.$RessourceFileName.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
            }
                 
        
$scheck $this->source_check($source);
        if(
$scheck !== true){
                   
trigger_error('The source loaded from the code server looks malicious ('.$scheck.' '.$RessourceFileName.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
        }
       
        if(eval(
'$data = '.$source.';')===false){
                   
trigger_error('Cannot process the request to the source server by APIDClient ('.$RessourceFileName.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
            }
       
        
$_defaults $this->Defaults();
        
$config $_defaults["config"];
        
$opt = (isset($data['opt'])) ? $data['opt'] : $this->getOpt();
        
$code $data['source'];

        
$sources = array();
        
$sources[$classNameOrig] = $code;

        if(
is_array($this->interface)){
            
$opt['pass'] = $this->interface['API_SECRET'];
            
$opt['rot1'] = $this->interface['rot1'];
            
$opt['rot2'] = $this->interface['rot2'];
        }
       

        if(
$this->loadSources($sources,$opt$config )===false){
                   
trigger_error('Cannot process the request to the source server by APIDClient ('.$className.')! '.__METHOD__.' '.__LINE__E_USER_WARNING);
                 return 
false;
        }
       
        return 
$RessourceFileName;
    } 

Erst in der Methode "load" wird erst der Code entschlüsselt und nochmal evaled:
PHP-Code:
$parsed = eval($p); 

Beispiel solch einer "remote-Klasse", hier die SourceLoader Klasse selbst:
http://interface.api.webfan.de/v1/public/software/class/frdl/frdl.webfan.Autoloading.SourceLoader/source.php

---

Das sind bis hierhin allerdings Details, entscheident ist:
- Ich möchte remote Code laden, überprüfen und ausführen
- Inwiefern macht es hier meinen Code sicherer wenn ich auf eval verzichte (und wie sollte ich stattdessen vorgehen)?
Antworten
#4
Till schrieb:These 1: eval ist entsprechend sicher oder unsicher, als wie es vom Programmierer verwendet wird.

Ja. Letztlich hängt es immer vom Einsatz ab. SQL-Query-Strings in PHP, in die variable Daten eingefügt werden, sind ja etwa auch nicht per se unsicher, sondern werden dadurch unsicher, dass Programmierer den Kontextwechsel und das damit verbundene Escaping der Eingaben nicht beachten.

Sicherheit ist eigentlich ein trivialer Aspekt, wenn es um die Beurteilung von Vorgehensweisen oder Sprachbestandteilen auf konzeptioneller Ebene geht. Man sollte voraussetzen, dass bei der Verwendung keine sicherheitsrelevanten Fehler begangen werden.

Aspekt Sicherheit:

Dennoch ist es nicht verkehrt, bei eval() explizit auf das Thema Sicherheit hinzuweisen, da die Eingabe grundsätzlich nun mal beliebiger PHP-Code ist, bei dem es nicht möglich ist oder nicht großartig Sinn ergibt, mit Escaping oder Filterung anfangen zu wollen, weil es in der Regel genau das Ziel ist, beliebigen Code auszuführen.

Es gibt also in dem Code, der eval() aufruft, zumeist keinen Weg, zu prüfen, ob Eingaben in Ordnung sind oder ob sie irgendeinen Unsinn anstellen. Damit wird Kontrolle abgegeben und eine verhältnismäßig große Angriffsfläche geschaffen, weil alle Stellen, die an der Generierung der Eingaben für eval() beteiligt sind, zu 100 % sicher sein müssen. Ist das nicht der Fall, können sich rasch Lücken ergeben, über die auf Umwegen beliebiger Code mit den Rechten der Anwendung selbst ausgeführt werden kann.

Das ist ein ähnlicher Punkt wie etwa bei {php}-Tags in einer Template-Sprache wie Smarty. Die sind dort auch nicht zufällig als deprecated markiert. Mit derlei Tags kann jeder, der das Recht hat, ein Template zu editieren, beliebigen Code ausführen. Werden die Templates dazu noch in der Datenbank gespeichert, reicht vielleicht eine SQL-Injection-Lücke, um beliebigen PHP-Code in eine Anwendung zu schleusen.

Derlei Risiken sollte man nach Möglichkeit konzeptionell minimieren. Und eval() ist letztlich nun mal ein Jackpot, an den jeder Angreifer ran will.

Aspekt Design:

Hier vorerst nur die Behauptungen, dass in den meisten Fällen zugunsten einer saubereren Lösung auf den Einsatz von eval() verzichtet werden kann und dass eval() eine weitere Ebene an Komplexität in den Code einzieht und dazu beiträgt, Zuständigkeiten zu vermischen, die getrennt werden sollten.

Till schrieb:These 2/Idee: Wann wird eval evaluiert, beim parsen oder zur Laufzeit, kann man "lazy loading" damit realisieren?

Zur Laufzeit.

Na ja, das ist Ausführung von PHP-Code. Damit kann man alles tun, was man mit PHP-Code tun kann. Damit kann man lazy loading realisieren, aber das geht eben auch auf sinnvollere Art und Weise.

Till schrieb:These 3: Eval is a nicve feature.

Ich persönlich sehe die Vorteile nicht, sehe aber etliche Nachteile. Ansonsten fand ich vorhin diese Faustregel ganz treffend:

Zitat:As a rule of thumb I tend to follow this:

1. Sometimes eval() is the only/the right solution.
2. For most cases one should try something else.
3. If unsure, goto 2.
4. Else, be very, very careful.

- http://stackoverflow.com/questions/951373/when-is-eval-evil-in-php

Nur dass ich im Zweifel zu Punkt 1 überzeugende Beispiele einfordern würde.

Du sagst letztlich ja auch selbst:

Till schrieb:Dies ist der einzige Fall in welchem ich jemals "produktiv" eval einsetze, eingesetzt habe(!). Es wird remote Code geladen, geprüft und dann ausgeführt.

- Würde ein abspeichern als datei und ein include den code sicherer machen? (Ich denke nicht!?!)
- Gibt es in meinem Anwendungsfall eine (sichere) Alternative zu eval?

Ich halte das Konzept, in einer Anwendung Remote-Code zu laden und auszuführen, grundsätzlich für ganz schlecht. Das ist so ziemlich das Letzte, was ich als Anwender wollen würde.

Edit:

- Es ist langsam (zusätzliche Netzwerk-Requests).
- Es macht die Lauffähigkeit der eigenen Anwendung vom Status eines externen Systems abhängig.
- Es gibt einer externen Stelle die Möglichkeit, beliebigen Code auf dem eigenen System auszuführen.
- Die externe Stelle könnte gehackt werden.
Antworten
#5
Hallo mermshaus,
Zitat:Ich halte das Konzept, in einer Anwendung Remote-Code zu laden und auszuführen, grundsätzlich für ganz schlecht. Das ist so ziemlich das Letzte, was ich als Anwender wollen würde.
VOLLE ZUSTIMMUNG, ganz klar, dies steht nicht zur Debatte, ...
... aber ...
der Anwendungsfall betrifft einen INSTALLER Context, das laden von remote Content soll nicht im "normalen" Anwendungsfall erfolgen.
...und...
die Feststellung das laden von remote content evil ist beantwortet nicht meine Frage warum eval evil ist (ok, ich habe das Anwendungsbeispiel gebracht und setze es bei meiner Fragestellung vorraus das der Fall nunmal gegeben ist).

Ich würde es für mich so formulieren: Evil ist nicht narrensicher.
Streng genommen ist es aber genauso evil wie
include 'etwas.php';
oder
#!/usr/bin/env php

Ich möchte hier keinesfalls eine Kaugummidebatte über längst besprochenes anzetteln, der Titel sagt es schon, aber was wäre wenn die php developer wegen solcher Pauschalisierungen (eval=evil) auf die Idee kämen eval als deprecated zu markieren und zu verbannen, was wäre das, wärst Du dann damit zufrieden? (*)

Eval tut doch nichts anderes als einen String als ausführbaren Code zu parsen, das tue ich doch eh schon indem ich eine php Datei ausführe.
Verglichen mit einer "echten" Programmiersprache stelle ich es mir so vor, daß vor der Ausführung von Code halt eine Compillierung stattfindet shell_execute(saveToFile(Compile('Hello world')));

Um das alles mit einem Satz zu sagen:
(*) - Was soll ich denn mit einer Programmiersprache in welcher ich NICHT beliebeigen Code ausführen kann?
Antworten
#6
Für mich stellt sich die Frage auch generell nicht, wenn sogar die offizielle Doku von der Verwendung abrät bzw. diese nicht empfiehlt:
Doku-Zitat: http://php.net/manual/de/function.eval.php
Achtung Das eval()-Sprachkonstrukt ist sehr gefährlich, weil es die Ausführung von beliebigem PHP-Code erlaubt. Seine Verwendung wird daher nicht empfohlen. Wenn sorgfältig überprüft wurde, dass es keine andere Möglichkeit gibt als dieses Konstrukt zu verwenden, ist besonders darauf zu achten keine von Nutzern bereit gestellten Daten zu übergeben ohne diese zuvor ordnungsgemäß zu validieren.
Doku-Zitat: http://php.net/manual/de/function.eval.php#44008

Rasmus Lerdorf schrieb:If eval() is the answer, you're almost certainly asking the
wrong question. -- Rasmus Lerdorf, BDFL of PHP

Es gibt Dinge, die hinterfrage ich dann auch irgendwann nicht mehr.
Big Grin
Antworten
#7
Zitat:Für mich stellt sich die Frage auch generell nicht, wenn sogar die offizielle Doku von der Verwendung abrät bzw. diese nicht empfiehlt
Es gibt Dinge, die hinterfrage ich dann auch irgendwann nicht mehr.
Tja, ich bin halt manchmal ein Philosoph und stelle mir solche Fragen!

Anmerkung: Mein obiger Awendungsfall existiert als Schnittstelle, ich mache aber so gut wie keinen Gebrauch davon zur Zeit.

Deine obigen Zitate sind zwar GUTE Argumente, wiederlegen meinen Standpunkt
Zitat:eval ist entsprechend sicher oder unsicher, als wie es vom Programmierer verwendet wird.
m.E. aber nicht vollständig!?

Wie dem auch sei, die Frage ist beantwortet, ist keine richtige Frage und führt auch so zu nichts.
Dennoch eine Anmerkung wert wie ich finde, schliesslich mutet es seltsam an eine Funktion auszuliefern gleich mit dem Hinweis sie KEINESFALLS zu verwenden.
Antworten
#8
eval() stammt noch aus PHP4-Zeiten.
Ich könnte mir vorstellen, daß der Einsatz mal berechtigt war, bis bessere und sicherere Vaianten implementiert bzw. möglich waren.
Daß die die Funktion von vornherein mit dem Hinweis "Bitte nicht verwenden" implementiert ahben, halte ich doch für sehr sehr unwahrscheinlich Wink

Gruß Arne
Antworten
#9
Till schrieb:Ich würde es für mich so formulieren: Evil ist nicht narrensicher.

Das ändert aus meiner Sicht aber nichts an der Tatsache, dass mit eval benennbare Eigenschaften und Konzepte verknüpft sind, von deren Nutzung in nahezu allen Fällen klar abzuraten ist (siehe etwa mein erster Beitrag). Das finde ich in „narrensicher“ nicht ausgedrückt, weil bei Programmierung meines Erachtens prinzipiell wenig wirklich narrensicher ist.

Ich bin jetzt sicherlich niemand, der auf das Attribut „evil“ bestehen würde, aber ich finde es richtig, sehr explizit zu betonen, dass eine Verwendung dieses Konstrukts höchstwahrscheinlich nicht die beste Idee ist, um eine Sache umzusetzen.

Zitat:Streng genommen ist es aber genauso evil wie
include 'etwas.php';
oder
#!/usr/bin/env php

Wenn wir die übliche Nutzung der Konstrukte voraussetzen, besteht ein Unterschied darin, dass eval Code ausführt, der in einem dynamisch befüllten String vorliegt. Includes und die #!-Zeile beziehen sich jeweils auf einfache Dateien, die statisch auf dem lokalen System verfügbar sind. Damit ist wesentlich weniger Komplexität verbunden als mit eval. Eigentlich vergleicht man da aber auch Äpfel und Birnen und Trauben.

Zitat:aber was wäre wenn die php developer wegen solcher Pauschalisierungen (eval=evil) auf die Idee kämen eval als deprecated zu markieren und zu verbannen, was wäre das, wärst Du dann damit zufrieden?

Ich jetzt? Ich kann mich nicht erinnern, etwas in der Richtung gefordert zu haben.

Ich habe hier gepostet, weil du darum gebeten hast, dass dir jemand noch mal „erklärt“, worin die Nachteile bei der Verwendung von eval bestehen, die zu der Floskel „eval is evil“ führen. Das habe ich versucht.

Ich persönlich habe kein Problem mit der Existenz dieses Konstrukts. Das heißt aber nicht, dass es gemeinhin eine gute Idee ist, es einzusetzen. Das wiederum heißt aber nicht, dass kategorisch auszuschließen ist, dass es (mehr oder weniger) legitime Anwendungen für eval gibt oder dass es zumindest in manchen Fällen nicht ohne geht.

Ich weiß leider nicht, wie ich das verständlicher formulieren soll. Vielleicht ein anderes Beispiel: Ich habe auch kein Problem damit, dass es möglich ist, Singletons zu erstellen. Ich halte dennoch die Nutzung in der Regel für keine gute Idee.

Du brauchst dir aber sicherlich keine Gedanken darüber zu machen, dass eval entfernt werden könnte. Das halte ich für sehr unwahrscheinich.

Zitat:Eval tut doch nichts anderes als einen String als ausführbaren Code zu parsen, das tue ich doch eh schon indem ich eine php Datei ausführe.

Vielleicht könnte man einen Vergleich zu variablen Variablen ziehen. Klar kann man die einsetzen, aber auch bei ihnen wird davon abgeraten, weil es eben verwirrend/umständlich/kompliziert ist und besser und einfacher geht.
Antworten


Gehe zu: