DNS rebinding protection: check host header
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
parent
b57b1269d0
commit
f6c3fde7f4
1 changed files with 551 additions and 119 deletions
|
|
@ -48,6 +48,8 @@ class ServerTransportSecurityIntegrationTests {
|
|||
|
||||
private static final String DISALLOWED_ORIGIN = "https://malicious.example.com";
|
||||
|
||||
private static final String DISALLOWED_HOST = "malicious.example.com:8080";
|
||||
|
||||
@Parameter
|
||||
private static Transport transport;
|
||||
|
||||
|
|
@ -73,6 +75,7 @@ class ServerTransportSecurityIntegrationTests {
|
|||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
requestCustomizer.reset();
|
||||
mcpClient = transport.createMcpClient(baseUrl, requestCustomizer);
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +118,29 @@ class ServerTransportSecurityIntegrationTests {
|
|||
assertThatThrownBy(() -> mcpClient.listTools());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hostAllowed() {
|
||||
// Host header is set by default by HttpClient to the request URI host
|
||||
var result = mcpClient.initialize();
|
||||
var tools = mcpClient.listTools();
|
||||
|
||||
assertThat(result.protocolVersion()).isNotEmpty();
|
||||
assertThat(tools.tools()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectHostNotAllowed() {
|
||||
requestCustomizer.setHostHeader(DISALLOWED_HOST);
|
||||
assertThatThrownBy(() -> mcpClient.initialize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void messageHostNotAllowed() {
|
||||
mcpClient.initialize();
|
||||
requestCustomizer.setHostHeader(DISALLOWED_HOST);
|
||||
assertThatThrownBy(() -> mcpClient.listTools());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Tomcat management
|
||||
// ----------------------------------------------------
|
||||
|
|
@ -182,8 +208,10 @@ class ServerTransportSecurityIntegrationTests {
|
|||
public Sse() {
|
||||
transport = HttpServletSseServerTransportProvider.builder()
|
||||
.messageEndpoint("/mcp/message")
|
||||
.securityValidator(
|
||||
DefaultServerTransportSecurityValidator.builder().allowedOrigin("http://localhost:*").build())
|
||||
.securityValidator(DefaultServerTransportSecurityValidator.builder()
|
||||
.allowedOrigin("http://localhost:*")
|
||||
.allowedHost("localhost:*")
|
||||
.build())
|
||||
.build();
|
||||
McpServer.sync(transport)
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
|
|
@ -213,8 +241,10 @@ class ServerTransportSecurityIntegrationTests {
|
|||
|
||||
public StreamableHttp() {
|
||||
transport = HttpServletStreamableServerTransportProvider.builder()
|
||||
.securityValidator(
|
||||
DefaultServerTransportSecurityValidator.builder().allowedOrigin("http://localhost:*").build())
|
||||
.securityValidator(DefaultServerTransportSecurityValidator.builder()
|
||||
.allowedOrigin("http://localhost:*")
|
||||
.allowedHost("localhost:*")
|
||||
.build())
|
||||
.build();
|
||||
McpServer.sync(transport)
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
|
|
@ -245,8 +275,10 @@ class ServerTransportSecurityIntegrationTests {
|
|||
|
||||
public Stateless() {
|
||||
transport = HttpServletStatelessServerTransport.builder()
|
||||
.securityValidator(
|
||||
DefaultServerTransportSecurityValidator.builder().allowedOrigin("http://localhost:*").build())
|
||||
.securityValidator(DefaultServerTransportSecurityValidator.builder()
|
||||
.allowedOrigin("http://localhost:*")
|
||||
.allowedHost("localhost:*")
|
||||
.build())
|
||||
.build();
|
||||
McpServer.sync(transport)
|
||||
.serverInfo("test-server", "1.0.0")
|
||||
|
|
@ -275,18 +307,33 @@ class ServerTransportSecurityIntegrationTests {
|
|||
|
||||
private String originHeader = null;
|
||||
|
||||
private String hostHeader = null;
|
||||
|
||||
@Override
|
||||
public void customize(HttpRequest.Builder builder, String method, URI endpoint, String body,
|
||||
McpTransportContext context) {
|
||||
if (originHeader != null) {
|
||||
builder.header("Origin", originHeader);
|
||||
}
|
||||
if (hostHeader != null) {
|
||||
// HttpClient normally sets Host automatically, but we can override it
|
||||
builder.header("Host", hostHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOriginHeader(String originHeader) {
|
||||
this.originHeader = originHeader;
|
||||
}
|
||||
|
||||
public void setHostHeader(String hostHeader) {
|
||||
this.hostHeader = hostHeader;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.originHeader = null;
|
||||
this.hostHeader = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue