-
Notifications
You must be signed in to change notification settings - Fork 704
Add support for DNS rebinding protections #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| package io.modelcontextprotocol.server.transport; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.HashSet; | ||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * Configuration for DNS rebinding protection in SSE server transports. Provides | ||
| * validation for Host and Origin headers to prevent DNS rebinding attacks. | ||
| */ | ||
| public class DnsRebindingProtectionConfig { | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| private final Set<String> allowedHosts; | ||
|
|
||
| private final Set<String> allowedOrigins; | ||
|
|
||
| private final boolean enableDnsRebindingProtection; | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| private DnsRebindingProtectionConfig(Builder builder) { | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.allowedHosts = Collections.unmodifiableSet(new HashSet<>(builder.allowedHosts)); | ||
| this.allowedOrigins = Collections.unmodifiableSet(new HashSet<>(builder.allowedOrigins)); | ||
| this.enableDnsRebindingProtection = builder.enableDnsRebindingProtection; | ||
| } | ||
|
|
||
| /** | ||
| * Validates Host and Origin headers for DNS rebinding protection. Returns true if the | ||
| * headers are valid, false otherwise. | ||
| * @param hostHeader The value of the Host header (may be null) | ||
| * @param originHeader The value of the Origin header (may be null) | ||
| * @return true if the headers are valid, false otherwise | ||
| */ | ||
| public boolean validate(String hostHeader, String originHeader) { | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // Skip validation if protection is not enabled | ||
| if (!enableDnsRebindingProtection) { | ||
| return true; | ||
| } | ||
|
|
||
| // Validate Host header | ||
| if (hostHeader != null) { | ||
| String lowerHost = hostHeader.toLowerCase(); | ||
| if (!allowedHosts.contains(lowerHost)) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| // Validate Origin header | ||
| if (originHeader != null) { | ||
| String lowerOrigin = originHeader.toLowerCase(); | ||
| if (!allowedOrigins.contains(lowerOrigin)) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| public static Builder builder() { | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return new Builder(); | ||
| } | ||
|
|
||
| public static class Builder { | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| private final Set<String> allowedHosts = new HashSet<>(); | ||
|
|
||
| private final Set<String> allowedOrigins = new HashSet<>(); | ||
|
||
|
|
||
| private boolean enableDnsRebindingProtection = true; | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| public Builder allowedHost(String host) { | ||
| if (host != null) { | ||
| this.allowedHosts.add(host.toLowerCase()); | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| public Builder allowedHosts(Set<String> hosts) { | ||
| if (hosts != null) { | ||
| hosts.forEach(this::allowedHost); | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| public Builder allowedOrigin(String origin) { | ||
| if (origin != null) { | ||
| this.allowedOrigins.add(origin.toLowerCase()); | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| public Builder allowedOrigins(Set<String> origins) { | ||
| if (origins != null) { | ||
| origins.forEach(this::allowedOrigin); | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| public Builder enableDnsRebindingProtection(boolean enable) { | ||
ddworken marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.enableDnsRebindingProtection = enable; | ||
| return this; | ||
| } | ||
|
|
||
| public DnsRebindingProtectionConfig build() { | ||
| return new DnsRebindingProtectionConfig(this); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.