Autenticação e Autorização Baseado em Filter – Brecha de Segurança com JSF 2

Hoje eu gostaria de alertar a todos aqueles que tem utilizado como mecanismo de controle de autenticação e autorização de aplicativos web baseados em filtros servlet (ou qualquer framework derivado como Spring Security,  jGuard), JSF phase listener ou JAAS. Atenção – Existe uma possível brecha de segurança na versão JSF 2 não existente na versão 1.X. Segue a explicação resumida da questão:

Na nova versão do JSF 2, o novo formato de páginas oficiais  XHTML JSF seguem o ciclo de execução facelets diferentes da páginas JSF no formato JSP. E dentro da execução do facelets acontece uma otimização relacionado ao parse do XML, que casualmente resultou em duas situações problemáticas:

1) Os forwards das navegações entre as páginas JSF XHTML não estão executando os filtros servlet mapeados como <dispatcher>FORWARD</dispatcher>, justamente por que o otimização facelets não executa a pilha toda. Ou seja, a otimização do facelets consegue enviar o nova página navegada, sem executar o pilha inteira de execução do container.

2) Pelo mesmo motivo, o PHASE LISTENER JSF usado na fase RESTORE_VIEW também não mais executado nos forwards das navegações entre as páginas JSF XHTML. Ou seja, o listener não é executado quando acontece uma navegação.

Ja na versão do JSF 1.x tanto o filtro como o phase listener eram executados por que na navegação forwards, o container executava a pilha inteira de execução, uma vez que as paginas se transformavam em servlets.

Então meus amigos, caso sua aplicação dependa destas situações para validar credenciais e autorização nas páginas navegadas via serlvet filter, phase listener restore view ou JAAS,…quero te dizer hoje que isso não funciona 100%  na versão JSF 2. Dependendo da situação podem abrir brechas de segurança caso sua aplicação tenha situações aonde o usuário credenciado possa operar em um página e talvez não tenha credenciais para fazer algo na próxima navegada. Para uma explicação detalhada, acesse a thread do fórum GUJ http://www.guj.com.br/java/247653-nova-versao-do-jsf-2-nao-dispara-o-forward-na-navegacao-das-paginas-xhtml-resolvido

Como resolver então? Segue ai minhas dicas:

1. Não fazer nada:

Analisando a questão pode ser que a sua solução não tenha casos que  necessitem validar a permissão entre as navegação de páginas. Por experiência própria posso afirmar que realmente exista casos em que não seja necessários fazer essa validação pelo simples fato de não fazer sentido validar permissão da próxima navegação baseada na ação anterior. Normalmente quando o usuário tem permissão na operação ele também terá permissão para os possíveis resultados que ela possa gerar.

Caso sua solução não se enquadre na solução 1, segue as outras dicas:

2. Redirecionar todas as navegações do JSF:

Se no JSF 2 as navegações via forward não executa os filtros e nem o phase listener, uma forma de forçar isso acontecer é declarando as navegações usando redirecionamento. Para fazer isso, declare <redirect/> nas navegações no faces config. O resultado disso que é a cada navegação, o container web enviara uma resposta HTTP com o código 301 forçando o navegador requisitar a próxima página navegada, e com isso o filtro e o phase listener de restore view serão executados. Essa solução não é muito boa, uma vez que ela pode degradar a performance da solução sendo que em cada navegação o redirecionado resultara em +1 trafego HTTP completo. Pode ser viável para aplicações pequenos dentro de uma intranet.

3. Anotações de Segurança JEE:

Caso seu mecanismo de autenticação e autorização sejem baseadas em JAAS, você pode estar usando as anotações de segurança jEE nos managed beans @Allowed(“Nome_da_Role”) impedindo o usuário de executar ações na página caso ele tenha navegado para uma página que ele não tem permissão de operação. Essa situação não impede que ele veja a pagina no navegador, mas impede dele operar nela. Dependendo do tipo da solução, pode ser usado sem problemas. Essa solução foi sabiamente indicada pelo Rafael Ponte, em nossas trocas de emails sobre o assunto.

4. Combinar o filtro com o Phase Listener:

Em minha opinião a solução mais indicada é combinar o um filtro de autenticação  e autorização juntamente com um phase listener de RENDER_RESPONSE que garante autorização para as paginas navegadas. A mecânica funciona assim:

