Desenvolvimento Web

Filtro Anti-XSS em Java

Há um bom tempo, na empresa onde trabalho, enfrentei uma situação curiosa. Eu ainda estagiava e um colega (hoje na globo.com) me perguntou: “quão segura é a aplicação que você está desenvolvendo?”. Eu, muito ingênuo (bem, muito mais do que hoje), respondi que era possível encontrar bugs, mas nada catastrófico a ponto de pôr tudo a perder. Outro colega (talvez mais ingênuo) teve a audácia de dizer que o sistema sob sua responsabilidade era livre de erros de segurança de qualquer tipo. A questão voltou-se completamente contra o programador super-confiante. Todos começaram a pensar em formas de encontrar bugs catastróficos e logo, o primeiro colega, autor da questão inicial, encontrou a brecha. Em poucos minutos, ele mostrou como cadastrar um item num dos cadastros do sistema para que, quando alguém clicasse nesse item, o sistema direcionaria o usuário para o site do google (ele foi bonzinho, poderia direcionar para algo bem pior…). Não preciso mencionar que o responsável pelo sistema ficou sem fala… Logo após, ele mencionou como era fácil encontrar sites expostos a este tipo de ataque e mostrou uma meia dúzia deles. Nos próximos dias comecei a trocar umas idéias com alguns desenvolvedores sobre como defender-se de ataques XSS. Esse post mostra basicamente uma forma de defesa “server-side” contra esse tipo de ataque (mais especificamente em sistemas Java, mas obviamente, a idéia se aplica a muitas outras linguagens).

Primeiro, uma tentativa de definição do que é Cross-Site Scripting:

Cross-Site Scripting (XSS) é um tipo de vulnerabilidade que pode existir em aplicações Web, na qual o ataque começa no lado do cliente, “atravessa” o sistema e permite ao atacante obter informações não permitidas de outros usuários que acessam ou acessaram a aplicação. Isso é feito por meio de injeção de código malicioso dentro das páginas web acessadas pelos usuários.

Ataque XSS

Ataque XSS

Esses ataques normalmente são códigos javascript inseridos por meio de campos de texto da aplicação e é bem fácil de simular. Aqui você encontra como simular um ataque XSS. É bem simples mesmo. Da mesma forma, a solução que eu utilizei é bem simples, é server-side e aqui vai um passo-a-passo de como implementar isso:

1 – A idéia é construir uma classe para tratar a requisição (request) da forma como você deseja. No meu caso, precisávamos tratar requests com tags html, como por exemplo “<script>” e “<a href>”. Um método estático que recebe a String original e retorna uma String modificada (do jeito que você precisa fazer) deve dar conta do serviço. Para casos mais gerais – e acredito que tratar TODAS as tags do tipo “<*>” – aconselho usar expressões regulares.

– Exemplo de classe TratarTags:


public class TratarTags {

    /*Expressão representando todas as expressões com tags html. "?"
    significa não-gulosa(non-greedy).*/
    private static final String expressaoComTags = "<.*?>";

    public static String retirarTags(String html) {

        String retorno = html.replaceAll(expressaoComTags, "");
        return retorno;
    }
}

2 – Construir o filtro propriamente dito. É necessário que você implemente a interface javax.servlet.Filter que consiste dos métodos init, destroy e – chegaremos lá – doFilter. Dentro do init e do destroy não tem muito o que fazer. O Container Web (uso o Tomcat 6.0.14 no momento) é esperto o suficiente pra saber o que fazer com esses caras. Particularmente falando, coloquei um log pra sinalizar inicialização e destruição do filtro.

Dentro do doFilter() – e é agora que começa a ação – você deve pegar o objeto do tipo HttpServletRequest, decorá-lo dentro de um tipo que você criará (ver passo 3) e passá-lo adiante para o container. Daí em diante, o container decidirá se enviará o request embrulhado, ou seja, seu objeto “request” criado no passo 3, para o próximo filtro (se existir um) ou para o servlet em questão. Para passá-lo adiante é bem simples. Pegue o objeto FilterChain que foi passado por parâmetro e chame o método doFilter do mesmo passando o request embrulhado e o response (você também pode alterá-lo se quiser! =D).

3 – Para entender esse passo você precisa ter ao menos uma noção do que é um Decorator. Tá. É um padrão de projeto, eu sei. Quase todo mundo sabe. O que eu quero dizer é que você precisa ENTENDÊ-LO.

