Skip to content

Commit f178eb4

Browse files
authored
F/security headers (#61)
* Security headers!
1 parent 2fadca0 commit f178eb4

File tree

8 files changed

+144
-4
lines changed

8 files changed

+144
-4
lines changed

stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/handlers/CustomHandlers.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
import com.stubbornjava.common.HealthChecks;
1515
import com.stubbornjava.common.Metrics;
1616
import com.stubbornjava.common.undertow.Exchange;
17+
import com.stubbornjava.undertow.handlers.MiddlewareBuilder;
18+
import com.stubbornjava.undertow.handlers.ReferrerPolicyHandlers;
19+
import com.stubbornjava.undertow.handlers.ReferrerPolicyHandlers.ReferrerPolicy;
20+
import com.stubbornjava.undertow.handlers.StrictTransportSecurityHandlers;
21+
import com.stubbornjava.undertow.handlers.XContentTypeOptionsHandler;
22+
import com.stubbornjava.undertow.handlers.XFrameOptionsHandlers;
23+
import com.stubbornjava.undertow.handlers.XXssProtectionHandlers;
1724
import com.stubbornjava.undertow.handlers.accesslog.Slf4jAccessLogReceiver;
1825

1926
import io.undertow.Handlers;
@@ -144,4 +151,19 @@ public static HttpHandler loadBalancerHttpToHttps(HttpHandler next) {
144151
next.handleRequest(exchange);
145152
};
146153
}
154+
155+
public static HttpHandler securityHeaders(HttpHandler next, ReferrerPolicy policy) {
156+
MiddlewareBuilder security = MiddlewareBuilder
157+
.begin(XFrameOptionsHandlers::deny)
158+
.next(XXssProtectionHandlers::enableAndBlock)
159+
.next(XContentTypeOptionsHandler::nosniff)
160+
.next(handler -> ReferrerPolicyHandlers.policy(handler, policy));
161+
162+
// TODO: Only add HSTS if we are not local. We should probably
163+
// use a self signed cert locally for a better test env
164+
if (Env.LOCAL != Env.get()) {
165+
security = security.next(handler -> StrictTransportSecurityHandlers.hstsIncludeSubdomains(handler, 31536000L));
166+
}
167+
return security.complete(next);
168+
}
147169
}

stubbornjava-common/src/main/java/com/stubbornjava/common/undertow/handlers/Middleware.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package com.stubbornjava.common.undertow.handlers;
22

33
import com.stubbornjava.undertow.handlers.MiddlewareBuilder;
4+
import com.stubbornjava.undertow.handlers.ReferrerPolicyHandlers.ReferrerPolicy;
45

56
import io.undertow.server.HttpHandler;
67
import io.undertow.server.handlers.BlockingHandler;
78

