Maison  >  Article  >  développement back-end  >  Sécurité Python : analyse et solutions des vulnérabilités du nouveau format de chaîne

Sécurité Python : analyse et solutions des vulnérabilités du nouveau format de chaîne

PHPz
PHPzoriginal
2017-05-01 09:53:521448parcourir

Récemment, une vulnérabilité de formatage de chaîne Python a attiré mon attention. Aujourd'hui, je vais parler de la vulnérabilité de sécurité d'une nouvelle syntaxe de formatage de chaîne introduite par Python. J'ai mené une analyse approfondie et fourni la solution de sécurité correspondante.

Lorsque nous utilisons str.format pour une saisie utilisateur non fiable, cela entraînera des risques de sécurité - je connais ce problème depuis longtemps, mais je n'en avais pas vraiment réalisé la gravité jusqu'à aujourd'hui. Étant donné que les attaquants peuvent l'utiliser pour contourner le bac à sable Jinja2, cela entraînera de graves problèmes de fuite d'informations. En attendant, je propose une nouvelle version sécurisée de str.format à la fin de cet article.

Il convient de rappeler qu’il s’agit d’un risque de sécurité assez grave. La raison pour laquelle j’écris un article ici est que la plupart des gens ne savent probablement pas à quel point il est facile d’être exploité.

Problème principal

À partir de Python 2.6, Python a introduit une nouvelle syntaxe pour le formatage des chaînes inspirée de .NET. Bien entendu, en plus de Python, Rust et certains autres langages de programmation prennent également en charge cette syntaxe. Cette syntaxe peut être appliquée à la fois aux chaînes d'octets et aux chaînes Unicode (dans Python 3, uniquement aux chaînes Unicode) grâce à la méthode .format(), et elle peut également être mappée à des chaînes plus personnalisables.

Une caractéristique de cette syntaxe est qu'elle permet de déterminer les arguments de position et de mot-clé du format de chaîne et de réorganiser explicitement les éléments de données à tout moment. De plus, il peut même accéder aux propriétés et aux éléments de données de l'objet, ce qui est à l'origine du problème de sécurité ici.

Dans l'ensemble, on peut exploiter cela pour faire les choses suivantes :

>>> 'class of {0} is {0.__class__}'.format(42)
"class of 42 is "

Essentiellement, toute personne ayant le contrôle sur la chaîne de format a le potentiel d'accéder à diverses propriétés internes de l'objet.

Quel est le problème ?

La première question est de savoir comment contrôler la chaîne de format. Vous pouvez commencer à partir des endroits suivants :

1. Traducteur non fiable dans le fichier de chaîne. Nous allons probablement nous en sortir, car de nombreuses applications traduites dans plusieurs langues utilisent cette nouvelle méthode de formatage de chaîne Python, mais tout le monde n'effectuera pas un examen approfondi de toutes les chaînes saisies.

2. Configuration exposée par l'utilisateur. Étant donné que certains utilisateurs du système peuvent configurer certains comportements, ces configurations peuvent être exposées sous la forme de chaînes de format. En guise de remarque spéciale, j'ai vu certains utilisateurs configurer des e-mails de notification, des formats de messages de journal ou d'autres modèles de base via l'application Web.

Niveau de danger

Si vous transmettez simplement l'objet interpréteur C à la chaîne de format, il n'y aura pas beaucoup de danger, car dans ce cas, vous exposerez au plus quelques classes entières.

Cependant, une fois qu'un objet Python est passé à cette chaîne de format, cela devient gênant. En effet, la quantité de données pouvant être exposées à partir des fonctions Python est assez stupéfiante. Voici un scénario pour une application Web hypothétique qui pourrait divulguer la clé :

CONFIG = {
    'SECRET_KEY': 'super secret key'
}
 
class Event(object):
    def __init__(self, id, level, message):
        self.id = id
        self.level = level
        self.message = message
 
def format_event(format_string, event):
    return format_string.format(event=event)

Si l'utilisateur pouvait injecter format_string ici, alors il trouverait quelque chose comme cette chaîne secrète :

{event.__init__.__globals__[CONFIG][SECRET_KEY]}

Formatage du bac à sable

Et si vous avez besoin que quelqu'un d'autre fournisse la chaîne de formatage ? En fait, certains mécanismes internes non documentés peuvent être utilisés pour modifier le comportement de formatage des chaînes.

from string import Formatter
from collections import Mapping
 
class MagicFormatMapping(Mapping):
    """This class implements a dummy wrapper to fix a bug in the Python
    standard library for string formatting.
 
    See http://bugs.python.org/issue13598 for information about why
    this is necessary.
    """
 
    def __init__(self, args, kwargs):
        self._args = args
        self._kwargs = kwargs
        self._last_index = 0
 
    def __getitem__(self, key):
        if key == '':
            idx = self._last_index
            self._last_index += 1
            try:
                return self._args[idx]
            except LookupError:
                pass
            key = str(idx)
        return self._kwargs[key]
 
    def __iter__(self):
        return iter(self._kwargs)
 
    def __len__(self):
        return len(self._kwargs)
 
# This is a necessary API but it's undocumented and moved around
# between Python releases
try:
    from _string import formatter_field_name_split
except ImportError:
    formatter_field_name_split = lambda \
        x: x._formatter_field_name_split()
{C} 
class SafeFormatter(Formatter):
 
    def get_field(self, field_name, args, kwargs):
        first, rest = formatter_field_name_split(field_name)
        obj = self.get_value(first, args, kwargs)
        for is_attr, i in rest:
            if is_attr:
                obj = safe_getattr(obj, i)
            else:
                obj = obj[i]
        return obj, first
 
def safe_getattr(obj, attr):
    # Expand the logic here.  For instance on 2.x you will also need
    # to disallow func_globals, on 3.x you will also need to hide
    # things like cr_frame and others.  So ideally have a list of
    # objects that are entirely unsafe to access.
    if attr[:1] == '_':
        raise AttributeError(attr)
    return getattr(obj, attr)
 
def safe_format(_string, *args, **kwargs):
    formatter = SafeFormatter()
    kwargs = MagicFormatMapping(args, kwargs)
    return formatter.vformat(_string, args, kwargs)

Maintenant, nous pouvons utiliser la méthode safe_format pour remplacer str.format :

>>> '{0.__class__}'.format(42)
""
>>> safe_format('{0.__class__}', 42)
Traceback (most recent call last):
  File "", line 1, in
AttributeError: __class__

Résumé :

Il existe un tel dicton dans le développement de programmes : tout Ne faites jamais confiance aux entrées des utilisateurs ! Il semble désormais que cette phrase soit parfaitement logique. Alors étudiants, gardez cela à l’esprit !

【Recommandation de cours】

Tutoriel vidéo en ligne gratuit Python

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn