Content Security Policy

From Documentation


Content Security Policy


Overview

Content-security-policy (CSP) is a security standard which helps to prevent XSS attacks (cross-site scripting) and other content injection attacks. We could configure our web server to return the CSP HTTP header, or use <meta> element. After CSP is enabled, it would restrict contents that could be loaded from.

See more: Content Security Policy Level 2

CSP Implementations and limitations in ZK

To implement CSP, there are several issues in ZK according to the CSP spec.

1. CSP blocks the use of the inline <script> source.

There are several inline scripts created and removed immediately during the ZK page initialization. Attached a custom PageRenderer and AuExtension which allows removing the script-src 'unsafe-inline' policy.

2. CSP blocks the use of eval() and new Function() in javascript.

It is necessary for ZK, because ZK evaluates the returned JSON like JS object from almost every AU response.

3.CSP blocks the use of the inline source in style attribute by default.

To achieve the dimension or size calculation in ZK components, changing the style of DOM element is necessary.

4.(Optional) Iframe and WebSocket issue

Iframe-src is currently needed for file upload and download.

If we want to use WebSocket in ZK, we need to specify the connection of WebSocket URL.

To avoid all those errors the policies would need to be loosened like that:

<?header name="Content-Security-Policy-Report-Only"
    value="default-src 'none';
    script-src 'self' 'unsafe-eval';
    frame-src 'self'; connect-src 'self' ws://your.server.name:8080/;
    img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self';" ?>

Custom setting of PageRenderer and AuExtension

<listener>
	<listener-class>zk.custom.AuCsp</listener-class>
</listener>
<listener>
	<listener-class>zk.custom.CspPageRenderer</listener-class>
</listener>

Custom PageRenderer

public class CspPageRenderer extends PageRenderer implements Initiator {

	public static final String SCRIPT_START = "<script class=\"z-runonce\" type=\"text/javascript\">";
	public static final String SCRIPT_END = "</script>";

	@Override
	public void doInit(Page page, Map<String, Object> args) throws Exception {
		Executions.getCurrent().setAttribute(PAGE_RENDERER, this);
	}

	@Override
	protected void renderDesktop(Execution exec, Page page, Writer out) throws IOException {
		StringBuilder renderBuffer = renderDesktopToBuffer(exec, page);

		int begin = renderBuffer.indexOf(SCRIPT_START);
		int beginAfterTag = begin + SCRIPT_START.length();
		int end = renderBuffer.indexOf(SCRIPT_END, begin);
		int endAfterTag = end + SCRIPT_END.length();

		String desktopRenderScript = renderBuffer.substring(beginAfterTag, end);
		String desktopRenderScriptUrl = AuCsp.createUrlForDesktopRenderScript(page.getDesktop(), desktopRenderScript);

		out.write(renderBuffer.substring(0, begin));
		out.write("<script class=\"z-runonce\" src=\"" + desktopRenderScriptUrl + "\"></script>");
		out.write(renderBuffer.substring(endAfterTag));
	}

	private StringBuilder renderDesktopToBuffer(Execution exec, Page page) throws IOException {
		StringBuilder sb = new StringBuilder();
		StringBuilderWriter outBuffer = new StringBuilderWriter(sb);

		//render to a buffer first
		super.renderDesktop(exec, page, outBuffer);
		return sb;
	}
}

Custom AuExtension

public class AuCsp implements AuExtension, WebAppInit {
	private static final Logger log = LoggerFactory.getLogger(AuRedirect.class);

	public static final String DESKTOP_RENDER_SCRIPT = "desktopRenderScript";
	public static final String URI_PREFIX = "/desktopRender";

	@Override
	public void init(WebApp wapp) throws Exception {
		if (DHtmlUpdateServlet.getAuExtension(wapp, URI_PREFIX) == null) {
			try {
				DHtmlUpdateServlet.addAuExtension(wapp, URI_PREFIX, this);
			} catch (Throwable ex) {
				log.error("could not initialize AuCsp extension", ex);
				throw new IllegalStateException("could not initialize AuCsp extension", ex);
			}
		}
	}

	@Override
	public void init(DHtmlUpdateServlet servlet) throws ServletException {
	}

	@Override
	public void destroy() {
	}

	@Override
	public void service(HttpServletRequest request, HttpServletResponse response, String pi)
	throws ServletException, IOException {
		DesktopCache desktopCache = ((SessionCtrl) Sessions.getCurrent()).getDesktopCache();
		Desktop desktop = desktopCache.getDesktop(pi.substring(URI_PREFIX.length() + 1));
		response.getWriter().write(String.valueOf(desktop.removeAttribute(DESKTOP_RENDER_SCRIPT)));
	}

	public static String createUrlForDesktopRenderScript(Desktop desktop, String desktopRenderScript) {
		desktop.setAttribute(DESKTOP_RENDER_SCRIPT, desktopRenderScript);
		return desktop.getUpdateURI(URI_PREFIX + "/" + desktop.getId());
	}
}

Version History

Last Update : 2018/03/13


Version Date Content
     



Last Update : 2018/03/13

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.