89
public class Middleware {
910

1011
public static HttpHandler common(HttpHandler root) {
11-
return MiddlewareBuilder.begin(BlockingHandler::new)
12+
return MiddlewareBuilder.begin(handler -> CustomHandlers.securityHeaders(handler, ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
1213
.next(CustomHandlers::gzip)
14+
.next(BlockingHandler::new)
1315
.next(CustomHandlers::accessLog)
1416
.next(CustomHandlers::statusCodeMetrics)
1517
.complete(root);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.stubbornjava.undertow.handlers;
2+
3+
import io.undertow.server.HttpHandler;
4+
import io.undertow.server.handlers.SetHeaderHandler;
5+
6+
public class ReferrerPolicyHandlers {
7+
private static final String REFERRER_POLICY_STRING = "Referrer-Policy";
8+
9+
// See https://scotthelme.co.uk/a-new-security-header-referrer-policy/
10+
public enum ReferrerPolicy {
11+
EMPTY(""),
12+
NO_REFERRER("no-referrer"),
13+
NO_REFERRER_WHEN_DOWNGRADE("no-referrer-when-downgrade"),
14+
SAME_ORIGIN("same-origin"),
15+
ORIGIN("origin"),
16+
STRICT_ORIGIN("strict-origin"),
17+
ORIGIN_WHEN_CROSS_ORIGIN("origin-when-cross-origin"),
18+
STRICT_ORIGIN_WHEN_CROSS_ORIGIN("strict-origin-when-cross-origin"),
19+
UNSAFE_URL("unsafe-url");
20+
21+
private final String value;
22+
ReferrerPolicy(String value) {
23+
this.value = value;
24+
}
25+
public String getValue() {
26+
return value;
27+
}
28+
};
29+
public static HttpHandler policy(HttpHandler next, ReferrerPolicy policy) {
30+
return new SetHeaderHandler(next, REFERRER_POLICY_STRING, policy.getValue());
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.stubbornjava.undertow.handlers;
2+
3+
import io.undertow.server.HttpHandler;
4+
import io.undertow.server.handlers.SetHeaderHandler;
5+
import io.undertow.util.Headers;
6+
7+
public class StrictTransportSecurityHandlers {
8+
9+
public static HttpHandler hsts(HttpHandler next, long maxAge) {
10+
return new SetHeaderHandler(next, Headers.STRICT_TRANSPORT_SECURITY_STRING, "max-age=" + maxAge);
11+
}
12+
13+
public static HttpHandler hstsIncludeSubdomains(HttpHandler next, long maxAge) {
14+
return new SetHeaderHandler(next, Headers.STRICT_TRANSPORT_SECURITY_STRING, "max-age=" + maxAge + "; includeSubDomains");
15+
}
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.stubbornjava.undertow.handlers;
2+
3+
import io.undertow.server.HttpHandler;
4+
import io.undertow.server.handlers.SetHeaderHandler;
5+
6+
public class XContentTypeOptionsHandler {
7+
private static final String X_CONTENT_TYPE_OPTIONS_STRING = "X-Content-Type-Options";
8+
9+
public static HttpHandler nosniff(HttpHandler next) {
10+
return new SetHeaderHandler(next, X_CONTENT_TYPE_OPTIONS_STRING, "nosniff");
11+
}
12+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.stubbornjava.undertow.handlers;
2+
3+
import java.util.function.Function;
4+
5+
import io.undertow.server.HttpHandler;
6+
import io.undertow.server.HttpServerExchange;
7+
import io.undertow.server.handlers.SetHeaderHandler;
8+
import io.undertow.util.HttpString;
9+
10+
public class XFrameOptionsHandlers {
11+
private static final String X_FRAME_OPTIONS_STRING = "X-Frame-Options";
12+
private static final HttpString X_FRAME_OPTIONS = new HttpString(X_FRAME_OPTIONS_STRING);
13+
14+
public static HttpHandler deny(HttpHandler next) {
15+
return new SetHeaderHandler(next, X_FRAME_OPTIONS_STRING, "DENY");
16+
}
17+
18+
public static HttpHandler sameOrigin(HttpHandler next) {
19+
return new SetHeaderHandler(next, X_FRAME_OPTIONS_STRING, "SAMEORIGIN");
20+
}
21+
22+
public static HttpHandler allowFromOrigin(HttpHandler next, String origin) {
23+
return new SetHeaderHandler(next, X_FRAME_OPTIONS_STRING, "ALLOW-FROM " + origin);
24+
}
25+
26+
public static HttpHandler allowFromDynamicOrigin(HttpHandler next,
27+
Function<HttpServerExchange, String> originExtractor) {
28+
// Since this is dynamic skip using the SetHeaderHandler
29+
return exchange -> {
30+
exchange.getResponseHeaders().put(X_FRAME_OPTIONS, originExtractor.apply(exchange));
31+
next.handleRequest(exchange);
32+
};
33+
}
34+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.stubbornjava.undertow.handlers;
2+
3+
import io.undertow.server.HttpHandler;
4+
import io.undertow.server.handlers.SetHeaderHandler;
5+
6+
public class XXssProtectionHandlers {
7+
private static final String X_XSS_PROTECTION_STRING = "X-Xss-Protection";
8+
9+
public static HttpHandler disable(HttpHandler next) {
10+
return new SetHeaderHandler(next, X_XSS_PROTECTION_STRING, "0");
11+
}
12+
13+
public static HttpHandler enable(HttpHandler next) {
14+
return new SetHeaderHandler(next, X_XSS_PROTECTION_STRING, "1");
15+
}
16+
17+
public static HttpHandler enableAndBlock(HttpHandler next) {
18+
return new SetHeaderHandler(next, X_XSS_PROTECTION_STRING, "1; mode=block");
19+
}
20+
}

stubbornjava-webapp/src/main/java/com/stubbornjava/webapp/StubbornJavaWebApp.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.stubbornjava.common.undertow.SimpleServer;
1212
import com.stubbornjava.common.undertow.handlers.CustomHandlers;
1313
import com.stubbornjava.undertow.handlers.MiddlewareBuilder;
14+
import com.stubbornjava.undertow.handlers.ReferrerPolicyHandlers.ReferrerPolicy;
1415
import com.stubbornjava.webapp.guide.GuideRoutes;
1516
import com.stubbornjava.webapp.post.JavaLibRoutes;
1617
import com.stubbornjava.webapp.post.PostRoutes;
@@ -30,14 +31,15 @@ private static HttpHandler exceptionHandler(HttpHandler next) {
3031
.addExceptionHandler(Throwable.class, PageRoutes::error);
3132
}
3233

33-
private static HttpHandler wrapWithMiddleware(HttpHandler handler) {
34+
private static HttpHandler wrapWithMiddleware(HttpHandler next) {
3435
return MiddlewareBuilder.begin(PageRoutes::redirector)
35-
.next(BlockingHandler::new)
36+
.next(handler -> CustomHandlers.securityHeaders(handler, ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
3637
.next(CustomHandlers::gzip)
38+
.next(BlockingHandler::new)
3739
.next(ex -> CustomHandlers.accessLog(ex, logger))
3840
.next(CustomHandlers::statusCodeMetrics)
3941
.next(StubbornJavaWebApp::exceptionHandler)
40-
.complete(handler);
42+
.complete(next);
4143
}
4244

4345
// These routes do not require any authentication

0 commit comments

Comments
 (0)