Shinyproxy: Tempo limite de autenticação SAML (?) Erro

Criado em 15 fev. 2021  ·  6Comentários  ·  Fonte: openanalytics/shinyproxy

Olá de novo! Conforme avançamos no desenvolvimento de nosso aplicativo usando SAML, encontramos um problema com a autenticação que só parece acontecer se uma guia for deixada aberta por algum tempo (não descobrimos o intervalo de tempo exato que causa o problema) e um usuário atualiza sua guia (não tenho certeza se uma nova janela também a aciona neste momento). Parece que, após esse tempo de inatividade, o ShinyProxy tenta se reautenticar e falha por algum motivo. A única solução que encontrei é limpar os dados do site no navegador para nosso IdP (auth.company.com). Feito isso, as coisas funcionam conforme o esperado.

Quando isso acontece, ShinyProxy tenta autenticar várias vezes seguidas (posso ver muitos retornos de chamada de redirecionamento SAML passando rapidamente na barra de URL), mas eventualmente falha e me despeja em http://my.company.com/app/saml / SSO com o seguinte erro:

image

Nos logs do servidor, vejo o seguinte:

shinyproxy-server_1  | 2021-02-15 04:00:11.531  INFO 1 --- [  XNIO-1 task-4] o.s.security.saml.log.SAMLDefaultLogger  : AuthNRequest;SUCCESS;XXX.XX.XX.XXX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;
shinyproxy-server_1  | 2021-02-15 04:00:11.608  INFO 1 --- [  XNIO-1 task-4] colMessageXMLSignatureSecurityPolicyRule : Validation of protocol message signature succeeded, message type: {urn:oasis:names:tc:SAML:2.0:protocol}Response
shinyproxy-server_1  | 2021-02-15 04:00:11.609  INFO 1 --- [  XNIO-1 task-4] o.s.security.saml.log.SAMLDefaultLogger  : AuthNResponse;FAILURE;XXX.XX.XX.XXX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;org.springframework.security.saml.SAMLStatusException: Response has invalid status code urn:oasis:names:tc:SAML:2.0:status:Responder, status message is null

Algo estranho é que parece que a primeira tentativa de autenticação foi bem-sucedida, mas o ShinyProxy não gostou por algum motivo e tentou novamente.


Rastreamento de erro completo:

