Exécuter du Lua dans du PHP à l'aide d'une sandbox

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : securite lua php web sandbox

Un projet (personnel) sur lequel je travaille en ce moment consiste à réaliser un jeu de rôle en ligne (autrement dit, un « webgame »). Devant le nombre d'objets, d'armes, de quêtes ou d'autres choses différentes, il est alors évident que le moteur du jeu se doit d'être le plus flexible possible. En effet, certains objets modifient les règles du jeu, de la même manière que les actions des cartes L5R modifient la résolution des batailles.

Cependant, implémenter ces modificateurs de règle de manière statique, c'est-à-dire en PHP, obligerait en réalité de mélanger les règles générales et les règles particulières de chaque objet, ce qui ne serait pas souhaitable.

La solution consiste alors en l'exécution de petits bouts de code particuliers dans un langage de script facile à manier. Un très bon candidat pour cela est le Lua.

L'avantage du Lua réside dans sa simplicité et la présence de « first-class variables » : c'est-à-dire que n'importe quel objet, y compris une fonction, peut être affectée à une variable. Sa simplicité permet également, avec peut-être une page pour résumer l'API, à des gens qui ne sont pas forcément les développeurs d'exercer leur créativité.

Compiler l'extension PECL sur Gentoo

Tout d'abord, avant de pouvoir s'amuser avec ce langage, il nous faut compiler l'extension PECL lua.

Il s'avère que même s'il est disponible tout prêt sur PHP.net, qu'il n'a pas encore été packagé pour Gentoo.

Heureusement, l'ebuild n'est pas très compliqué, et l'installer ne l'est pas non plus :

  • Téléchargez-le ici : pecl-lua-1.0.0.ebuild
  • Placez l'ebuild dans /usr/local/portage/dev-php/pecl-lua ;
  • Tapez les commandes suivantes en tant que root :
# cd /usr/local/portage/dev-php/pecl-lua
# ebuild pecl-lua-1.0.0.ebuild manifest
  • Vérifiez que vous avez un PORTDIR_OVERLAY="/usr/local/portage" dans votre /etc/portage/make.conf ;
  • Enfin, mergez le paquet à l'aide d'un emerge pecl-lua.

Oui, je sais, je devrais publier cet ebuild sur l'overlay Sunrise, mais j'ai pas le temps là.

Il se pourrait que vous devrez ajouter votre architecture aux KEYWORDS avant de pouvoir merger le paquet. N'hésitez pas à me signaler si cet ebuild fonctionne sur d'autres architectures ; je vous en serais reconnaissant !

Le bac à sable

Maintenant que nous avons installé l'environnement, nous pourrons exécuter du code Lua à l'intérieur de l'interpréteur PHP.

Cependant, face à la possibilité d'exécuter du code arbitraire, et vu les risques que cela implique, nous allons exécuter ce code Lua dans un environnement contrôlé, ou « sandbox » (bac à sable) ; en particulier, nous désactiverons les fonctions suivantes :

  • os.setlocale, afin d'empêcher les bugs ou failles exploitées à l'aide d'un changement de locale ;
  • os.execute, afin d'empêcher l'exécution de commandes arbitraires ;
  • os.getenv, afin d'empêcher la fuite d'informations sensibles sur le serveur ;
  • enfin, toutes les fonctions de la famille debug.*, afin d'interdire les bidouilles avec l'interpréteur Lua.

Preuve de concept

En guise de preuve de concept et afin de tester l'intégration de Lua dans PHP, j'ai codé le script PHP suivant. Il affiche les caractéristiques d'un personnage fictif, et permet ensuite de les modifier en exécutant le code Lua adéquat.

<?php

$perso 
= array(
    
'nom'     => 'Herp Derp',
    
'sexe'    => 'M',
    
'race'    => 'Humain',
    
'origine' => 'Détessay',
    
'bru'     => 10,
    
'fin'     => 12,
    
'dis'     => 17,
    
'con'     => 13,
);

$perso_b $perso;

$lua_disabled_funcs = array(
    
'os.setlocale',    /* This potentially breaks things */
    
'os.execute',      /* Potentially very dangerous */
    
'os.getenv',       /* Might leak too much info about server */
    
'debug',           /* Disable debugging stuff */
);


function 
dump_perso($perso) {
    echo 
"<pre>\n";

    echo 
htmlspecialchars($perso['nom']) . "\n\n";
    echo 
htmlspecialchars($perso['race']) . ", ";

    switch(
$perso['sexe']) {
        case 
'M': echo "homme"; break;
        case 
'F': echo "femme"; break;
        default: echo 
"sexe inconnu"; break;
    }
    echo 
", " $perso['origine'] . "\n\n";

    echo 
"Brutalité :    " htmlspecialchars($perso['bru']) . "\n";
    echo 
"Finesse :      " htmlspecialchars($perso['fin']) . "\n";
    echo 
"Discipline :   " htmlspecialchars($perso['dis']) . "\n";
    echo 
"Constitution : " htmlspecialchars($perso['con']) . "\n";

    echo 
"</pre>\n";
}

?>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>test lua</title>
<style type="text/css">
.float {
    float: left;
    width: 40%;
}
</style>
</head>
<body>


<form method="POST" action="<?php print $_SERVER['SCRIPT_NAME'?>">
    <textarea id="lua-code" name="lua-code" style="min-width: 30em; min-height: 20em;">
        <?php if (isset($_POST['lua-code'])) { print $_POST['lua-code']; } ?>
    
</textarea>
    <br />
    <input type="submit" value="Exécuter" />
</form> 

<div class="float">
    <h1>Avant</h1>
    <pre><?php dump_perso($perso_b); ?></pre>
</div>

<?php
if (isset($_POST['lua-code'])) {
    
/* Set up Lua environment */
    
$l = new lua();

    
/* Set some variables from outside */
    
$l->assign("perso"$perso);

    
/* Deactivate dangerous functions */
    
foreach ($lua_disabled_funcs as $function) {
        try {
            
$l->eval("${function} = nil");
        } catch(
LuaException $e) {
            echo 
"$function: " $e->getMessage();
        }
    }

    
/* Run user code */
    
try {
        
$l->eval($_POST['lua-code']);
    } catch (
LuaException $e) {
        echo 
"user code: " $e->getMessage();
    }
?>

<div class="float">
    <h1>Après</h1>
    <pre><?php dump_perso($l->perso); ?></pre>
</div>
<?php
}
?>


</body>
</html>

Le code le plus intéressant se trouve plutôt vers la fin du script, et illustre comment on peut faire passer des données de l'environnement PHP vers l'environnement Lua, et vice-versa.

Et quand on exécute la page, ça marche :

Démo Lua en PHP

En conclusion, ce système est bien sympathique, et je suis content d'avoir pu expérimenter avec ça. Cela dit, pendant que je rédigais l'article, je suis passé à du Perl/Catalyst pour le projet en question (la même techno que ce qui fait tourner ce site), parce que Zend me donnait des boutons.

Commentaires

Poster un commentaire

Poster un commentaire