MSSE SENG 5199

Course Materials for the MSSE Web Application Development Course

REST Api Security

Marc Kapke

kapkema@gmail.com


Disclaimers


Forms of Vulnerability


Gaining unauthorized access to sensitive data


Denial of service


Cross Site Request Forgery (CSRF)


Protecting Data In Transit


Code injection


Spring Boot Provides Help


Prevent SQL Injection - JPA Data access


Constructing JPQL/SQL via String concatenation

DON’T do this

List results = entityManager.createQuery("Select user from Users user where user.id = " + userId).getResultList();
List results = entityManager.createNativeQuery("Select * from Users where email_address = " + email).getResultList();
int resultCode = entityManager.createNativeQuery("Delete from Users where id = " + userId).executeUpdate();

Entity Manager query building


Entity Manager examples

Query jpqlQuery = entityManager.createQuery("Select user from Users user where user.emailAddress='foo@gmail.com'")
Query jpqlQuery = entityManager.createNamedQuery("allUsers");
Query sqlQuery = entityManager.createNativeQuery("Select * from Users where email_address='foo@gmail.com", User.class)

Parameter Binding


Parameter Binding examples

Query jpqlQuery = entityManager.createQuery("Select user from Users user where user.emailAddress=?1")
List results = jpqlQuery.setParameter(1, "foo@gmail.com").getResultList();
Query jpqlQuery = entityManager.createQuery("Select user from Users user where user.emailAddress=:email")
List results = jpqlQuery.setParameter("email, "foo@gmail.com").getResultList();

Spring Controllers


Validate User Input


Server side validation


Server Side Validation - Validators


Validators - examples

class AccountCredentials {
  @Size(min = 2, max=25)
  String username

  @Password
  String password
}

Validator - Custom

class PasswordValidator implements ConstraintValidator<Password, String> {
  private Pattern pattern
  private Matcher matcher
  private static final String PASSWORD_PATTERN = '((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})'

  PasswordValidator() {
    pattern = Pattern.compile(PASSWORD_PATTERN)
  }

  //...

  @Override
  boolean isValid(String value, ConstraintValidatorContext context) {
    if(!value){
      return false
    }

    matcher = pattern.matcher(value)
    return matcher.matches()
  }
}

Validator - Custom annotation

Client Side Validation


Escaping Output


HTML Escaping


Preventing CSRF in Spring


Access Control


Authentication


Authentication - Common patterns


Authorization


Authentication - Basic Authentication


Authentication - Json Web Token (JWT)


JWT Token


JWT Token - header

{
  "alg": "HS256",
  "typ": "JWT"
}

JWT Payload


JWT Payload - Example

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

JWT Payload Signature

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

Spring Boot Security


JWT with Spring Security


Spring Boot - Access Control


Example Spring Web Security config

@Override
protected void configure(HttpSecurity http) throws Exception {
  // disable caching
  http.headers().cacheControl()

  http.csrf().disable()
      .authorizeRequests()
      .antMatchers("/").permitAll()
      .antMatchers(HttpMethod.POST, "/login").permitAll()
      .antMatchers("/admin/**").hasRole("ADMIN")
      .anyRequest().authenticated()
      .and()
  .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
  .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
}

Spring Boot Filters


Example Authentication Filter

class JWTAuthenticationFilter extends GenericFilterBean{

  @Override
  void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    Authentication authentication = new TokenAuthenticationService().parseAndValidateToken((HttpServletRequest)request)

    SecurityContextHolder.getContext().setAuthentication(authentication)
    filterChain.doFilter(request,response)
  }
}

Example Login Filter

class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

  private TokenAuthenticationService tokenAuthenticationService

  JWTLoginFilter(String url, AuthenticationManager authenticationManager) {
    super(new AntPathRequestMatcher(url))
    setAuthenticationManager(authenticationManager)
    tokenAuthenticationService = new TokenAuthenticationService()
  }

  @Override
  Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
      throws AuthenticationException, IOException, ServletException {
    AccountCredentials credentials = new ObjectMapper().readValue(httpServletRequest.getInputStream(), AccountCredentials.class)
    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.username, credentials.password)
    return getAuthenticationManager().authenticate(token)
  }

  @Override
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication)
      throws IOException, ServletException {
    String name = authentication.name
    tokenAuthenticationService.createAndAddTokenToResponse(response, name)
  }
}

JWT Token Service


JWT Token Service - Issue Token

  private long EXPIRATIONTIME = 1000 * 60 * 60 * 24 * 10 // 10 days
  private String secret = "ThisIsASecret"
  private String tokenPrefix = "Bearer"
  private String headerString = "Authorization"

  void createAndAddTokenToResponse(HttpServletResponse response, String username) {
    String JWT = Jwts.builder()
                     .setSubject(username)
                     .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                     .signWith(SignatureAlgorithm.HS512, secret)
                     .compact()
    response.addHeader(headerString, tokenPrefix + " " + JWT)
  }

Json Token Service - Validate token

Authentication parseAndValidateToken(HttpServletRequest request) {
   String token = request.getHeader(headerString)
   if (token != null) {
     // parse the token.
     String username = Jwts.parser()
                        .setSigningKey(secret)
                        .parseClaimsJws(token)
                        .getBody()
                        .getSubject()

     return username ? new AuthenticatedUser(username) : null
   }
   return null
 }