~Brightproxy-server_1 |
em org.springframework.security.saml.SAMLAuthenticationProvider.authenticate (SAMLAuthenticationProvider.java:88)Brightproxy-server_1 |
em org.springframework.security.authentication.ProviderManager.authenticate (ProviderManager.java:219)Brightproxy-server_1 |
em sun.reflect.GeneratedMethodAccessor63.invoke (fonte desconhecida)Brightproxy-server_1 |
em java.lang.reflect.Method.invoke (Method.java:498)Brightproxy-server_1 |
em org.springframework.aop.framework.JdkDynamicAopProxy.invoke (JdkDynamicAopProxy.java:205)Brightproxy-server_1 |
em org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication (SAMLProcessingFilter.java:92)Brightproxy-server_1 |
em org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter (FilterChainProxy.java:334)Brightproxy-server_1 |
em org.springframework.security.web.FilterChainProxy.doFilter (FilterChainProxy.java:186)Brightproxy-server_1 |
em org.springframework.security.web.authentication.
em org.springframework.web.filter.OncePerRequestFilter.doFilter (OncePerRequestFilter.java:119)Brightproxy-server_1 |
em org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter (AbstractAuthenticationProcessingFilter.java:200)Brightproxy-server_1 |
em org.springframework.security.web.authentication.logout.LogoutFilter.doFilter (LogoutFilter.java:116)Brightproxy-server_1 |
em org.springframework.security.web.csrf.CsrfFilter.doFilterInternal (CsrfFilter.java:117)Brightproxy-server_1 |
em org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter (FilterChainProxy.java:334)Brightproxy-server_1 |
em org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal (HeaderWriterFilter.java:77)Brightproxy-server_1 |
em org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter (FilterChainProxy.java:334)Brightproxy-server_1 |
em org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter (FilterChainProxy.java:334)Brightproxy-server_1 |
em org.springframework.web.filter.OncePerRequestFilter.doFilter (OncePerRequestFilter.java:119)Brightproxy-server_1 |
em org.springframework.security.saml.metadata.MetadataGeneratorFilter.doFilter (MetadataGeneratorFilter.java:87)Brightproxy-server_1 |
em org.springframework.security.web.FilterChainProxy.doFilterInternal (FilterChainProxy.java:215)Brightproxy-server_1 |
em org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate (DelegatingFilterProxy.java:358)Brightproxy-server_1 |
em io.undertow.servlet.core.ManagedFilter.doFilter (ManagedFilter.java:61)Brightproxy-server_1 |
em org.springframework.web.filter.RequestContextFilter.doFilterInternal (RequestContextFilter.java:100)Brightproxy-server_1 |
em io.undertow.servlet.core.ManagedFilter.doFilter (ManagedFilter.java:61)Brightproxy-server_1 |
em org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal (WebMvcMetricsFilter.java:93)Brightproxy-server_1 |
em io.undertow.servlet.core.ManagedFilter.doFilter (ManagedFilter.java:61)Brightproxy-server_1 |
em org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal (CharacterEncodingFilter.java:201)Brightproxy-server_1 |
em io.undertow.servlet.core.ManagedFilter.doFilter (ManagedFilter.java:61)Brightproxy-server_1 |
em io.undertow.servlet.handlers.FilterHandler.handleRequest (FilterHandler.java:84)Brightproxy-server_1 |
em io.undertow.servlet.handlers.ServletChain $ 1.handleRequest (ServletChain.java:68)Brightproxy-server_1 |
em io.undertow.server.handlers.PathHandler.handleRequest (PathHandler.java:91)Brightproxy-server_1 |
em io.undertow.servlet.handlers.RedirectDirHandler.handleRequest (RedirectDirHandler.java:68)Brightproxy-server_1 |
em io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest (ServletAuthenticationCallHandler.java:57)Brightproxy-server_1 |
em io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest (AbstractConfidentialityHandler.java:46)Brightproxy-server_1 |
em io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest (AuthenticationMechanismsHandler.java:60)Brightproxy-server_1 |
em io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest (AbstractSecurityContextAssociationHandler.java:43)Brightproxy-server_1 |
em io.undertow.server.handlers.PredicateHandler.handleRequest (PredicateHandler.java:43)Brightproxy-server_1 |
em io.undertow.servlet.handlers.ServletInitialHandler.access $ 100 (ServletInitialHandler.java:78)Brightproxy-server_1 |
em io.undertow.servlet.handlers.ServletInitialHandler $ 2.call (ServletInitialHandler.java:130)Brightproxy-server_1 |
em io.undertow.servlet.core.ContextClassLoaderSetupAction $ 1.call (ContextClassLoaderSetupAction.java:43)Brightproxy-server_1 |
em io.undertow.servlet.handlers.ServletInitialHandler.access $ 000 (ServletInitialHandler.java:78)Brightproxy-server_1 |
em io.undertow.server.Connectors.executeRootHandler (Connectors.java:370)Brightproxy-server_1 |
em org.jboss.threads.ContextClassLoaderSavingRunnable.run (ContextClassLoaderSavingRunnable.java:35)Brightproxy-server_1 |
em org.jboss.threads.EnhancedQueueExecutor $ ThreadBody.doRunTask (EnhancedQueueExecutor.java:1558)Brightproxy-server_1 |
em java.lang.Thread.run (Thread.java:748)~

A solução simples seria clicar no botão "Logout", mas esse botão não é mostrado na tela de erro do SAML. É difícil depurar, porque não posso fazer isso acontecer sob demanda. De acordo com minha pessoa de suporte IdP, o ADFS realmente emite um token válido no início desse processo, então parece ser um problema local de como o ShinyProxy está lidando com o token que recebe. Estou supondo que, como a guia do navegador nunca fechou, talvez o cookie de login tenha expirado e não tenha sido renovado corretamente, mas não tenho ideia do que estou falando, então aceite isso com cautela.

Voltarei com algumas informações, espero, mais úteis quando o erro ocorrer novamente, mas eu estava curioso para ver se alguém tinha visto isso e tinha alguma ideia de como corrigi-lo.

question

Todos 6 comentários

Encontrei o seguinte problema , que aponta para mensagens de erro semelhantes indicando uma incompatibilidade entre os algoritmos de assinatura SHA-1 e SHA-256, mas se fosse esse o caso em nossa instalação, não tenho certeza de por que funcionaria na maioria das vezes, mas falham intermitentemente após um longo período de inatividade, então não acho que esse seja o meu problema.

Como acompanhamento, defini a parte SAML do aplicativo para DEBUG log de nível e vi o seguinte em um destes eventos de tempo limite:

shinyproxy-server_1  | 2021-02-15 21:03:14.050 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.context.SAMLContextProviderImpl  : No IDP specified, using default http://auth.company.com/adfs/services/trust
shinyproxy-server_1  | 2021-02-15 21:03:14.050 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.util.SAMLUtil          : Index for AssertionConsumerService not specified, returning default
shinyproxy-server_1  | 2021-02-15 21:03:14.051 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLEntryPoint         : Processing SSO using WebSSO profile
shinyproxy-server_1  | 2021-02-15 21:03:14.051 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.websso.WebSSOProfileImpl      : Using default consumer service with binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
shinyproxy-server_1  | 2021-02-15 21:03:14.052 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.storage.HttpSessionStorage    : Storing message a17g5ahbxxxxxxxxxxx454jj to session hxxxxxxJ_E8xxxxxxxxHP_TwIxxxxxxxH
shinyproxy-server_1  | 2021-02-15 21:03:14.053  INFO 1 --- [  XNIO-1 task-2] o.s.security.saml.log.SAMLDefaultLogger  : AuthNRequest;SUCCESS;XXX.XX.XXX.XX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;
shinyproxy-server_1  | 2021-02-15 21:03:14.805 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Request is to process authentication
shinyproxy-server_1  | 2021-02-15 21:03:14.806 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Attempting SAML2 authentication using profile urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser
shinyproxy-server_1  | 2021-02-15 21:03:14.811 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.processor.SAMLProcessorImpl   : Retrieving message using binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
shinyproxy-server_1  | 2021-02-15 21:03:14.816  INFO 1 --- [  XNIO-1 task-2] colMessageXMLSignatureSecurityPolicyRule : SAML protocol message was not signed, skipping XML signature processing
shinyproxy-server_1  | 2021-02-15 21:03:14.816 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.util.SAMLUtil          : Found endpoint org.opensaml.saml2.metadata.impl.AssertionConsumerServiceImpl<strong i="7">@6531c789</strong> for request URL https://my.company.com/app/saml/SSO based on location attribute in metadata
shinyproxy-server_1  | 2021-02-15 21:03:14.816 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.storage.HttpSessionStorage    : Message a17g5ahb0gieeh9b4e48ff6a3d454jj found in session hXmYJ_E8wf6F2plFsHP_TwIZS1Cg6n2Vu4wD4i2H, clearing
shinyproxy-server_1  | 2021-02-15 21:03:14.817 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Verifying issuer of the Response
shinyproxy-server_1  | 2021-02-15 21:03:14.817 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Verifying signature
shinyproxy-server_1  | 2021-02-15 21:03:14.820 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.t.MetadataCredentialResolver     : Added 1 credentials resolved from metadata of entity http://auth.company.com/adfs/services/trust
shinyproxy-server_1  | 2021-02-15 21:03:14.826 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Processing Bearer subject confirmation
shinyproxy-server_1  | 2021-02-15 21:03:14.836 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.s.w.WebSSOProfileConsumerImpl      : Validation of authentication statement in assertion failed, skipping
shinyproxy-server_1  | org.springframework.security.authentication.CredentialsExpiredException: Authentication statement is too old to be used with value 2021-02-15T15:51:35.161Z
                        ...
shinyproxy-server_1  | 2021-02-15 21:03:14.839 DEBUG 1 --- [  XNIO-1 task-2] o.s.s.saml.SAMLAuthenticationProvider    : Error validating SAML message
shinyproxy-server_1  |
shinyproxy-server_1  | org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.841  INFO 1 --- [  XNIO-1 task-2] o.s.security.saml.log.SAMLDefaultLogger  : AuthNResponse;FAILURE;XXX.XX.XXX.XX;https://my.company.com/app;http://auth.company.com/adfs/services/trust;;;org.opensaml.common.SAMLException: Response doesn't have any valid assertion which would pass subject validation
shinyproxy-server_1  |  at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:265)
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.842  INFO 1 --- [  XNIO-1 task-2] e.o.containerproxy.service.UserService   : Authentication failure [user: ] [error: Error validating SAML message]
shinyproxy-server_1  | 2021-02-15 21:03:14.848 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
shinyproxy-server_1  |
shinyproxy-server_1  | org.springframework.security.authentication.AuthenticationServiceException: Error validating SAML message
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.849 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Updated SecurityContextHolder to contain null Authentication
shinyproxy-server_1  | 2021-02-15 21:03:14.849 DEBUG 1 --- [  XNIO-1 task-2] o.s.security.saml.SAMLProcessingFilter   : Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler<strong i="8">@19dec0b4</strong>
shinyproxy-server_1  | 2021-02-15 21:03:14.858 ERROR 1 --- [  XNIO-1 task-2] org.thymeleaf.TemplateEngine             : [THYMELEAF][XNIO-1 task-2] Exception processing template "/": Error resolving template [/], template might not exist or might not be accessible by any of the configured Template Resolvers
shinyproxy-server_1  | org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/], template might not exist or might not be accessible by any of the configured Template Resolvers
                       ...