1) Usar um filtro que faça a já conhecida autenticação e autorização, impedindo o usuário não autenticado ou não autorizado ao acessar recursos restritos da aplicação.
2) Usar um phase listener JSF de RENDER_RESPONSE que refaça a autorização nas navegações. Como otimização da segunda autorização, verifique se URI do phase é igual a URI do filtro. Caso forem iguais não precisa aplicar a segunda validação, caso forem diferentes aplica-se novamente a validação da autorização, uma vez que se configura o caso de navegar de uma página para outra. Use escopo de ThreadLocal para armazenar a URL compartilhada entre as classes de filtro e phase dentro da execução das requisições HTTP.

Me coloco a disposição para ajudar as esclarecer as dúvidas relacionado com as dicas 😉 .

“Tu, ó Deus, mandaste copiosa chuva; restauraste a tua herança, quando estava cansada.” Salmos 68:9

Anúncios

7 pensamentos sobre “Autenticação e Autorização Baseado em Filter – Brecha de Segurança com JSF 2

  1. Olá Fernando. Meu Filtro está funcionando entre a navegacao de paginas. Tenho 3 paginas de exemplo. A pagina de login.xhtml, a pagina produto.xhtml e a pagina notaFiscal.xhtml.

    De acordo com o que você alertou, o filtro nao deveria agir se eu clicar em um commandLink da pagina produto.xhtml para notaFiscal.xhtml. por exemplo

    Dentro da pagina produto.xhtml:

    Segue o meu Web.xml:

    JSF-06

    Faces Servlet
    javax.faces.webapp.FacesServlet
    1

    Faces Servlet
    *.xhtml

    Filtro
    notasfiscais.filtro.Filtro

    Filtro
    /produto.xhtml

    Filtro
    /notaFiscal.xhtml

    javax.faces.PROJECT_STAGE
    Development

    Segue o meu filtro: (Vc sabe como obter o alias, “/produto.xhtml”, em vez do URI completo do objeto request ? tive que fazer uma gamber para armazenar na sessao a pagina original que o usuario queria, colocando somente o alias e nao o uri todo, pois estava dando erro na view.)

    package notasfiscais.filtro;

    import java.io.IOException;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;

    import br.com.caelum.notasfiscais.mb.LoginBean;

    public class Filtro implements Filter
    { @SuppressWarnings(“unused”)
    private FilterConfig filterConfig = null;

    public void init(FilterConfig filterConfig)
    {
    this.filterConfig = filterConfig;
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    throws IOException, ServletException
    {

    System.out.println(“Dentro do Filtro. Entrei no metodo doFilter “);

    HttpServletRequest request = (HttpServletRequest) req; //cast

    HttpServletResponse response = (HttpServletResponse) res;

    HttpSession sessao = request.getSession();

    LoginBean loginBean = (LoginBean) sessao.getAttribute(“loginBean”);

    System.out.println(“Dentro do Filtro. loginBean vale: ” + loginBean);

    //verificar se o usuario esta logado (verificando no objeto sessao)

    if(loginBean==null||!loginBean.isUsuarioLogado())

    {
    System.out.println(“Dentro do Filtro. Entrei dentro do if “);

    //salvar antes qual pagina ele queria ir. colocar isso na sessao
    sessao.setAttribute(“destino”, request.getRequestURI().substring(7));

    response.sendRedirect(response.encodeRedirectURL(“/JSF-06/login.xhtml”));

    }
    else
    {
    System.out.println(“Dentro do Filtro. Entrei no else para liberar o usuario “);

    chain.doFilter(req, res);
    }

    }

    public void destroy()
    {
    }
    }

    O filtro funciona sempre. Utilizei o Mojarra Versao 2.0 e a mais recente e funfou.

    Abracos!

  2. Ola thiago…. conforme ja escrito e documentado por mim, o filtro não vai agir em forward! Somente em redirect! No caso do forward vc tem que fazer o phase do geito q eu citei! Boa semana.

  3. Pingback: Top 15 Post 2012 « Fernando Franzini Java Blog

  4. hello there and thank you for your info – I have definitely picked up anything new from right here.
    I did however expertise some technical issues using this site,
    since I experienced to reload the website a lot of times previous
    to I could get it to load properly. I had been wondering if your
    web hosting is OK? Not that I am complaining, but sluggish loading instances times will
    sometimes affect your placement in google and can damage your
    quality score if ads and marketing with Adwords. Anyway I am adding
    this RSS to my email and could look out for a lot more of your respective exciting content.
    Ensure that you update this again soon.

  5. Pingback: Top 10 Post em 2013 « Fernando Franzini Java Blog

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s