A má notícia é que você não pode alterar o request em si e passá-lo adiante. Por outro lado, ninguém nunca falou nada sobre passar um request “de mentirinha” para o container. É isso que você faz basicamente. Você cria um objeto wrapper, no caso um RequestWrapper, que faça tudo que um HTTPServletRequest normal faz. “Tenho que implementar a interface de HttpServletRequest então…” você pensa. Você até pode. Mas essa interface possui uma monstruosidade de métodos que, acredite em mim, você não vai querer implementar um a um. Por isso já existe na API Java Servlet uma classe chamada HttpServletRequestWrapper, a qual você só modifica os métodos que você quiser! É só estendê-la! Agora ficou fácil. Bom, pra tratar tags html eu sobrecarreguei todos os métodos getParameter* para pegar o valor do getParameter* original, aplicar a regra da minha classe TratarTags (passo 1) e retornar o valor modificado. Sempre que uma requisição chegar ao servlet e você quiser extrair um valor do request(um parâmetro por exemplo), esse valor já chegará SEM as tags. O que significa que o filtro deu conta do trabalho.

Exemplo: TratarTagsFilter:

public class TratarTagsFilter implements Filter {
    private MapeamentoUrlCampo mapeamento = null;

    public TratarTagsFilter() {
        this.mapeamento = MapeamentoUrlCampo.getMapeamento();
        this.mapeamento.addMapeamento("campanhaAction.do", "script");
     }

     private static final Log logging =
     LogFactory.getLog(TratarTagsFilter.class);

     public void destroy() {
         logging.info("Terminando de executar o filtro. Classe= " + this);
     }

     public void doFilter(ServletRequest request,
     ServletResponse response, FilterChain chain) throws IOException,
     ServletException {
         logging.info("Executando o filtro neste momento. Classe= " + this);
         RequestWrapper requestWrapper = new RequestWrapper(request);
         chain.doFilter(requestWrapper, response);
     }

         public void init(FilterConfig arg0) throws ServletException {
         logging.info("Iniciando o filtro neste momento. Classe= " + this);
     }
     /*** Classe Interna estática que adiciona uma nova
     funcionalidade aos métodos da Classe HttpServletRequest* */
     private static class RequestWrapper extends HttpServletRequestWrapper {

         public RequestWrapper(ServletRequest request) {
             super((HttpServletRequest)request);
         }

         @Override
         public String getParameter(String name) {
             String valor = super.getParameter(name);
             String urlAtual = this.getRequestURL().toString();
             MapeamentoUrlCampo mapeamento = MapeamentoUrlCampo.getMapeamento();
             if(valor != null && !mapeamento.permiteCampoHtml(urlAtual, name)){
                 String novoNome = TratarTags.retirarTags(valor);
                 return novoNome;
             }
             else {
                 return valor;
             }
         }
         @Override
         public Map getParameterMap() {
             Map<String, String> map = super.getParameterMap();
             Set<String> chaves = map.keySet();
             List<String> listaChaves = new ArrayList<String>(chaves);
             Map<String, String> mapRetorno = new TreeMap<String, String>();
             for(String chave : listaChaves) {
                 mapRetorno.put(chave, this.getParameter(chave));
             }
             return mapRetorno;
         }
         @Override
         public String[] getParameterValues(String name) {
             String[] valores = super.getParameterValues(name);
             String[] novosValores;
             String urlAtual = this.getRequestURL().toString();
             MapeamentoUrlCampo mapeamento = MapeamentoUrlCampo.getMapeamento();
             if(valores != null && !mapeamento.permiteCampoHtml(urlAtual, name)) {
                 novosValores = new String[valores.length];
                 for(int i = 0; i < valores.length; i++) {
                     novosValores[i] = TratarTags.retirarTags(valores[i]);
                 }
                 return novosValores;
             }
             else {
                 return valores;
             }

         }
    }

}

4 – Agora basta configurar o filtro construído no seu web.xml. Dentro do elemento web-app você cria
duas tags:

A primeira é:


<filter>
    <filter-name>TratarTagsFilter</filter-name>
    <filter-class>com.projeto.filtro.TratarTagsFilter</filter-class>
</filter>

Que você direciona para interceptar apenas um padrão específico de url dessa forma:


<filter-mapping>
    <filter-name>TratarTagsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Pronto, o filtro está pronto pra funcionar na sua aplicação. Não sei se existe outra estratégia de evitar ataques via XSS fora essa de retirar tags html arriscadas. Sei que essa funciona eficientemente, por isso decidi utilizá-la. No entanto, se algum leitor souber de outra abordagem interessante, ficaria muito curioso em ouvi-la.

Padrão
Spring

Spring – Migrando de XML para Annotations

Introdução

