0
Posted by alex on Sep 1st, 2008

En php, comme dans la plupart des langages, lorsque nous effectuons des fonctions récursives (s'appelant elle même) on arrive vite à un dépassement de la pile. Dans un langage tel que le C ce n'est pas très important, on peut très facilement éviter/prévoire cela. Dans une application en PHP aussi c'est facile d'éviter les fonctions récursives s'appelant elle même des dizaines de milliers de fois.

Cependant, dans le cas d'un hébergement vous ne pouvez évidement pas vérifier tous les scripts de votre serveur. Le problème est que certains membres pourraient s'en servir pour causer un déni de service. En effet lorsque PHP est utilisé comme module apache, en effectuant un dépassement de la pile, il se produit une faute de segmentation et cela tue le processus entier. Si c'est un serveur multithreader alors tout le serveur meurt, si c'est en prefork alors le fils meurt et éventuellement le serveur au complet pourrait mourrir dans le cas d'une attaque répété. Même si vous n'êtes pas hébergeur, avec un script tel que phpmyadmin ou peu importe il est facile d'envoyer des milliers de variables POST qui sont lue récursivement par PHP (magic_quotes, etc) !

Je n'entrerai pas dans les explications de bas niveaux, je vais plutôt vous expliquer comment produire un crash de PHP :)  et éventuellement comment le corriger.

En C vous pouvez essayer un code du genre:

 

#include <stdio.h>
void a(int i){
printf("Stack: %d\
",i);
i++;
a(i);
}

int main( int argc, char **argv ) {
a(0);
}


Lorsque la pile va être pleine, vous allez avoir une faute de segmentation. Le nombre d'appels récursif dépend de votre système (surtout de la grosseur du programme en mémoire), dans mon cas ça tourne aux alentours de 392200 à 392940. Vous pouvez évidement utiliser un autre type qu'un entier, mais vous allez arriver au même résultat.

En php pour produire une faute de segmentation il suffit de faire ceci:

function a($i) {
echo "$i\
";
$i++;
a($i);
}

a(0);

Pour php 5.2.6 sous linux 2.6.25 le dépassement de la pile se produit entre 23354 et 39291 appels.

 

Comment s'en protéger ?

Il n'y a pas des centaines de façons:

  1. Contrôler les scripts du serveur.
  2. Utiliser l'extension Xdebug
  3. Patcher PHP.

La première méthode étant hors de question dans le cas d'un hébergement, la façon la plus stable est sans doute d'utiliser une extension tel que Xdebug qui permets de limiter la récursion. (Voir la documentation, en particulier la directive xdebug.max_nesting_level. Je le mentionne pour les paresseux :) )

Par ailleurs j'ai commencé à travailler sur un patch qui limite le nombre d'appel récursif juste avant qu'une faute de segmentation se produise. Les premiers résultats sont très concluants, mais je n'ai pas encore eu la chance de tester tous les cas de figure possible. Je commence à peine à comprendre l'api Zend sur l'exécution interne de fonction. Il est donc fonctionnel, mais hautement expérimental.

Vous pouvez avoir le patch ICI, mais je ne conseille pas de l'utiliser sur un environnement en production pour le moment, je viens de le commencer et, je me répète, il est hautement expérimental.

Dernière note: Ce script n'a pas pour but de protéger la pile au complet, par conséquent si vous mettez une limite qui fonctionne dans un script de test, dans un vrai script d'autres éléments peuvent très bien prendre la place et causer un overflow si une récursion est appelé en fin de script. Pour éviter ce genre de situation il suffit simplement de mettre une limite de récursion plus basse, 10000 par exemple. (Je doute beaucoup que vous ayez besoin de beaucoup plus que 100 ou 200 récursions :| )

Le patch ajoute une directive au php.ini "e3bsecurity.call_stack_size" par défaut sa valeur est 20000. Une valeur de 0 désactive la protection.

Pourquoi s'en protéger ?

