[java-idp-oidc] branch main updated: JOIDC-179 - Add CSP protection to views
Henri Mikkonen
henri.mikkonen at iki.fi
Fri Mar 15 13:27:11 UTC 2024
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=abd5718cfd053a230d023c4a67dd8b0c3cb2fccd
The following commit(s) were added to refs/heads/main by this push:
new abd5718c JOIDC-179 - Add CSP protection to views
abd5718c is described below
commit abd5718cfd053a230d023c4a67dd8b0c3cb2fccd
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Mar 15 15:23:06 2024 +0200
JOIDC-179 - Add CSP protection to views
https://shibboleth.atlassian.net/browse/JOIDC-179
- Defined OPCSPDigester and OPCSPNonce global beans
- to be replaced with shibboleth.CSP -prefixed ones once we depend on 5.1+
- Wired those to the flows having view-states or end-states with views
- authorize -flow
- end-session -flow
- logoutprop -flow
- Add policy to oidc-form-post.vm
---
.../op/encoding/impl/NimbusResponseEncoder.java | 38 ++++++++++++++++++++++
.../META-INF/net.shibboleth.idp/postconfig.xml | 25 ++++++++++++++
.../oidc/oidc-logout-propagation-flow.xml | 2 ++
.../flows/oidc/abstract/oidc-abstract-beans.xml | 4 ++-
.../idp/flows/oidc/authorize/authorize-flow.xml | 8 +++++
.../flows/oidc/end-session/end-session-flow.xml | 14 ++++++++
.../src/main/resources/templates/oidc-form-post.vm | 8 +++--
7 files changed, 96 insertions(+), 3 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/encoding/impl/NimbusResponseEncoder.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/encoding/impl/NimbusResponseEncoder.java
index ac255841..8ede8ee5 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/encoding/impl/NimbusResponseEncoder.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/encoding/impl/NimbusResponseEncoder.java
@@ -41,7 +41,9 @@ import jakarta.servlet.http.HttpServletResponse;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.codec.HTMLEncoder;
+import net.shibboleth.shared.codec.StringDigester;
import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.security.IdentifierGenerationStrategy;
import net.shibboleth.shared.servlet.HttpServletSupport;
/**
@@ -65,6 +67,12 @@ public class NimbusResponseEncoder extends AbstractHttpServletResponseMessageEnc
/** Object mapper used for pretty-printing JSON response. */
@NonnullAfterInit private ObjectMapper objectMapper;
+ /** CSP digester for generating CSP hashes. */
+ @Nullable private StringDigester cspDigester;
+
+ /** CSP nonce generator. */
+ @Nullable private IdentifierGenerationStrategy cspNonceGenerator;
+
/** Constructor. */
public NimbusResponseEncoder() {
super();
@@ -108,6 +116,30 @@ public class NimbusResponseEncoder extends AbstractHttpServletResponseMessageEnc
objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
}
+ /**
+ * Sets a {@link StringDigester} to use in computing CSP digests in views.
+ *
+ * @param digester digester to set
+ *
+ * @since 4.1.0
+ */
+ public void setCSPDigester(@Nullable final StringDigester digester) {
+ checkSetterPreconditions();
+ cspDigester = digester;
+ }
+
+ /**
+ * Sets an {@link IdentifierGenerationStrategy} to use in computing CSP nonces in views.
+ *
+ * @param strategy nonce strategy
+ *
+ * @since 4.1.0
+ */
+ public void setCSPNonceGenerator(@Nullable final IdentifierGenerationStrategy strategy) {
+ checkSetterPreconditions();
+ cspNonceGenerator = strategy;
+ }
+
/**
* Whether we should use FORM POST response encoding.
*
@@ -131,6 +163,12 @@ public class NimbusResponseEncoder extends AbstractHttpServletResponseMessageEnc
context.put(entry.getKey(), entry.getValue().get(0));
}
context.put("action", HTMLEncoder.encodeForHTMLAttribute(message.getRedirectionURI().toString()));
+ if (cspDigester != null) {
+ context.put("cspDigester", cspDigester);
+ }
+ if (cspNonceGenerator != null) {
+ context.put("cspNonce", cspNonceGenerator);
+ }
return context;
}
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 2f08bb74..8db4a7bd 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -782,4 +782,29 @@
p:validator-ref="shibboleth.Conditions.TRUE"
abstract="true" />
+ <!-- TODO: OPCSP-prefixed beans temporarily defined here and used in views to calculate CSP hashes and nonces.
+ Switch into shibboleth.CSP -prefixed ones once we depend on 5.1+ -->
+
+ <bean id="OPCSPDigester" class="net.shibboleth.shared.codec.StringDigester"
+ c:algorithm="SHA256" c:format="BASE64" />
+
+ <bean id="OPCSPNonce" destroy-method=""
+ class="net.shibboleth.shared.security.IdentifierGenerationStrategy" factory-method="getInstance">
+ <constructor-arg>
+ <util:constant
+ static-field="net.shibboleth.shared.security.IdentifierGenerationStrategy.ProviderType.SECURE" />
+ </constructor-arg>
+ <constructor-arg>
+ <bean class="net.shibboleth.shared.security.RandomIdentifierParameterSpec"
+ c:identifierSize="16">
+ <constructor-arg name="source">
+ <null/>
+ </constructor-arg>
+ <constructor-arg name="identifierEncoder">
+ <null/>
+ </constructor-arg>
+ </bean>
+ </constructor-arg>
+ </bean>
+
</beans>
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/logoutprop/oidc/oidc-logout-propagation-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/logoutprop/oidc/oidc-logout-propagation-flow.xml
index f5e50501..b3038cbf 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/logoutprop/oidc/oidc-logout-propagation-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/logoutprop/oidc/oidc-logout-propagation-flow.xml
@@ -65,6 +65,8 @@
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.session.context.LogoutContext))" result="viewScope.logoutContext" />
<evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.profile.context.MultiRelyingPartyContext))" result="viewScope.multiRPContext" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')" result="viewScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')" result="viewScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
<evaluate expression="opensamlProfileRequestContext.ensureOutboundMessageContext().getMessage().toHTTPResponse().getHeaderValue('Location')" result="viewScope.frontChannelLogoutLocation" />
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml
index 8b55525c..b16e670f 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/abstract/oidc-abstract-beans.xml
@@ -54,7 +54,9 @@
<bean id="oidc.nimbusEncoder" class="net.shibboleth.idp.plugin.oidc.op.encoding.impl.NimbusResponseEncoder"
scope="prototype" p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier" init-method=""
p:velocityEngine-ref="shibboleth.VelocityEngine"
- p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"/>
+ p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}"
+ p:cSPDigester-ref="OPCSPDigester"
+ p:cSPNonceGenerator-ref="OPCSPNonce"/>
<bean id="EncodeMessage" class="org.opensaml.profile.action.impl.EncodeMessage" scope="prototype"
p:messageEncoderFactory-ref="oidc.messageEncoderFactory"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
index aaee3297..7dab858e 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
@@ -349,6 +349,10 @@
<evaluate expression="opensamlProfileRequestContext" result="requestScope.profileRequestContext" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)"
result="requestScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')"
+ result="requestScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')"
+ result="requestScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()"
result="requestScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()"
@@ -366,6 +370,10 @@
<evaluate expression="opensamlProfileRequestContext" result="requestScope.profileRequestContext" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)"
result="requestScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')"
+ result="requestScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')"
+ result="requestScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()"
result="requestScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()"
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-flow.xml
index a9ad01c8..da2fc539 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/end-session/end-session-flow.xml
@@ -145,6 +145,8 @@
<evaluate expression="opensamlProfileRequestContext.ensureOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCRpInitiatedLogoutContext))" result="viewScope.oidcRpInitiatedLogoutContext" />
<evaluate expression="opensamlProfileRequestContext.ensureOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCRpInitiatedLogoutContext)).getPostLogoutRedirectUri()" result="viewScope.postLogoutRedirectUri" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')" result="viewScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')" result="viewScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
<evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" />
@@ -177,6 +179,8 @@
<evaluate expression="opensamlProfileRequestContext.ensureOutboundMessageContext().ensureSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCRpInitiatedLogoutContext)).getPostLogoutRedirectUri()" result="viewScope.postLogoutRedirectUri" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.htmlEncoder" />
<evaluate expression="T(java.net.URLEncoder)" result="viewScope.urlEncoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')" result="viewScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')" result="viewScope.cspNonce" />
<evaluate expression="T(org.cryptacular.util.CodecUtil)" result="viewScope.codecUtil" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
@@ -197,6 +201,8 @@
<evaluate expression="opensamlProfileRequestContext.ensureOutboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCRpInitiatedLogoutContext))" result="viewScope.oidcRpInitiatedLogoutContext" />
<evaluate expression="opensamlProfileRequestContext.ensureOutboundMessageContext().ensureSubcontext(T(net.shibboleth.idp.plugin.oidc.op.messaging.context.OIDCRpInitiatedLogoutContext)).getPostLogoutRedirectUri()" result="viewScope.postLogoutRedirectUri" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)" result="viewScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')" result="viewScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')" result="viewScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()" result="viewScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()" result="viewScope.response" />
<evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().containsBean('shibboleth.CustomViewContext') ? flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.CustomViewContext') : null" result="viewScope.custom" />
@@ -247,6 +253,10 @@
<evaluate expression="opensamlProfileRequestContext" result="requestScope.profileRequestContext" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)"
result="requestScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')"
+ result="requestScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')"
+ result="requestScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()"
result="requestScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()"
@@ -264,6 +274,10 @@
<evaluate expression="opensamlProfileRequestContext" result="requestScope.profileRequestContext" />
<evaluate expression="T(net.shibboleth.shared.codec.HTMLEncoder)"
result="requestScope.encoder" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPDigester')"
+ result="requestScope.cspDigester" />
+ <evaluate expression="flowRequestContext.getActiveFlow().getApplicationContext().getBean('OPCSPNonce')"
+ result="requestScope.cspNonce" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeRequest()"
result="requestScope.request" />
<evaluate expression="flowRequestContext.getExternalContext().getNativeResponse()"
diff --git a/idp-oidc-extension-impl/src/main/resources/templates/oidc-form-post.vm b/idp-oidc-extension-impl/src/main/resources/templates/oidc-form-post.vm
index 57a324cf..eb62b0d8 100644
--- a/idp-oidc-extension-impl/src/main/resources/templates/oidc-form-post.vm
+++ b/idp-oidc-extension-impl/src/main/resources/templates/oidc-form-post.vm
@@ -2,6 +2,10 @@
## Velocity Template for OIDC Form Post response mode.
##
##
+#set ($onLoad = "document.forms[0].submit()")
+#if($cspDigester)
+ $response.addHeader("Content-Security-Policy","object-src 'none'; script-src 'none'; script-src-attr 'unsafe-hashes' 'sha256-$cspDigester.apply($onLoad)'")
+#end
<!DOCTYPE html>
<html>
@@ -9,7 +13,7 @@
<meta charset="utf-8" />
</head>
-<body onload="document.forms[0].submit()">
+<body onload="$onLoad">
<noscript>
<p>
<strong>Note:</strong> Since your browser does not support JavaScript, you must press the Continue button once to proceed.
@@ -45,4 +49,4 @@
</form>
</body>
-</html>
\ No newline at end of file
+</html>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list