You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pa...@apache.org on 2020/01/19 20:36:24 UTC
[wicket] branch csp updated: WICKET-6727: Updated documentation,
fixed websocket, fixed examples
This is an automated email from the ASF dual-hosted git repository.
papegaaij pushed a commit to branch csp
in repository https://gitbox.apache.org/repos/asf/wicket.git
The following commit(s) were added to refs/heads/csp by this push:
new ea1e129 WICKET-6727: Updated documentation, fixed websocket, fixed examples
ea1e129 is described below
commit ea1e129b94b1327c35a0c31ae7631e27d8c540d4
Author: Emond Papegaaij <em...@topicus.nl>
AuthorDate: Sun Jan 19 21:36:04 2020 +0100
WICKET-6727: Updated documentation, fixed websocket, fixed examples
---
.../java/org/apache/wicket/csp/CSPDirective.java | 12 +++--
.../apache/wicket/csp/CSPHeaderConfiguration.java | 60 ++++++++++++++++++----
.../wicket/csp/ContentSecurityPolicyEnforcer.java | 44 ++++++++--------
.../org/apache/wicket/csp/FixedCSPDirective.java | 5 +-
.../org/apache/wicket/mock/MockWebResponse.java | 6 +++
.../org/apache/wicket/page/PartialPageUpdate.java | 6 +++
.../wicket/protocol/http/BufferedWebResponse.java | 6 +++
.../protocol/http/HeaderBufferingWebResponse.java | 6 +++
.../protocol/http/servlet/ServletWebResponse.java | 6 +++
.../wicket/examples/WicketExampleApplication.java | 3 ++
.../apache/wicket/examples/WicketExamplePage.html | 4 +-
.../CustomLoadedTemplate.html | 4 +-
.../wicket/protocol/ws/api/WebSocketResponse.java | 5 ++
.../apache/wicket/request/http/WebResponse.java | 9 ++++
14 files changed, 135 insertions(+), 41 deletions(-)
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java
index 1ea52c1..0edf6b9 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPDirective.java
@@ -40,8 +40,11 @@ public enum CSPDirective
MEDIA_SRC("media-src"),
CHILD_SRC("child-src"),
FRAME_ANCESTORS("frame-ancestors"),
+ /**
+ * @deprecated Use CHILD-SRC, this will also add FRAME-SRC automatically for compatibility with
+ * older browsers.
+ */
@Deprecated
- /** @deprecated Gebruik CHILD-SRC, deze zet ook automatisch FRAME-SRC. */
FRAME_SRC("frame-src"),
SANDBOX("sandbox")
{
@@ -156,8 +159,7 @@ public enum CSPDirective
}
// strip off "*." so "*.example.com" becomes "example.com" and we can check if
- // it
- // is a valid uri
+ // it is a valid uri
if (strValue.startsWith("*."))
{
strValue = strValue.substring(2);
@@ -180,11 +182,15 @@ public enum CSPDirective
public static CSPDirective fromValue(String value)
{
if (Strings.isEmpty(value))
+ {
return null;
+ }
for (int i = 0; i < values().length; i++)
{
if (value.equals(values()[i].getValue()))
+ {
return values()[i];
+ }
}
return null;
}
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
index 91521ba..6bee9fd 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/CSPHeaderConfiguration.java
@@ -40,6 +40,10 @@ import java.util.stream.Collectors;
import org.apache.wicket.request.cycle.RequestCycle;
/**
+ * {@code CSPHeaderConfiguration} contains the configuration for a Content-Security-Policy header.
+ * This configuration is constructed using the available {@link CSPDirective}s. An number of default
+ * profiles is provided. These profiles can be used as a basis for a specific CSP. Extra directives
+ * can be added or exising directives modified.
*
* @author papegaaij
*/
@@ -54,33 +58,69 @@ public class CSPHeaderConfiguration
public CSPHeaderConfiguration()
{
}
-
- public CSPHeaderConfiguration disabled() {
+
+ /**
+ * Removes all directives from the CSP, returning an empty configuration.
+ *
+ * @return {@code this} for chaining.
+ */
+ public CSPHeaderConfiguration disabled()
+ {
return clear();
}
-
+
+ /**
+ * Builds a CSP configuration with the following directives: {@code default-src 'none';}
+ * {@code script-src 'self' 'unsafe-inline' 'unsafe-eval';}
+ * {@code style-src 'self' 'unsafe-inline';} {@code img-src 'self';} {@code connect-src 'self';}
+ * {@code font-src 'self';} {@code manifest-src 'self';} {@code child-src 'self';}
+ * {@code frame-src 'self'}. This will allow resources to be loaded from {@code 'self'} (the
+ * current host). In addition, unsafe inline Javascript, {@code eval()} and inline CSS is
+ * allowed.
+ *
+ * It is recommended to not allow {@code unsafe-inline} or {@code unsafe-eval}, because those
+ * can be used to trigger XSS attacks in your application (often in combination with another
+ * bug). Because older application often rely on inline scripting and styling, this CSP can be
+ * used as a stepping stone for older Wicket applications, before switching to {@link #strict}.
+ * Using a CSP with unsafe directives is still more secure than using no CSP at all.
+ *
+ * @return {@code this} for chaining.
+ */
public CSPHeaderConfiguration unsafeInline()
{
return clear().addDirective(DEFAULT_SRC, NONE)
- .addDirective(STYLE_SRC, SELF, UNSAFE_INLINE)
.addDirective(SCRIPT_SRC, SELF, UNSAFE_INLINE, UNSAFE_EVAL)
+ .addDirective(STYLE_SRC, SELF, UNSAFE_INLINE)
.addDirective(IMG_SRC, SELF)
+ .addDirective(CONNECT_SRC, SELF)
.addDirective(FONT_SRC, SELF)
- .addDirective(CHILD_SRC, SELF)
.addDirective(MANIFEST_SRC, SELF)
- .addDirective(CONNECT_SRC, SELF);
+ .addDirective(CHILD_SRC, SELF);
}
-
+
+ /**
+ * Builds a strict, very secure CSP configuration with the following directives:
+ * {@code default-src 'none';} {@code script-src 'strict-dynamic' 'nonce-XYZ';}
+ * {@code style-src 'nonce-XYZ';} {@code img-src 'self';} {@code connect-src 'self';}
+ * {@code font-src 'self';} {@code manifest-src 'self';} {@code child-src 'self';}
+ * {@code frame-src 'self'}. This will allow most resources to be loaded from {@code 'self'}
+ * (the current host). Scripts and styles are only allowed when rendered with the correct nonce.
+ * Wicket will automatically add the nonces to the {@code script} and {@code link} (CSS)
+ * elements and to the headers.
+ *
+ * @return {@code this} for chaining.
+ */
public CSPHeaderConfiguration strict()
{
return clear().addDirective(DEFAULT_SRC, NONE)
+ .addDirective(SCRIPT_SRC, STRICT_DYNAMIC, NONCE)
.addDirective(STYLE_SRC, NONCE)
- .addDirective(SCRIPT_SRC, STRICT_DYNAMIC, NONCE)
.addDirective(IMG_SRC, SELF)
+ .addDirective(CONNECT_SRC, SELF)
.addDirective(FONT_SRC, SELF)
- .addDirective(CHILD_SRC, SELF)
.addDirective(MANIFEST_SRC, SELF)
- .addDirective(CONNECT_SRC, SELF);
+ .addDirective(CHILD_SRC, SELF)
+ ;
}
/**
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java b/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java
index 5fb65e7..dad360c 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/ContentSecurityPolicyEnforcer.java
@@ -36,32 +36,21 @@ import org.apache.wicket.util.lang.Args;
* An {@link IRequestCycleListener} that adds {@code Content-Security-Policy} and/or
* {@code Content-Security-Policy-Report-Only} headers based on the supplied configuration.
*
- * See also the {@code CSPSettingRequestCycleListenerTest}.
- *
- * Example usage:
- *
+ * Build the CSP configuration like this:
+ *
* <pre>
* {@code
- * myApplication.getRequestCycleListeners().add(
- * new CSPSettingRequestCycleListener()
- * .addBlockingDirective(CSPDirective.DEFAULT_SRC, CSPDirectiveSrcValue.NONE)
- * .addBlockingDirective(CSPDirective.SCRIPT_SRC, CSPDirectiveSrcValue.SELF)
- * .addBlockingDirective(CSPDirective.IMG_SRC, CSPDirectiveSrcValue.SELF)
- * .addBlockingDirective(CSPDirective.FONT_SRC, CSPDirectiveSrcValue.SELF));
+ * myApplication.getCSP().blocking().clear()
+ * .addDirective(CSPDirective.DEFAULT_SRC, CSPDirectiveSrcValue.NONE)
+ * .addDirective(CSPDirective.SCRIPT_SRC, CSPDirectiveSrcValue.SELF)
+ * .addDirective(CSPDirective.IMG_SRC, CSPDirectiveSrcValue.SELF)
+ * .addDirective(CSPDirective.FONT_SRC, CSPDirectiveSrcValue.SELF));
*
- * myApplication.getRequestCycleListeners().add(
- * new CSPSettingRequestCycleListener()
- * .addReportingDirective(CSPDirective.DEFAULT_SRC, CSPDirectiveSrcValue.NONE)
- * .addReportingDirective(CSPDirective.IMG_SRC, CSPDirectiveSrcValue.SELF)
- * .addReportingDirective(CSPDirective.FONT_SRC, CSPDirectiveSrcValue.SELF)
- * .addReportingDirective(CSPDirective.SCRIPT_SRC, CSPDirectiveSrcValue.SELF));
+ * myApplication.getCSP().reporting().strict();
* }
* </pre>
- *
- * {@code frame-src} has been deprecated since CSP 2.0 and replaced by {@code child-src}. Some
- * browsers do not yet support {@code child-src} and expect {@code frame-src} instead. When
- * {@code child-src} is added, a matching {@code frame-src} is added automatically for
- * compatibility.
+ *
+ * See {@link CSPHeaderConfiguration} for more details on specifying the configuration.
*
* @see "http://www.w3.org/TR/CSP2/"
* @see "https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives"
@@ -114,7 +103,9 @@ public class ContentSecurityPolicyEnforcer implements IRequestCycleListener
protected boolean mustProtect(IRequestHandler handler)
{
if (handler instanceof IRequestHandlerDelegate)
+ {
return mustProtect(((IRequestHandlerDelegate) handler).getDelegateHandler());
+ }
if (handler instanceof IPageClassRequestHandler)
{
return mustProtectPageRequest((IPageClassRequestHandler) handler);
@@ -131,16 +122,25 @@ public class ContentSecurityPolicyEnforcer implements IRequestCycleListener
public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
{
if (!mustProtect(handler) || !(cycle.getResponse() instanceof WebResponse))
+ {
return;
-
+ }
+
WebResponse webResponse = (WebResponse) cycle.getResponse();
+ if (!webResponse.isHeaderSupported())
+ {
+ return;
+ }
+
configs.entrySet().stream().filter(entry -> entry.getValue().isSet()).forEach(entry -> {
CSPHeaderMode mode = entry.getKey();
CSPHeaderConfiguration config = entry.getValue();
String headerValue = config.renderHeaderValue(this, cycle);
webResponse.setHeader(mode.getHeader(), headerValue);
if (config.isAddLegacyHeaders())
+ {
webResponse.setHeader(mode.getLegacyHeader(), headerValue);
+ }
});
}
diff --git a/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java b/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
index a091b6a..dc9865c 100644
--- a/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
+++ b/wicket-core/src/main/java/org/apache/wicket/csp/FixedCSPDirective.java
@@ -37,8 +37,9 @@ public class FixedCSPDirective implements CSPRenderable
public FixedCSPDirective(String value)
{
if (Strings.isEmpty(value))
- throw new IllegalArgumentException(
- "CSP directive cannot have empty or null values");
+ {
+ throw new IllegalArgumentException("CSP directive cannot have empty or null values");
+ }
this.value = value;
}
diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java
index ba65975..6021e92 100644
--- a/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java
+++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockWebResponse.java
@@ -166,6 +166,12 @@ public class MockWebResponse extends WebResponse
}
@Override
+ public boolean isHeaderSupported()
+ {
+ return true;
+ }
+
+ @Override
public void setHeader(String name, String value)
{
internalSetContentType(name, value);
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
index 34f77ef..cd777ac 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
@@ -806,6 +806,12 @@ public abstract class PartialPageUpdate
}
@Override
+ public boolean isHeaderSupported()
+ {
+ return originalResponse.isHeaderSupported();
+ }
+
+ @Override
public void setHeader(String name, String value)
{
originalResponse.setHeader(name, value);
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java
index 23de6ea..43458e0 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/BufferedWebResponse.java
@@ -193,6 +193,12 @@ public class BufferedWebResponse extends WebResponse implements IMetaDataBufferi
}
@Override
+ public boolean isHeaderSupported()
+ {
+ return originalResponse.isHeaderSupported();
+ }
+
+ @Override
public void setHeader(String name, String value)
{
actions.add(ActionType.HEADER.action(res -> res.setHeader(name, value)));
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java
index 993f959..8e31f4e 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/HeaderBufferingWebResponse.java
@@ -136,6 +136,12 @@ class HeaderBufferingWebResponse extends WebResponse implements IMetaDataBufferi
}
@Override
+ public boolean isHeaderSupported()
+ {
+ return getMetaResponse().isHeaderSupported();
+ }
+
+ @Override
public void setHeader(String name, String value)
{
getMetaResponse().setHeader(name, value);
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java
index 8f11fa0..fce9056 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebResponse.java
@@ -89,6 +89,12 @@ public class ServletWebResponse extends WebResponse
}
@Override
+ public boolean isHeaderSupported()
+ {
+ return true;
+ }
+
+ @Override
public void setHeader(String name, String value)
{
httpServletResponse.setHeader(name, value);
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExampleApplication.java b/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExampleApplication.java
index 3794cbf..ee1180b 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExampleApplication.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExampleApplication.java
@@ -16,6 +16,7 @@
*/
package org.apache.wicket.examples;
+import org.apache.wicket.csp.CSPDirective;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.resource.CssUrlReplacer;
import org.apache.wicket.settings.SecuritySettings;
@@ -57,5 +58,7 @@ public abstract class WicketExampleApplication extends WebApplication
getDebugSettings().setDevelopmentUtilitiesEnabled(true);
getResourceSettings().setCssCompressor(new CssUrlReplacer());
+ getCsp().blocking().addDirective(CSPDirective.STYLE_SRC, "https://maxcdn.bootstrapcdn.com")
+ .addDirective(CSPDirective.FONT_SRC, "https://maxcdn.bootstrapcdn.com");
}
}
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExamplePage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExamplePage.html
index b07d0a3..d57c44d 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExamplePage.html
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/WicketExamplePage.html
@@ -8,7 +8,7 @@
<link rel="shortcut icon" href="favicon.ico"
type="image/vnd.microsoft.icon" />
<link
- href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
+ href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
rel="stylesheet" />
</head>
@@ -28,4 +28,4 @@
</div>
</main>
</body>
-</html>
\ No newline at end of file
+</html>
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/customresourceloading/CustomLoadedTemplate.html b/wicket-examples/src/main/java/org/apache/wicket/examples/customresourceloading/CustomLoadedTemplate.html
index af4d41d..7d119c1 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/customresourceloading/CustomLoadedTemplate.html
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/customresourceloading/CustomLoadedTemplate.html
@@ -9,7 +9,7 @@
<link rel="shortcut icon" href="favicon.ico"
type="image/vnd.microsoft.icon" />
<link
- href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
+ href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"
rel="stylesheet" />
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
@@ -42,4 +42,4 @@
</div>
</main>
</body>
-</html>
\ No newline at end of file
+</html>
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java
index 325eeab..af1c938 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java
@@ -150,6 +150,11 @@ public class WebSocketResponse extends WebResponse
}
@Override
+ public boolean isHeaderSupported() {
+ return false;
+ }
+
+ @Override
public void setHeader(String name, String value)
{
throw new UnsupportedOperationException();
diff --git a/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java b/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
index 322f4ff..89a1b3e 100644
--- a/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
+++ b/wicket-request/src/main/java/org/apache/wicket/request/http/WebResponse.java
@@ -53,6 +53,15 @@ public abstract class WebResponse extends Response
public abstract void clearCookie(final Cookie cookie);
/**
+ * Indicates if the response supports setting headers. When this method returns
+ * false, {@link #setHeader(String, String)} and its variations will thrown an
+ * {@code UnsupportedOperationException}.
+ *
+ * @return True when this {@code WebResponse} supports setting headers.
+ */
+ public abstract boolean isHeaderSupported();
+
+ /**
* Set a header to the string value in the servlet response stream.
*
* @param name