Principalement pour éviter les attaques de déni de service. Et puis c'est jamais très bien un programme qui cause des fautes de segmentation, surtout sur un serveur de production !

Référence: http://www.php-security.org/MOPB/MOPB-02-2007.html

0 comment
0
Posted by alex on Aug 27th, 2008

PhpSysInfo est un script permettant de générer un résumé de l'état et des périphériques du serveur.

Je vais garder cet article court afin de ne pas vous décourager (les derniers paragraphes sont complémentaires, pas besoin de les lires ;) ) . C'est pourquoi je n'aborderai pas les pour/contre d'une chroot ou d'une bsd jail, je vais simplement expliquer plusieurs méthodes pour faire fonctionner phpsysinfo à l'intérieur d'une prison.

 

Voici donc la première méthode:

  1. Télécharger phpsysinfo et décompresser l'archive dans vos documents apache.
  2. Renommez index.php en cron.php
  3. Exécutez chattr +i -R *.php
  4. Créer un cron qui s'execute à chaque minute. Executez crontab -e et inscrivez:
  5. * * * * * cd /chroot/httpd/home/phpsysinfo/ && php -n cron.php >index.html

N'oubliez pas de changer le chemin en rose.

Note: Pour plus de sécurité je recommande de placer les scripts de phpsysinfo à l'extérieur de la prison chroot et de garder seulement les répertoires images et templates dans la prison. Parce que si le serveur est compromis et vos fichiers altéré, il seront exécuter à l'extérieur de la prison ! Le chattr sert à réduire ces risques !

Inconvénients/Limitations:

  1. Le script est généré à chaque minute, peu importe si quelqu'un le consulte ou pas (Une charge inutile).
  2. Les informations ne sont pas en temps réel.
  3. On ne peut pas changer de thème sur la page de phpsysinfo, seul le thème par défaut fonctionnera.

Et voilà, à chaque minute votre phpsysinfo sera mis à jour :)

Plus d'informations/Prochains articles:

À force de réfléchir à des solutions, j'en suis venu à la liste d'idées suivante pour faire fonctionner phpsysinfo:

  1. Faire des hard-links de tous les devices, programmes et librairies dont phpsysinfo à besoin à l'intérieur de la chroot. Peu sécuritaire.
  2. Modifier phpsysinfo pour qu'il prenne ses informations dans des fichiers textes qu'un cron aurait généré. Compliqué pour rien.
  3. Créer un programme qui écoute sur un socket unix et qui génère le contenu de la page phpsysinfo sur demande (le programme étant à l'extérieur de la prison, mais le socket étant à l'intérieur).
  4. Même chose que point #3 mais avec un socket tcp.
  5. Simplement générer le contenu de la page à l'aide d'un cron à l'extérieur de la prison.

J'ai rapidement éliminé la première idée pour des raisons de sécurité. J'ai également éliminé la deuxième méthode parce que c'est trop compliqué pour rien.

Il me restait deux solutions: Un programme qui écoute à l'extérieur de la prison et génère la page sur demande ou bien un cron qui génère le contenu de la page à intervalle régulier. Vous l'aurez deviné, le gros avantage des méthodes 2 et 3 est que la page va être généré en temps réel et seulement au besoin, l'utilisateur n'y verra que du feu. La cinquième méthode a l'avantage d'être très très facile à mettre en œuvre :)  mais elle va représenter un décalage de 60 secondes avec la réalité.

Note: Cette suite d'articles s'adresse aussi à ceux ayant désactivé des fonctions de php ou ayant mis un open_basedir empêchant phpsysinfo de fonctionner !

0 comment
0
Posted by alex on Jun 16th, 2008

J'ai récemment fait l'acquisition d'un nouvel ordinateur portable équipée d'un processeur Core 2 Duo d'intel (architecture Santa Rosa Refresh). Après quelques jours d'utilisation sur l'alimentation secteur je décide de le débrancher afin de tester les limites de mon réseau sans fil. C'est à ce moment que le drame se produisit: Un petit sifflement (comme des milliers de beeps très rapides; un peu comme du code morse accéléré) venait de l'ordinateur !