Com certeza, você já abriu um arquivo do tipo applicationContext.xml e se deparou com um arquivo xml gigantesco com um monte de declarações de beans, onde se define em cada um deles, o nome de identificação, o nome completo da classe concreta, o escopo, o tipo da injeção de dependência e etc. do tipo:

<bean name="/meuBean"
scope="prototype" autowire="autodetect"/>

Más notícias pra quem encontrar esse tipo de abordagem no seu projeto. Primeiro, as configurações internas do seu projeto estão tornando a implementação de qualquer nova funcionalidade dependentes de um arquivo xml externo as classes. E isso significa alto acoplamento, ou seja, má idéia. Segundo, é muito mais difícil pra quem está aprendendo um novo sistema (uma manutenção por exemplo) se deparar com arquivos contendo um monte de beans a serem injetados pelo Spring e entender o propósito deles. Além desses 2 motivos, um ou mais xml´s enormes dão um aspecto lixoso ao projeto, quando uma solução mais elegante poderia ser empregada.

A boa notícia é que o Spring Framework já provê um melhor suporte a forma como seus beans são injetados pelo container de Injeção de Dependência – Annotations! Nesse artigo, você verá como é simples migrar da forma antiga de injetar dependências (XML) para a nova forma (Annotations) usando o melhor das novidades do Spring 2.5.

@Autowired – injetando dependências

Bom, vou levar em consideração que o leitor já conhece o funcionamento do container de IoC do Spring. Tendo dito isto, vamos direto ao ponto da mudança principal: a migração de xml para annotations. A annotation que o Spring disponibiliza para marcar o objeto injetado é @Autowired. Você pode encontrar mais informações técnicas sobre ele aqui. Você pode marcar um atributo, um setter ou um construtor como @Autowired, de acordo com sua preferência/necessidade. Pessoalmente, eu prefiro marcar os atributos de classe mesmo. Por que? 2 motivos:

1 – É mais simples. Simplesmente. Não preciso escrever um setObjeto nem um construtor que, no fim, só será usado pelo framework. Se eu não uso, não deveria escrever. Ou seja, menos verbose.

2 – Programando voltado-a-interfaces, se eu anoto meu atributo, uma interface, como @Autowired, o container IoC irá buscar apenas a única implementação daquela interface. Caso você tenha mais de uma implementação e quer escolher uma, pode usar o @Qualifier para escolher o bean mais especificamente. Nesse caso, aumentaria a verbose.

Para exemplificar, na sua classe que usaria uma dependência, você faria assim:

public class LoginAction {
    private final Log logger = LogFactory.getLog(LoginAction.class);

    @Autowired
    private IControladorHorario controladorHorario;
}

@Component, @Service, @Repository – dando nome aos bois.

Na configuração dos beans por xml era necessário dizer o id dos beans, ou um nome de identificação. Com annotations não é diferente. Só que é BEM mais fácil dar nome aos bois. Você simplesmente usa a annotation @Component na classe do bean que será injetado e coloca o id do bean entre os parênteses. Exemplo:

@Component("controladorHorario")
class ControladorHorario {...}

Na verdade, você deve optar por usar annotations mais específicas como @Service para controladores e @Repository para Daos/Repositórios. São os chamados stereotypes. Essas Annotations têm o mesmo efeito de @Component e estendem esta.

Parece mágica, mas não é. Ou é?

Nada demais até aqui. Repare que estamos injetando um controlador numa Action (Struts). É, eu sei. Parece mesmo mágica, mas o que ocorre na verdade é que, para o container saber onde detectar annotations você precisa declarar no seu applicationContext.xml o conjunto de pacotes onde ele deve procurar. Já vi casos onde são usados 3 contextos na aplicação (um para beans das actions, um para beans dos controladores[controladores do model] e um para beans do Dao). Nesse caso devemos indicar que beans procurar em cada um desses contextos (eu sei, é mais complicado e você normalmente deve usar apenas um applicationContext mesmo). No caso, no meu contexto de actions, eu indico onde estão os beans dos controladores, porque as actions injetam controladores. No meu contexto de controladores, eu indico onde estão os beans dos daos, porque os controladores injetam os Daos. Por exemplo, no contexto onde declararia os beans dos actions:

<beans xmlns="<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>"
xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>"
xmlns:context="<a href="http://www.springframework.org/schema/context" target="_blank">http://www.springframework.org/schema/context</a>"
xsi:schemaLocation="<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>
<a href="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans-2.5.xsd</a>
<a href="http://www.springframework.org/schema/context" target="_blank">http://www.springframework.org/schema/context</a>
<a href="http://www.springframework.org/schema/context/spring-context-2.5.xsd" target="_blank">http://www.springframework.org/schema/context/spring-context-2.5.xsd</a>">

    <context:annotation-config/>
    <context:component-scan base-package="com.provider.discador.horario.controle">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
