Java tutorial
/* * This file is part of Alpine. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright (c) Steve Springett. All Rights Reserved. */ package alpine.filters; import org.apache.commons.lang3.StringUtils; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * <p> * Implements W3C Content Security Policy (Level 1 and 2). * </p> * * <p> * This filter is configured via the applications web.xml. * </p> * * An example implementation in web.xml: * * <pre> * <filter> * <filter-name>CspFilter</filter-name> * <filter-class>alpine.filters.ContentSecurityPolicyFilter</filter-class> * <init-param> * <param-name>default-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>script-src</param-name> * <param-value>'self' 'unsafe-inline'</param-value> * </init-param> * <init-param> * <param-name>style-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>img-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>connect-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>font-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>object-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>media-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>frame-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>sandbox</param-name> * <param-value>allow-forms</param-value> * </init-param> * <init-param> * <param-name>report-uri</param-name> * <param-value>/some-report-uri</param-value> * </init-param> * <init-param> * <param-name>child-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>form-action-src</param-name> * <param-value>'self'</param-value> * </init-param> * <init-param> * <param-name>frame-ancestors</param-name> * <param-value>'none'</param-value> * </init-param> * <init-param> * <param-name>plugin-types</param-name> * <param-value>application/pdf</param-value> * </init-param> * </filter> * <filter-mapping> * <filter-name>CspFilter</filter-name> * <url-pattern>/*</url-pattern> * </filter-mapping> * </pre> * * <p> * The following parameters default to 'self' if not defined: * default-src, script-src, style-src, img-src, font-src, object-src, media-src, child-src and form-action. *</p> * <p> * The sandbox param defaults to null indicating that the default sandbox will be applied. The report-uri and * plugin-types also default to null. frame-ancestors defaults to 'none' if not specified. * </p> * * @author Steve Springett * @since 1.0.0 */ public final class ContentSecurityPolicyFilter implements Filter { private static final String SELF = "'self'"; private static final String NONE = "'none'"; private String policy = null; private String defaultSrc = SELF; private String scriptSrc = SELF; private String styleSrc = SELF; private String imgSrc = SELF; private String connectSrc = SELF; private String fontSrc = SELF; private String objectSrc = SELF; private String mediaSrc = SELF; private String frameSrc = SELF; private String sandbox = null; private String reportUri = null; private String childSrc = SELF; private String formAction = SELF; private String frameAncestors = NONE; private String pluginTypes = null; @Override public void init(final FilterConfig filterConfig) { defaultSrc = getValue(filterConfig, "default-src", defaultSrc); scriptSrc = getValue(filterConfig, "script-src", scriptSrc); styleSrc = getValue(filterConfig, "style-src", styleSrc); imgSrc = getValue(filterConfig, "img-src", imgSrc); connectSrc = getValue(filterConfig, "connect-src", connectSrc); fontSrc = getValue(filterConfig, "font-src", fontSrc); objectSrc = getValue(filterConfig, "object-src", objectSrc); mediaSrc = getValue(filterConfig, "media-src", mediaSrc); frameSrc = getValue(filterConfig, "frame-src", frameSrc); sandbox = getValue(filterConfig, "sandbox", sandbox); reportUri = getValue(filterConfig, "report-uri", reportUri); childSrc = getValue(filterConfig, "child-src", childSrc); formAction = getValue(filterConfig, "form-action", formAction); frameAncestors = getValue(filterConfig, "frame-ancestors", frameAncestors); pluginTypes = getValue(filterConfig, "plugin-types", pluginTypes); policy = formatHeader(); } /** * Returns the value of the initParam. * @param filterConfig a FilterConfig instance * @param initParam the name of the init parameter * @param variable the variable to use if the init param was not defined * @return a String */ private String getValue(FilterConfig filterConfig, String initParam, String variable) { final String value = filterConfig.getInitParameter(initParam); if (StringUtils.isNotBlank(value)) { return value; } else { return variable; } } /** * Formats a CSP header * @return a String representation of CSP header */ private String formatHeader() { final StringBuilder sb = new StringBuilder(); getStringFromValue(sb, "default-src", defaultSrc); getStringFromValue(sb, "script-src", scriptSrc); getStringFromValue(sb, "style-src", styleSrc); getStringFromValue(sb, "img-src", imgSrc); getStringFromValue(sb, "connect-src", connectSrc); getStringFromValue(sb, "font-src", fontSrc); getStringFromValue(sb, "object-src", objectSrc); getStringFromValue(sb, "media-src", mediaSrc); getStringFromValue(sb, "frame-src", frameSrc); getStringFromValue(sb, "sandbox", sandbox); getStringFromValue(sb, "report-uri", reportUri); getStringFromValue(sb, "child-src", childSrc); getStringFromValue(sb, "form-action", formAction); getStringFromValue(sb, "frame-ancestors", frameAncestors); getStringFromValue(sb, "plugin-types", pluginTypes); return sb.toString().replaceAll("(\\[|\\])", "").trim(); } /** * Assists in the formatting of a single CSP directive. * @param builder a StringBuilder object * @param directive a CSP directive * @param value the value of the CSP directive */ private void getStringFromValue(final StringBuilder builder, final String directive, final String value) { if (value != null) { builder.append(directive).append(" ").append(value).append(";"); } } @Override public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException { final HttpServletResponse response = (HttpServletResponse) res; chain.doFilter(req, response); response.addHeader("Content-Security-Policy", policy); } @Override public void destroy() { // Intentionally empty to satisfy interface } }