FlawOpen

flawopen.com/SSTI/FreeMarker et Velocity

Les modèles FreeMarker et Velocity sont-ils sûrs avec des entrées utilisateur ?

CWE-1336: Server-Side Template InjectionPage de référence
Réponse courte

Les valeurs fournies par l’utilisateur restent des données lorsqu’elles sont insérées dans un modèle de confiance. Une source de modèle contrôlée par l’utilisateur est différente : elle permet d’écrire des expressions dans le langage du moteur. La portée dépend de la version, du wrapper ou de l’introspecteur, des objets du modèle et des privilèges de l’application. Ne supposez pas que toute injection exécute des commandes système ; traitez une source de modèle non fiable comme de la logique exécutable et gardez son rendu hors du processus applicatif.

VULNÉRABLE
// Risque : analyser une donnée de requête comme modèle
String src = "Hello " + userSuppliedName;
Template t = new Template("t", src, cfg);
t.process(model, writer);

// Risque : évaluer une chaîne VTL contrôlée par l’utilisateur
Velocity.evaluate(context, writer,
                  "log", userTemplate);
CORRIGÉ
// Modèle du classpath fiable ; modèle de données avec scalaires
Configuration cfg = new Configuration(
    Configuration.VERSION_2_3_34);
cfg.setTemplateLoader(new ClassTemplateLoader(
    App.class, "/templates"));
cfg.setNewBuiltinClassResolver(
    TemplateClassResolver.ALLOWS_NOTHING_RESOLVER);
cfg.setObjectWrapper(new SimpleObjectWrapper(
    Configuration.VERSION_2_3_34));
Template t = cfg.getTemplate("hello.ftl");
Map<String,Object> model = Map.of(
    "name", userSuppliedName);
t.process(model, writer);

// Velocity : modèle fiable et introspection restreinte (défense en profondeur)
Properties p = new Properties();
p.setProperty("resource.loaders", "class");
p.setProperty("resource.loader.class.class",
    ClasspathResourceLoader.class.getName());
p.setProperty("runtime.introspector.uberspect",
    SecureUberspector.class.getName());
VelocityEngine engine = new VelocityEngine();
engine.init(p);
Template vt = engine.getTemplate("hello.vm", "UTF-8");
VelocityContext vc = new VelocityContext();
vc.put("name", userSuppliedName);
vt.merge(vc, writer);

Pourquoi les moteurs Java peuvent exposer des capacités dangereuses

Les modèles évaluent des expressions sur un modèle de données, mais la surface Java accessible dépend de la configuration. Le wrapper par défaut de FreeMarker peut exposer les propriétés et méthodes JavaBean de POJO arbitraires ; les membres accessibles dépendent du wrapper et de la politique d’accès. L’Uberspect par défaut de Velocity associe les propriétés VTL aux méthodes Java publiques. Cela ne signifie pas que tout modèle donne accès à une réflexion sans restriction : le risque augmente si le modèle contient des services, des chargeurs de classes, des objets de requête ou d’autres capacités puissantes. Préférez des cartes de chaînes et des valeurs simples ; n’exposez que les champs nécessaires au modèle de confiance.

Le built-in ?new de FreeMarker peut instancier des classes qui implémentent TemplateModel. Apache recommande de restreindre la résolution de classes pour les modèles téléversés ; SAFER_RESOLVER autorise encore certaines classes, tandis que ALLOWS_NOTHING_RESOLVER bloque toute résolution. Cela ne remplace ni un wrapper restreint ni un modèle de données minimal.

Réduire le risque et connaître les limites

Considérez les restrictions du moteur comme une défense en profondeur, pas comme une isolation de processus. Utilisez des versions maintenues, vérifiez la configuration exacte selon la documentation actuelle et éloignez le moteur des secrets et des objets privilégiés.

Questions fréquentes

Thymeleaf est-il aussi concerné ?

C’est un risque proche, mais distinct. Un nom de vue contrôlé par l’utilisateur ne constitue pas automatiquement une injection d’expression. Vérifiez le flux précis des données vers l’évaluation Thymeleaf ou Spring Expression Language et ne construisez pas de fragments d’expression depuis des entrées non fiables. Maintenez les intégrations à jour et sélectionnez les vues dans la logique fiable de l’application.

Seuls les administrateurs peuvent modifier les modèles. Est-ce acceptable ?

Les auteurs de modèles peuvent exercer les capacités exposées par le moteur et le modèle de données ; cela ne signifie pas toujours qu’ils peuvent exécuter des commandes système. Traitez le droit de modifier les modèles comme un privilège sensible de création de code, limitez-le aux mainteneurs de confiance et auditez les changements.

Quel moteur est le plus sûr pour les modèles fournis par les utilisateurs ?

Un moteur sans logique comme Mustache peut réduire la surface d’expressions et d’appels de méthodes, mais l’encodage de sortie, le contrôle de la source des modèles et les capacités des helpers restent importants. Choisissez un moteur adapté à ce modèle de confiance et isolez le rendu ; ne comptez pas sur un moteur JVM généraliste comme bac à sable.

Sources officielles et références

À voir aussi SSTI : cause et correction de l’injection de templates render_template_string() de Jinja2 est-il sûr ? Qu’est-ce que l’injection de template côté serveur ?