2008-10-13 8 views
2

J'ai une application PHP qui doit sortir un script python, plus précisément un tas d'instructions d'affectation de variables, par exemple.Quel est le meilleur moyen d'échapper à des chaînes Python en PHP?

subject_prefix = 'This String From User Input' 
msg_footer = """This one too.""" 

Le contenu de subject_prefix et al doit être écrit pour prendre l'entrée de l'utilisateur; en tant que tel, j'ai besoin d'échapper au contenu des cordes. Ecrire quelque chose comme ce qui suit ne va pas le couper; nous sommes bourrés dès que quelqu'un utilise une citation ou saut de ligne ou toute autre chose que je ne suis pas au courant de cela pourrait être dangereux:

echo "subject_prefix = '".$subject_prefix."'\n"; 

So. Des idées?

(Réécriture l'application en Python est pas possible en raison de contraintes de temps. P)

Edition, des années plus tard:

Ce fut pour l'intégration entre une application Web (écrit en PHP) et Mailman (écrit en Python). Je n'ai pas pu modifier l'installation de ce dernier, je devais donc trouver un moyen de parler dans sa langue pour gérer sa configuration.

C'était aussi une mauvaise idée de vraiment.

Répondre

2

Ne pas Essayez d'écrire cette fonction en PHP. Vous allez inévitablement vous tromper et votre application aura inévitablement un exploit d'exécution à distance arbitraire.

D'abord, réfléchissez au problème que vous résolvez réellement. Je suppose que vous essayez simplement d'obtenir des données de PHP vers Python. Vous pouvez essayer d'écrire un fichier .ini plutôt qu'un fichier .py. Python a un excellent parser de syntaxe ini, ConfigParser. Vous pouvez écrire la fonction de citation la plus évidente et potentiellement incorrecte en PHP et rien de grave ne se produira si (lire: quand) vous vous trompez.

Vous pouvez également écrire un fichier XML. Il y a trop de parseurs et d'émetteurs XML pour PHP et Python pour que je les liste même ici.

Si je vraiment ne peux pas vous convaincre que c'est un terrible, terrible idée, vous pouvez au moins utiliser la fonction pré-existante que Python a pour faire une telle chose: repr().

Voici une fonction PHP pratique qui exécuter un script Python pour le faire pour vous:

<?php 

function py_escape($input) { 
    $descriptorspec = array(
     0 => array("pipe", "r"), 
     1 => array("pipe", "w") 
     ); 
    $process = proc_open(
     "python -c 'import sys; sys.stdout.write(repr(sys.stdin.read()))'", 
     $descriptorspec, $pipes); 
    fwrite($pipes[0], $input); 
    fclose($pipes[0]); 
    $chunk_size = 8192; 
    $escaped = fread($pipes[1], $chunk_size); 
    if (strlen($escaped) == $chunk_size) { 
     // This is important for security. 
     die("That string's too big.\n"); 
    } 
    proc_close($process); 
    return $escaped; 
} 

// Example usage: 
$x = "string \rfull \nof\t crappy stuff"; 
print py_escape($x); 

Le chunk_size chèque est destiné à empêcher une attaque par laquelle se termine votre entrée par être deux chaînes très longues, qui ont l'air comme ("hello " + ("." * chunk_size)) et '; os.system("do bad stuff") respectivement. Maintenant, cette attaque naïve ne fonctionnera pas exactement, car Python ne laissera pas une chaîne entre guillemets se terminer au milieu d'une ligne, et ces guillemets dans l'appel system() seront eux-mêmes cités, mais si l'attaquant réussit à obtenir un ligne continuation ("\") dans le bon endroit et utiliser quelque chose comme os.system(map(chr, ...)) puis ils peuvent injecter du code qui va fonctionner. J'ai opté pour simplement lire un morceau et abandonner s'il y avait plus de sortie, plutôt que de continuer à lire et à accumuler, car il y a aussi des limites sur la longueur de la ligne du fichier source Python; pour tout ce que je sais, ça pourrait être un autre vecteur d'attaque. Python n'est pas destiné à être sécurisé contre les personnes arbitraires qui écrivent du code source arbitraire sur votre système, il est donc peu probable que cette zone soit auditée.

Le fait que j'ai dû penser à tout cela pour cet exemple trivial est juste un autre exemple de pourquoi vous ne devriez pas utiliser le code source python comme format d'échange de données.

+0

Nono, tu m'as convaincu. :) Cette approche devrait avoir soulevé toutes sortes de drapeaux rouges dans ma tête quand j'ai d'abord pensé à cela. Je suis curieux de savoir pourquoi la fonction ci-dessus vérifie que la taille du segment est égale à la longueur de la chaîne; pourriez-vous expliquer cette partie un peu plus loin? –

+0

Je l'ai édité pour ajouter une belle explication longue; J'espère que cela pourra aider. – Glyph

-2

Je suggère d'écrire une fonction qui prendra deux arguments: le texte à échapper et le type de guillemets dans lequel se trouve la chaîne. Ensuite, par exemple, si le type de guillemets est guillemets simples, la fonction échappera au les guillemets dans la chaîne et tous les autres caractères qui doivent être échappés (barre oblique inverse?).

function escape_string($text, $type) { 
    // Escape backslashes for all types of strings? 
    $text = str_replace('\\', '\\\\', $text); 

    switch($type) { 
     case 'single': 
      $text = str_replace("'", "\\'", $text); 
      break; 
     case 'double': 
      $text = str_replace('"', '\\"', $text); 
      break; 
     // etc... 
    } 

    return $text; 
} 

Je suppose que pour les chaînes entre guillemets simples que vous voulez échapper aux guillemets simples, et avec des chaînes entre guillemets que vous voulez échapper aux guillemets doubles ...

+0

Y at-il autre chose qui peut "casser" une chaîne python? Nouvelles lignes? –

+0

Je ne suis pas sûr, je ne connais pas très bien Python. –

0

Je commencerais par standardiser le type de chaîne que j'utilisais en python, pour utiliser des chaînes entre guillemets ("" "). Cela devrait réduire les incidents de problèmes de guillemets errants dans l'entrée, mais vous devrez toujours y échapper, mais Ce que j'ai fait pour échapper aux cordes dépendait de ce que je craignais de me faire glisser, et le contexte qu'ils sont imprimés à nouveau. Si vous êtes seulement préoccupé par les citations qui causent des problèmes, vous pouvez simplement vérifier les occurrences de "" "" et les échapper, d'un autre côté, si je m'inquiétais de l'intrication malveillante de l'entrée elle-même.), puis je regarderais des options comme strip_tags() ou d'autres fonctions similaires

0

Une autre option peut être d'exporter les données en tant que tableau ou objet en tant que chaîne JSON et modifier le code python légèrement pour gérer la nouvelle entrée. échapper via JSON est pas à 100%, il sera-balles encore mieux que les routines propres s'échappent.

Et vous serez en mesure de gérer les erreurs si la chaîne JSON est malformatted.

Ther e est un paquet pour Python pour encoder et décoder JSON: python-json 3.4

0

Je devais coder ceci pour échapper une chaîne au format "ntriples", qui utilise python escaping.

La fonction suivante prend une chaîne utf-8 et la renvoie échappée pour python (ou format ntriples). Il peut faire des choses étranges si donné des données utf-8 illégales. Il ne comprend pas les caractères Unicode passés xFFFF. Il n'emballe pas la chaîne entre guillemets (actuellement).

La fonction uniord provient d'un commentaire sur php.net.

function python_string_escape($string) { 
    $string = preg_replace("/\\\\/", "\\\\", $string); # \\ (first to avoid string re-escaping) 
    $string = preg_replace("/\n/", "\\n", $string); # \n 
    $string = preg_replace("/\r/", "\\r", $string); # \r 
    $string = preg_replace("/\t/", "\\t", $string); # \t 
    $string = preg_replace("/\"/", "\\\"", $string); # \" 
    $string = preg_replace("/([\x{00}-\x{1F}]|[\x{7F}-\x{FFFF}])/ue", 
          "sprintf(\"\\u%04X\",uniord(\"$1\"))", 
          $string); 
    return $string; 
} 

function uniord($c) { 
    $h = ord($c{0}); 
    if ($h <= 0x7F) { 
     return $h; 
    } else if ($h < 0xC2) { 
     return false; 
    } else if ($h <= 0xDF) { 
     return ($h & 0x1F) << 6 | (ord($c{1}) & 0x3F); 
    } else if ($h <= 0xEF) { 
     return ($h & 0x0F) << 12 | (ord($c{1}) & 0x3F) << 6 | (ord($c{2}) & 0x3F); 
    } else if ($h <= 0xF4) { 
     return ($h & 0x0F) << 18 | (ord($c{1}) & 0x3F) << 12 | (ord($c{2}) & 0x3F) << 6 | (ord($c{3}) & 0x3F); 
    } else { 
     return false; 
    } 
}