Au début je n'y faisait pas trop attention, étant habitué à des cartes graphiques haut de gamme je savais très bien que du matériel informatique pouvait émettre un léger sifflement lorsqu'il est chargé, mais ça devint très vite agaçant. Le sifflement était présent seulement lorsque l'ordinateur fonctionnait sur la batterie et lorsque qu'il ne travaillait pas (idle ou presque). J'ai aussi remarqué que lorsqu'un périphérique usb était branché le cillement diminuait nettement. Sachant que Windows désactive les modes de veille profonde pour le CPU (C3 et C4) (correction: à vrai dire non il ne les désactive pas, mais plutôt il n'arrête jamais vraiment le cpu) lorsqu'un périphérique usb est présent, j'en ai donc déduit que c'était un problème au niveau de la gestion de l'énergie.

Après quelques recherches j'ai découvert que c'était généralisé à tous les ordinateurs utilisant la plateforme Centrino. Autant sur les ordinateurs d'entrée de gamme que ceux haut de gamme.

Pour ceux qui se demande ce dont je parle voila: http://www.youtube.com/watch?v=ErkhU2qK-fM

Dans mon cas c'était beaucoup moins fort.

Je suis parvenu à trouver plusieurs solutions à mon problème. Mais aucune n'est totalement satisfaisante pour le moment... Enfin je vous fait un petit résumé, ça peut vous aider !

Solution 1:

Si possible désactiver le mode C4 dans le BIOS.

+: Le sifflement arrête totalement. C'est une solution dans le BIOS, donc relativement sécuritaire.

-: Le processeur chauffe légèrement plus (puisqu'il dort moins, dans mon cas je vois une augmentation(en idle) de près de 4-5 degré, c'est loin d'être satisfaisant). L'autonomie de la batterie est légèrement réduite (en théorie, dans mon cas j'atteins approximativement le même temps qu'avant. Disons que dans le pire des cas ça va vous amputer de 4 à 8% de la durée de vie de votre batterie)

Solution 2:

Si votre bios ne permet pas de désactiver le mode C4 vous pouvez alors utiliser un petit logiciel qui va vous permettre de le faire. Ce programme s'appelle RMClock et existe en version gratuite.

Une fois le logiciel installé il faut aller dans la section "Advanced CPU Settings" -> Onglet Chipset -> Cocher "Disable C4 mode"

Appuyer sur "Apply" et le sifflement devrait disparaitre instantanément !

+:Solution efficace et simple

-: Même chose que solution 1 et en plus c'est relativement instable puisque le OS pense que le mode C4 est toujours disponible, un plantage peut survenir à tout moment. On s'encombre d'un logiciel. De plus il provoque des plantages aléatoire sur certain ordinateurs. Et évidement on doit être sous windows pour l'utiliser.

Solution 3:

Cette solution est identique à la solution 2 mais à la place de cocher "Disable C4 mode" nous allons décocher "Enable popup mode" et "Enable popdown mode"
+: Semble être plus stable que la solution 2 et sans gâcher autant de d'autonomie. Je n'ai pas tester profondément mais ça ne semble pas faire chauffer le processeur d'avantage (notez quand même une différence d'un ou deux degré).

-: C'est une solution logiciel qui ne fonctionnera que sous Windows. Avoir ces options dans le BIOS serait un très grand avantage ! Espérons que les constructeurs y pensent. Toujours une petite perte d'autonomie mais rien de catastrophique.

Solution 4:

Si vous êtes sous linux, il est possible de recompiler le kernel sans le support du mode C4 ou de le patcher.

+: C'est une solution propre, pas de logiciels supplémentaires, pas de changement dans le BIOS.

-: C'est peut être un peu compliqué à mettre en œuvre pour la plupart d'entre nous puisque cela implique une recompilation du kernel. Il est toutefois possible que la désactivation du mode c4 puisse se faire en run-time, c'est à dire pendant que le système fonctionne sans rien recompiler, mais je n'ai pas trouver comment.

Conclusion

Comme je le disais, aucune solution ne me satisfait totalement. La dernière solution semble être la meilleure, mais je déteste devoir m'encombrer d'un logiciel qui va faire ça à chaque démarrage. Bref la troisième solution mais ajustable depuis le BIOS ferait mon bonheur. En attendant je vie avec le bruit et lorsque je dois travailler dans un endroit plus calme je désactive le mode C4 depuis le bios (mais je vais tester d'avantage la troisième solution prochainement pour vous en donner des nouvelles)

Annexe:

Popup/Popdown: Cela signifie un changement progressif du mode de veille du processeur. Par exemple si le cpu est en mode C4, avec Popup activé il va passer par C3,C2 avant de retourner en C1.

0 comment
1
Posted by alex on May 6th, 2008

Pour un débutant, iptables peut être extrêment difficle à maitriser puisqu'il implique soit: de bien connaitre les différentes couches d'un réseau et les techniques de routage de base, soit de connaitre les arguments par coeur sans forcément les comprendres.

Je vais donc rassembler ici des petits exemples de commandes qui pourraient éventuellement vous être utiles. Si vous essayez de faire une règle de routage, de rebond, de redirection ou de dénie qui ne se trouve pas dans la liste, dites le moi et si j'ai le temps j'essayerai de l'écrire pour vous, et de l'ajouter dans cette liste.

C'est quoi Iptables ?

Iptables est une application en ligne de commande qui sert à configurer les règles du filtre de paquets du kernel de Linux (depuis 2.4). Il peut être utilisé autant comme firewall(plutot pour appliqué les règles du firewall) que pour configuré les NAT (Network Address Translation: la réécriture des headers des paquets et le réacheminement de ceux-ci. Du routage quoi.)

1. Interdire les connexions entrantes d'une certaine IP (Bannir)

bash-3.1# iptables -A INPUT -s 255.255.255.255 -j DROP

2. Interdire les connexions sortantes vers une certaine IP

bash-3.1# iptables -A OUTPUT -d 255.255.255.255 -j DROP

3. Bloquer tous les genres de requêtes ICMP [ping,trace,echo request, etc]

C'est dans un but de sécurité (ça réduit nettement l'impact d'une attaque DoS icmp (ping flood)) Évite de deviner le OS du système. Plusieurs autres avantages..

bash-3.1# iptables -t filter -A INPUT -p icmp -i eth0 -j DROP

4. Bloquer les connexions sortantes sur un port et une ip en particulier.

Dans cette exemple les connexions sortantes de 1.2.3.4 (ip local,source) vers n'importe quel IP extérieur sur le port 25 seraient refusées.

bash-3.1# iptables -A OUTPUT -p tcp --dport 25 -s 1.2.3.4 -j DROP

5. Routage des paquets d'un port à un autre (ou d'une ip à une autre)

Si vous avez plusieurs IP ou souhaitez rediriger des ports cette commande est pour vous.

Prenons comme exemple que votre application en local écoute sur le ip:port 1.2.3.4:25, et vous souhaitez que les clients puissent s'y connecter depuis un autre port mais sans forcer l'application à écouter sur deux ports. Avec la ligne ci-dessous les clients de l'extérieur pourront s'y connecter depuis le ip:port 1.2.3.4:25 ET 1.2.3.4:250.

bash-3.1# iptables -t nat -A PREROUTING -d 1.2.3.4 -p tcp --dport 250 -i eth0 -j DNAT --to 1.2.3.4:25

6. Voir toutes les règles de iptables

bash-3.1# iptables --list

7. Flusher les règles de iptables (effacer toute les règles)

bash-3.1# iptables --flush

Read more...
1 comment
Go to Top