使用spring security saml2 provider version 5.7。如果在身份验证响应中提供InResponseTo,则引入InResponseTo的强制验证。
验证逻辑期望在HttpSession中找到保存的Saml2AuthenticationRequest。但是,这只有在没有设置SameSite属性时才有可能。
根据我目前正在进行的项目的安全要求,设置为Lax或Strict。此配置在应用程序外部完成。这会导致会话和请求数据丢失。
也许有人已经处理过一个问题,知道如何处理它?我没有看到任何方法可以禁用验证或在库中可用的其他保存请求的方法。从5.升级弹簧安全。x到6,我在spring会话cookie中遇到了同样的问题。似乎没有任何标准的解决方案。现在,我提供了一个Saml2AuthenticationRequestRepository
的实现,它基于RelayState将SAML请求保存在数据库中。参数。下面是使用Mongo的示例实现。任何后端(内存缓存,Redis, MySQL等)都可以使用-
import java.util.Optional;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.saml2.core.Saml2ParameterNames;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.stereotype.Repository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Repository
@RequiredArgsConstructor
@Slf4j
public class MongoSaml2AuthenticationRequestRepository implements Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
public static final String SAML2_REQUEST_COLLECTION = "saml2RequestsRepository";
private final @NonNull MongoTemplate mongoTemplate;
@Override
public AbstractSaml2AuthenticationRequest loadAuthenticationRequest(HttpServletRequest request) {
String relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);
if (relayState == null) {
return null;
}
log.debug("Fetching SAML2 Authentication Request by relay state : {}", relayState.get());
Query query = Query.query(Criteria.where("relayState").is(relayState.get()));
AbstractSaml2AuthenticationRequest authenticationRequest = mongoTemplate.findOne(query, AbstractSaml2AuthenticationRequest.class, SAML2_REQUEST_COLLECTION);
if (!authenticationRequest.getRelayState().equals(relayState.get())) {
log.error("Relay State received from request '{}' is different from saved request '{}'.", relayState.get(), authenticationRequest.getRelayState());
return null;
}
log.debug("SAML2 Request retrieved : {}", authenticationRequest);
return authenticationRequest;
}
@Override
public void saveAuthenticationRequest(AbstractSaml2AuthenticationRequest authenticationRequest,
HttpServletRequest request, HttpServletResponse response) {
//As per OpenSamlAuthenticationRequestResolver, it will always have value. However, one validation can be added to check for null and regenerate.
String relayState = authenticationRequest.getRelayState();
log.debug("Relay State Received: {}", relayState);
mongoTemplate.save(authenticationRequest, SAML2_REQUEST_COLLECTION);
}
@Override
public AbstractSaml2AuthenticationRequest removeAuthenticationRequest(HttpServletRequest request,
HttpServletResponse response) {
AbstractSaml2AuthenticationRequest authenticationRequest = loadAuthenticationRequest(request);
if (authenticationRequest == null) {
return null;
}
mongoTemplate.remove(authenticationRequest, SAML2_REQUEST_COLLECTION);
return authenticationRequest;
}
}
添加此后,包括InResponseTo验证在内的所有SAML验证都成功通过。自RelayState不应该在每个规范的SAML请求之间更改,这似乎是合理的替代方案(到目前为止没有任何标准解决方案)。我正在进行标准的安全测试,如果有的话,我会用我的发现更新解决方案。