Est-ce que sécuriser une application REST avec un JWT et une authentification Basique a du sens?

j'ai une application Spring REST qui a d'abord été sécurisée avec une authentification basique.

puis j'ai ajouté un contrôleur de connexion qui crée un Token Web JSON JWT qui est utilisé dans les requêtes suivantes.

puis-je déplacer le code suivant du contrôleur de connexion et dans le filtre de sécurité? Alors je n'aurais plus besoin du contrôleur de connexion.

tokenAuthenticationService.addTokenToResponseHeader(responseHeaders, credentialsResource.getEmail());

Ou puis-je supprimer l'authentification de Base?

Est-ce une bonne conception de mélanger Authentification de base avec une JWT?

bien que tout fonctionne bien, je suis un peu dans le noir ici pour concevoir cette sécurité.

35
demandé sur SilverlightFox 2015-03-07 21:35:12

2 réponses

en supposant 100% TLS pour toutes les communications - pendant et en tout temps après la connexion - l'authentification avec nom d'utilisateur/mot de passe via l'authentification de base et la réception D'une JWT en échange est un cas d'utilisation valide. C'est presque comment fonctionne exactement L'un des flux de OAuth 2 ('password grant').

l'idée est que l'utilisateur final est authentifié par l'intermédiaire d'un seul paramètre, par exemple /login/token en utilisant le mécanisme que vous voulez, et la réponse doit contenir le JWT qui doit être envoyé de retour sur toutes les demandes ultérieures. Le JWT doit être un JWS (c'est-à-dire un JWT signé par cryptographie) avec une expiration JWT appropriée (exp) champ: ceci garantit que le client ne peut pas manipuler le JWT ou le faire vivre plus longtemps qu'il ne devrait.

Vous n'avez pas besoin d'un X-Auth-Token en-tête soit: L'authentification HTTP Bearer scheme a été créé pour ce cas d'utilisation exacte: essentiellement n'importe quelle information qui trace le Bearer le nom du schéma est une information "au porteur" qui doit être validée. Vous il suffit de régler l' Authorization header:

Authorization: Bearer <JWT value here>

mais, cela étant dit, si votre client REST est "non fiable" (par exemple un navigateur compatible JavaScript), Je ne le ferais même pas: toute valeur dans la réponse HTTP qui est accessible via JavaScript-essentiellement toute valeur d'en - tête ou de corps de réponse - pourrait être reniflée et interceptée via les attaques MITM XSS.

il est préférable de stocker la valeur JWT dans un cookie sécurisé uniquement, http uniquement (cookie config: setSecure(true), setHttpOnly(true)). Ce garantit que le navigateur:

  1. transmettez seulement le cookie sur une connexion TLS et,
  2. ne mettez jamais la valeur du cookie à la disposition du code JavaScript.

Cette approche est presque tout ce que vous devez faire pour la sécurité des meilleures pratiques. La dernière chose à faire est de s'assurer que vous avez une protection CSRF sur chaque requête HTTP pour s'assurer que les domaines externes initiant des requêtes vers votre site ne peuvent pas fonctionner.

la façon la plus simple de le faire est de définir un cookie sécurisé uniquement (mais pas http uniquement) avec une valeur aléatoire, par exemple un UUID.

ensuite, sur chaque requête de votre serveur, Assurez-vous que votre propre code JavaScript lit la valeur du cookie et la positionne dans un en-tête personnalisé, par exemple X-CSRF-Token et vérifiez cette valeur sur chaque requête du serveur. Les clients de domaine externe ne peuvent pas définir des en-têtes personnalisés pour les requêtes vers votre domaine à moins que le client externe obtienne une autorisation via un HTTP Options request, donc toute tentative D'attaque CSRF (par exemple dans une IFrame, peu importe) échouera pour eux.

C'est le meilleur de la sécurité de race disponible pour les clients JavaScript non fiables sur le web aujourd'hui que nous connaissons. Stormpath a écrit un article sur ces techniques ainsi si vous êtes curieux.

Enfin, le Stormpath Java Servlet Plugindéjà fait tout cela pour vous (et beaucoup de choses plus cool, y compris des des contrôles de sécurité automatisés), de sorte que vous n'avez jamais à l'écrire - ou pire - le maintenir vous-même. Découvrez section D'authentification des requêtes HTTP et L'exemple Form/Ajax pour voir comment l'utiliser. HTH!

68
répondu Les Hazlewood 2015-03-09 23:02:51

voici un code pour sauvegarder la réponse acceptée sur la façon de faire cela au printemps....simplement étendre UsernamePasswordAuthenticationFilter et ajoutez-le à la sécurité du printemps...cela fonctionne bien avec L'authentification HTTP Basic + Spring Security

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {

        this.authenticationManager = authenticationManager;

    }

    @Override

    public Authentication attemptAuthentication(HttpServletRequest req,

                                                HttpServletResponse res) throws AuthenticationException {

        try {

            ApplicationUser creds = new ObjectMapper()

                    .readValue(req.getInputStream(), ApplicationUser.class);

            return authenticationManager.authenticate(

                    new UsernamePasswordAuthenticationToken(

                            creds.getUsername(),

                            creds.getPassword(),

                            new ArrayList<>())

            );

        } catch (IOException e) {

            throw new RuntimeException(e);

        }

    }

    @Override

    protected void successfulAuthentication(HttpServletRequest req,

                                            HttpServletResponse res,

                                            FilterChain chain,

                                            Authentication auth) throws IOException, ServletException {

        String token = Jwts.builder()

                .setSubject(((User) auth.getPrincipal()).getUsername())

                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))

                .signWith(SignatureAlgorithm.HS512, SECRET)

                .compact();

        res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);

    }

}

en utilisant JWT lib.:

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

classe de configuration de la botte de printemps

package com.vanitysoft.payit.security.web.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

    import com.vanitysoft.payit.util.SecurityConstants;

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
         @Autowired
           private UserDetailsService userDetailsService;

            @Autowired
            private  BCryptPasswordEncoder bCryptPasswordEncoder;

         @Override
           protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth.userDetailsService(userDetailsService)
                      .passwordEncoder(bCryptPasswordEncoder);
           }

         @Override
            protected void configure(HttpSecurity http) throws Exception {
             http.cors().and().csrf().disable()
                    .authorizeRequests()                             
                        .antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
                        .antMatchers("/user/**").authenticated()
                        .and()
                        .httpBasic()
                        .and()
                        .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                        .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                        .and()
                        .logout()
                        .permitAll();

            }
    }
1
répondu Jeryl Cook 2018-08-05 23:22:26