Search code examples
javascriptjavasecuritystruts2content-security-policy

HTTP Content-Security-Policy header not working correctly for script-src in Struts


I have a problem updating my version of struts struts2-core-2.5.30 project to struts2-core-6.1.1 so I began to receive an error indicating that the security policies have been violated, doing some research, I found that a header should be added

[Report Only] Refused to load the script '' because it violates the following Content Security Policy directive: "script-src 'nonce-MOz6w31eaDHGUDfV__K8LEZ1' 'strict-dynamic' http: https:". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

and inside this error I have this description

[Report Only] Refused to load the script http://localhost:8080/Portal/html/js/jquery/jquery-1.8.3.min.js because it violates the following Content Security Policy directive: "script-src 'nonce-MOz6w31eaDHGUDfV__K8LEZ1' 'strict-dynamic' http: https:". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

[Report Only] Refused to load the script http://localhost:8080/Portal/html/js/jquery/jquery-ui.1.10.4.min.js because it violates the following Content Security Policy directive: "script-src 'nonce-MOz6w31eaDHGUDfV__K8LEZ1' 'strict-dynamic' http: https:". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

however I have tried these headers

<meta http-equiv="Content-Security-Policy" content="default-src 'self'">


<meta http-equiv="Content-Security-Policy" content="default-src *;
    style-src * 'unsafe-inline'; script-src * 'unsafe-inline'
    'unsafe-eval'; img-src * data: 'unsafe-inline'; connect-src *
    'unsafe-inline'; frame-src *;">

<meta http-equiv="Content-Security-Policy" content="default-src  'nonce-rAnd0m'">
<script src="${pageContext.request.contextPath}/html/js/jquery/jquery-1.8.3.min.js" type="text/javascript" nonce="rAnd0m123"></script> 

with each of them I get the same error, In my previous version of Struts it did not ask me for any of this.

I have also tried to make an interceptor to add the corresponding directives, however it did not work for me either.

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.StrutsStatics;


public class SessionInterceptor extends AbstractInterceptor{

   private static final long serialVersionUID = 1L;


   public String intercept(ActionInvocation invocation) throws Exception {
     
    ActionContext ac = invocation.getInvocationContext();
    HttpServletResponse response = (HttpServletResponse) ac.get(StrutsStatics.HTTP_RESPONSE);
    //HttpServletResponse response = ServletActionContext.getResponse();

    response.addHeader("X-Frame-Options", "SAMEORIGIN");
    response.addHeader("Content-Security-Policy-Report-Only", "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'none'; frame-src 'none'; font-src 'self'; connect-src 'self'; report-uri REDACTED");
    response.addHeader("X-Content-Security-Policy-Report-Only", "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'none'; frame-src 'none'; font-src 'self'; connect-src 'self'; report-uri REDACTED");
    return invocation.invoke();
}

}

In the same way I have updated the jquery-1.8.3 version as suggested in the comments but it dod not worked for me either


Solution

  • Struts provides support for Content-Security-Policy since version 6.x.

    The functionality is implemented primarily in CspInterceptor.

    This interceptor is configured by providing a convenient default implementation of CspSettings.

    This interceptor is included by default in the Struts configuration. As you can see in the linked resource and in the documentation by default is configured in report only, non enforcing mode:

    <interceptor-ref name="csp">
      <param name="disabled">false</param>
      <param name="enforcingMode">false</param>
    </interceptor-ref>
    

    I reviewed the current source code of the library and the companion documentation and it seems that providing a custom CSP configuration to CspInterceptor is not possible right now.

    That means that in order to mitigate your error one possibility will be to disable the CspInterceptor and provide your own. The Struts documentation provides guidance about how it could be accomplished. In your case, I think it should look like similar to the following:

    <action name="myAction" class="myActionClass">
        <interceptor-ref name="defaultStack">
            <param name="csp.disabled">true</param>
        </interceptor-ref>
    </action>
    

    In addition, in the commit I cited at the beginning of the answer they mention the components and corresponding tags s:link and s:script as a possible way for fetching the required CSS and Javascript resources taking into account the default CSP settings: basically they provide the necessary mechanisms for taking into account the appropriate nonces for the linked resources, required according to the policy applied. Please, consider review for instance this page in the showcase provided by Struts as example, reproduced here for convenience:

    <%@taglib prefix="s" uri="/struts-tags" %>
    
    
    <html lang="en">
    <head>
        <!-- content removed for brevity -->
    
        <s:url var="jqueryJs" value='/js/jquery-2.1.4.min.js' encode='false' includeParams='none'/>
        <s:script src="%{jqueryJs}"/>
        <!-- other resources... now handling inline sources --> 
        <s:script type="text/javascript">
            $(function () {
                var alerts = $('ul.alert').wrap('<div />');
                alerts.prepend('<a class="close" data-dismiss="alert" href="#">&times;</a>');
                alerts.alert();
            });
        </s:script>
    
    
        <!-- ... -->
    </head>