Secure Spring Boot API без аутентификации пользователя - PullRequest
0 голосов
/ 03 августа 2020

В настоящее время я разрабатываю несколько сервисов с весенней загрузкой, и у меня возникли некоторые проблемы с безопасностью. Все открытые конечные точки являются publi c и не требуют аутентификации пользователя / пароля. Однако эти службы не могут быть легко доступны для вызывающего абонента, кроме нашего интерфейсного приложения, поскольку мы должны собирать некоторую информацию о пользователе через форму, в которой капча выполняет проверку. Из-за этого нам нужно убедиться, что службы вызываются только этим интерфейсным приложением и что поддельные запросы отклоняются.

Из-за этих требований я изначально думал, что сделать конечные точки доступными через https было достаточно . Тем не менее, возможность повторных атак и спуфинга по-прежнему беспокоит меня.

Итак, прочитав несколько статей, я пришел к следующему черновику:

Пожалуйста, называйте клиент клиентским приложением.

  1. клиент и сервер должны совместно использовать пару ключей (publi c и закрытые ключи).

  2. Для каждого запроса необходимо следующее: быть удовлетворенным:

  • клиент создает уникальный одноразовый номер (случайное число)

  • клиент создает HMA C - Токен SHA1 с общим закрытым ключом

     token = hmac('sha1', private_key, public_key + timestamp + nonce);
    
  • клиент должен отправить public_key, timestamp, nonce и token в заголовке

при получении запроса сервер проверяет, присутствуют ли все параметры заголовка, а затем вычисляет тот же токен hma c -sha1 и сравнивает с полученным значением от клиента.

одноразовый номер затем добавляется в диспетчер кеша, так что дублированные запросы отбрасываются.

, если какой-либо из параметров заголовка отсутствует или вычисленный токен отличается от один, отправленный клиентом, запрос также отбрасывается.

Это подходящий подход? Стоит ли выгода от таких накладных расходов?

Вот коды, которые у меня сейчас есть:

@Service

public class APIAuthenticationManager implements AuthenticationManager {

@Value("${security.http.api_key}")
private String apiKeyValue;

@Value("${security.http.api_key_header}")
private String apiKeyRequestHeader;

@Value("${security.valid_timestamp.thresold}")
private String timestampThresold;

@Value("${security.valid_timestamp.header}")
private String timestampHeader;

@Value("${security.nonce.header}")
private String nonce;

@Value("${security.token.header}")
private String tokenHeader;

@Value("${security.private_key}")
private String privateKey;

@Override
public Authentication authenticate(Authentication authentication) {
    HttpServletRequest request = (HttpServletRequest) authentication.getPrincipal();

    if (!apiKeyValue.equals(request.getParameter(apiKeyRequestHeader))) {
        throw new BadCredentialsException("The API key was not found or not the expected value.");
    }

    String timestamp = request.getParameter(timestampHeader);
    if(timestamp == null) {
        throw new BadCredentialsException("Timestamp was not found or its value is invalid.");
    }

    Date requestIssueDate = Util.parseDate(timestamp, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
    if(requestIssueDate == null) {
        throw new BadCredentialsException("Timestamp was not found or its value is invalid.");
    }

    long expired = System.currentTimeMillis() - Integer.valueOf( timestampThresold );
    if (requestIssueDate.getTime() > expired) {
        throw new BadCredentialsException("Timestamp was not found or its value is invalid.");
    }

    // HMAC('SHA1', 'API_KEY', 'TOKEN GENERATED IN CLIENT');
    // TOKEN GENERATED IN CLIENT = HMAC('SHA1', 'API_KEY', 'SECRET_KEY + TIMESTAMP + NONCE');
    String tokenFromClient = request.getParameter(tokenHeader);     
    String calculatedToken = HMACSignatureUtil.calculateHMAC(privateKey, apiKeyValue + timestamp + nonce); 

    if(!tokenFromClient.equals(calculatedToken)) {
        throw new BadCredentialsException("Invalid token.");
    }

    authentication.setAuthenticated(true);
    return authentication;
}

Это ConfigurerAdapter

@Autowired
private APIAuthenticationManager apiAuthenticationManager;
    
@Override
protected void configure(HttpSecurity http) throws Exception {
    APIKeyAuthFilter filter = new APIKeyAuthFilter();
    filter.setAuthenticationManager( apiAuthenticationManager );

    http.csrf().disable()
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and().addFilter(filter).authorizeRequests().anyRequest().authenticated();
    
    http.requiresChannel()
    .anyRequest(). requiresSecure();
}
    
...