flawopen.com/SSTI/FreeMarker e Velocity
FreeMarker e Velocity são seguros com entrada do usuário?
Valores fornecidos pelo usuário continuam sendo dados quando inseridos em um template confiável. Já permitir que o usuário controle o código-fonte do template permite escrever expressões na linguagem do mecanismo. O alcance depende da versão, do wrapper ou introspector, dos objetos no modelo e das permissões da aplicação. Não presuma que toda injeção de template executa comandos do sistema; trate código de template não confiável como lógica executável e mantenha sua renderização fora do processo da aplicação.
// Risco: analisar entrada da requisição como template String src = "Hello " + userSuppliedName; Template t = new Template("t", src, cfg); t.process(model, writer); // Risco: avaliar uma string VTL controlada pelo usuário Velocity.evaluate(context, writer, "log", userTemplate);
// Template confiável do classpath; modelo só com escalares 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: template confiável e introspecção restrita (defesa em profundidade) 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);
Por que mecanismos Java podem expor capacidades perigosas
Os templates avaliam expressões sobre um modelo de dados, mas a superfície Java disponível depende da configuração. O wrapper padrão do FreeMarker pode expor propriedades e métodos JavaBean de POJOs arbitrários; os membros acessíveis dependem do wrapper e da política de acesso. O Uberspect padrão do Velocity mapeia propriedades VTL para métodos Java públicos. Isso não significa que qualquer modelo dê acesso irrestrito à reflexão: o risco aumenta quando o modelo contém objetos de serviço, carregadores de classes, objetos de requisição ou outras capacidades poderosas. Prefira mapas de strings e valores simples e exponha apenas os campos necessários a cada template confiável.
O built-in ?new do FreeMarker pode instanciar classes que implementam TemplateModel. O Apache recomenda restringir a resolução de classes para templates enviados por usuários; SAFER_RESOLVER ainda permite algumas classes, enquanto ALLOWS_NOTHING_RESOLVER nega toda resolução. Esse controle não substitui um wrapper restrito e um modelo de dados mínimo.
Como reduzir o risco e conhecer os limites
- ✓FreeMarker: para templates não confiáveis, defina
new_builtin_class_resolvercomoALLOWS_NOTHING_RESOLVERe use um wrapper comoSimpleObjectWrapper, que rejeita POJOs arbitrários. Forneça apenas valores simples e específicos para a finalidade. - ✓Velocity: use
SecureUberspectorcom templates não confiáveis e inclua no contexto somente os valores simples necessários. Ele bloqueia caminhos documentados de reflexão e criação de objetos; não é um sandbox do sistema operacional. - ✓Ambos: carregue templates da aplicação por uma origem confiável e protegida contra alterações. Nunca transforme parâmetros de requisição ou linhas editáveis pelo usuário em código-fonte de template.
- ✓Se templates escritos por usuários forem requisito do produto: renderize-os em processo separado com privilégios mínimos e sem rede, segredos ou arquivos graváveis da aplicação; imponha limites de CPU, memória e tempo fora do mecanismo.
Trate as restrições do mecanismo como defesa em profundidade, não como isolamento de processo. Use versões suportadas, confira a configuração exata na orientação atual do mecanismo e mantenha o renderizador longe de segredos e objetos privilegiados.
Perguntas frequentes
É um risco relacionado, mas distinto. Um nome de view controlado pelo usuário não é automaticamente uma falha de injeção de expressão. Verifique o fluxo de dados específico até a avaliação de Thymeleaf ou Spring Expression Language e não construa fragmentos de expressão com entrada não confiável. Mantenha as integrações atualizadas e selecione views pela lógica confiável da aplicação.
Autores de templates podem usar as capacidades expostas pelo mecanismo e pelo modelo de dados; isso nem sempre significa execução de comandos do sistema operacional. Trate a permissão de editar templates como privilégio sensível de autoria de código, restrinja-a a mantenedores confiáveis e registre as alterações.
Um mecanismo sem lógica, como Mustache, pode reduzir a superfície de expressões e chamadas de métodos, mas codificação da saída, controle da origem dos templates e capacidades dos helpers continuam importantes. Escolha um mecanismo projetado para esse modelo de confiança e isole a renderização; não use um mecanismo JVM de uso geral como sandbox.
Fontes oficiais e referência
- Apache FreeMarker — FAQ sobre segurança ao permitir envio de templates
- Apache FreeMarker — API SimpleObjectWrapper
- Apache FreeMarker — API TemplateClassResolver
- Apache Velocity — guia do desenvolvedor
- Apache Velocity 2.3 — API ClasspathResourceLoader
- PortSwigger — Server-Side Template Injection
- CWE-1336 — Server-Side Template Injection