В настоящее время я разрабатываю несколько сервисов с весенней загрузкой, и у меня возникли некоторые проблемы с безопасностью. Все открытые конечные точки являются publi c и не требуют аутентификации пользователя / пароля. Однако эти службы не могут быть легко доступны для вызывающего абонента, кроме нашего интерфейсного приложения, поскольку мы должны собирать некоторую информацию о пользователе через форму, в которой капча выполняет проверку. Из-за этого нам нужно убедиться, что службы вызываются только этим интерфейсным приложением и что поддельные запросы отклоняются.
Из-за этих требований я изначально думал, что сделать конечные точки доступными через https было достаточно . Тем не менее, возможность повторных атак и спуфинга по-прежнему беспокоит меня.
Итак, прочитав несколько статей, я пришел к следующему черновику:
Пожалуйста, называйте клиент клиентским приложением.
клиент и сервер должны совместно использовать пару ключей (publi c и закрытые ключи).
Для каждого запроса необходимо следующее: быть удовлетворенным:
клиент создает уникальный одноразовый номер (случайное число)
клиент создает 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();
}