</beans>

– A tag “<context:annotation-config/>” indica para o framework que você está declarando seus beans via annotations.
– A tag “<context:component-scan base-package=”com.provider.discador.horario.controle”>” indica aonde o container deve fazer uma busca por annotations relacionadas com injeção de dependência. Encontrando essas annotations, o container produz as instâncias necessárias e injeta aonde o usuário indicar por meio do @Autowired. A tag interna a esta é “<context:include-filter type=”annotation” expression=”org.springframework.stereotype.Service”/>” e é utilizada como filtro para o container. Como se dissesse: “Ei! Vá nesse pacote e procure apenas por beans anotados por @Service”. Você pode optar também por usar um filtro de exclusão ao invés de inclusão como o exemplificado agora. Aprenda mais sobre essa mágica aqui.

“Mas não é melhor mandar o container fazer scan de todos os pacotes procurando todos os tipos de annotations logo? Ao invés de perder tempo especificando os pacotes que serão incluídos/excluídos?” A resposta é: NÃO. Tecnicamente você pode. Mas o overhead é significativo. Pense nisso, o container do Spring vai ter que fazer uma busca em toda a aplicação, preparando beans, dentro de beans, dentro de beans… enfim, tudo isso é custoso. “Mas minha aplicação é pequena, não tem problema…” talvez diga. Acontece que de qualquer forma, você está sobrecarregando uma capacidade do framework sem necessidade. Se você não precisa carregar todos os beans de todos os pacotes, e mesmo assim o faz, isso revela que você não tem muitos critérios como desenvolvedor. Tipo matar mosquito com bazuca.

Uma abordagem a evitar

Apesar de todas as facilidades que essa técnica possibilita, existe um problema de conflito com um design facilmente encontrado por aí – HibernateDaoSupport. Essa classe faz parte do framework Spring e é bastante simples ao disponibilizar serviços de um DAO via Hibernate. O HibernateDaoSupport, com o objetivo de salvar objetos na sessão e consequentemente no banco de dados, possui uma SessionFactory que é inicializada uma vez assim que a aplicação começa a rodar. Você talvez configure essa SessionFactory no seu contexto de dados do spring. Então, se você deseja injetar uma classe Dao que estenda direta ou indiretamente HibernateDaoSupport você terá problemas. O problema começa uma vez que você chama getHibernateTemplate(). Visto que você estende o HibernateDaoSupport, a chamada a getHibernateTemplate() irá procurar pelo objeto hibernateTemplate na classe pai – o objeto não será encontrado visto que não foi injetado – e você terá um erro do tipo “java.lang.IllegalArgumentException: sessionFactory or hibernateTemplate is required”. A causa desse erro é bem difícil de sacar, mas você pode consertar bem facilmente também. Como a sua classe Dao precisa de um hibernateTemplate ou sessionFactory e não os encontra na classe pai, você mesmo precisa encontrar uma forma de injetá-los. Eu fiz assim:


@Repository("horarioDao")
public class HorarioDao extends Dao<Horario> implements IHorarioDao {

    @Autowired //injeção via set
    public HorarioDao(SessionFactory sessionFactory) {
        super(Horario.class);
        setSessionFactory(sessionFactory);
    }
}

Nesse caso, a classe Dao estende HibernateDaoSupport, causando o conflito mencionado. A solução: criar um construtor que receba a SessionFactory que você carrega uma vez no seu contexto de dados como parâmetro e marcar o construtor como @Autowired para que a injeção seja feita dinamicamente pelo container. Além disso, dentro do construtor invoque o setSessionFactory(que é herdado) passando o objeto. Voilá! Problema contornado!

Conclusão

Martin Fowler diz que bom código não é aquele que computadores entendem, mas sim aqueles que humanos possam entender. Assim, anotando seus atributos/métodos/construtores a serem injetados você está facilitando a vida de quem irá ler o código futuramente, tornando o aprendizado do seu código mais rápido. XML´s são ótimos, não me entenda mal. Mas você deve se preocupar se começa a existir uma dependência grande de xml no seu projeto e as configurações são gigantescas.

Claro, conflitos ocorrem, como você pôde notar no caso do HibernateDaoSupport, mas a técnica de contorná-lo é bem esperta também! De qualquer forma, o uso do HibernateDaoSupport é desencorajado oficialmente pelo Spring desde a versão 3.0. Mas esse é um assunto pra outro post…

Padrão