shinyproxy-server_1  |
shinyproxy-server_1  | 2021-02-15 21:03:14.861 ERROR 1 --- [  XNIO-1 task-2] io.undertow.request                      : UT005023: Exception handling request to /app/error
shinyproxy-server_1  |
shinyproxy-server_1  | org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/], template might not exist or might not be accessible by any of the configured Template Resolvers

Essa sequência é repetida 7 vezes em cerca de 2 segundos antes que o ShinyProxy desista e me envie para uma página de erro genérica do springboot, conforme mostrado acima. Duas curiosidades:

1) org.springframework.security.authentication.CredentialsExpiredException: Authentication statement is too old to be used with value 2021-02-15T15:51:35.161Z - esta parece ser a raiz do problema, mas não tenho certeza se isso é um problema no lado do IdP ou no lado do ShinyProxy

2) 2021-02-15 21:03:14.861 ERROR 1 --- [ XNIO-1 task-2] io.undertow.request : UT005023: Exception handling request to /app/error - parece que meu modelo de erro não está carregando corretamente. Se fosse, acho que poderia colocar um link para a página de "logout", no qual os usuários poderiam clicar para redefinir sua sessão (não tenho certeza se isso funcionaria, apenas um palpite)

Alguma ideia?

Links Relacionados:

1) https://stackoverflow.com/questions/48289763/spring-security-infinite-loop-after-initial-login-and-timeout
2) https://stackoverflow.com/questions/30528636/idp-initiated-saml-login-error-authentication-statement-is-too-old-to-be-used

O link 2 sugere algumas soluções: (1) alinhe maxAuthenticationAge ao tempo de vida do ADFS ou (2) defina forceAuthN como verdadeiro

Isso é possível a partir da configuração do ShinyProxy ou estou procurando uma versão personalizada necessária para isso?

Olá @ jat255

Obrigado por seu extenso relatório e pesquisa! Isso é uma grande coincidência: eu experimentei exatamente o mesmo problema em uma de nossas implantações também. Ontem encontrei as mesmas soluções que você, só tive que testar :)

Definir a opção forceAuthN realmente resolve o problema do nosso lado. A única desvantagem disso é que o usuário é forçado a fornecer suas credenciais completas novamente, enquanto o ADFS acredita que as credenciais do usuário ainda estão "atualizadas o suficiente". Além disso, pelo link que você forneceu, parece que alguns IDPs ignoram essa opção (por exemplo, google). Você pode habilitar forceAuthN especificando-o em sua configuração saml:

proxy:
  saml:
    idp-metadata-url: ... 
    ....
    force-authn: true

Esta opção ainda não está documentada em nosso site, eu cuidarei disso.

A especificação da opção maxAuthenticationAge ainda não foi implementada no ShinyProxy. No entanto, como vejo valor nesta opção (por exemplo, usando google como IDP), vou implementar uma opção para isso.

Também tentarei fornecer um comportamento mais útil no caso de CredentialsExpiredException .

@LEDfan muito obrigado por sua pronta resposta e solução! Eu implementei essa mudança, mas agora devo esperar 24 horas para ver se funcionou: smirk: Eu pelo menos confirmei que vejo ForceAuthn="true" na carga útil do SAML, então a opção definitivamente funciona no ShinyProxy lado.

Acho que a opção forceAuthN não será muito difícil para nossos usuários, já que a maioria de nossos usuários usa autenticação corporativa conectada ao gerenciamento automático de credenciais do Windows, de modo que não precisam inserir nenhuma senha para obter acesso a Recursos protegidos por SAML. Apresentarei um relatório aqui em alguns dias para informar se funcionou em nosso caso.

Isso parece estar funcionando nos últimos dias (não vi o erro novamente), então vou encerrar isso. Obrigado novamente!

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

PauloJhonny picture PauloJhonny  ·  8Comentários

lucius-verus-fan picture lucius-verus-fan  ·  7Comentários

donarus picture donarus  ·  3Comentários

xtrasimplicity picture xtrasimplicity  ·  5Comentários

dylancis picture dylancis  ·  3Comentários