You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by th...@apache.org on 2019/09/24 16:53:00 UTC

[nifi-registry] branch NIFIREG-320-rebased updated (bfc3552 -> e968d25)

This is an automated email from the ASF dual-hosted git repository.

thenatog pushed a change to branch NIFIREG-320-rebased
in repository https://gitbox.apache.org/repos/asf/nifi-registry.git.


 discard bfc3552  Added jetty header filters to set security headers. Setting security headers for the registry-api using spring security configuration.
     new e968d25  NIFIREG-320 - Added jetty header filters to set security headers. Setting security headers for the registry-api using spring security configuration. NIFIREG-320 - Fixed rest-api docs loading. Headers confirmed to be applied for docs.

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (bfc3552)
            \
             N -- N -- N   refs/heads/NIFIREG-320-rebased (e968d25)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:


[nifi-registry] 01/01: NIFIREG-320 - Added jetty header filters to set security headers. Setting security headers for the registry-api using spring security configuration. NIFIREG-320 - Fixed rest-api docs loading. Headers confirmed to be applied for docs.

Posted by th...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

thenatog pushed a commit to branch NIFIREG-320-rebased
in repository https://gitbox.apache.org/repos/asf/nifi-registry.git

commit e968d25392a3a4743dc41b62e4da160aa52df527
Author: thenatog <th...@gmail.com>
AuthorDate: Mon Sep 23 13:16:57 2019 -0400

    NIFIREG-320 - Added jetty header filters to set security headers. Setting security headers for the registry-api using spring security configuration.
    NIFIREG-320 - Fixed rest-api docs loading. Headers confirmed to be applied for docs.
---
 .../apache/nifi/registry/jetty/JettyServer.java    | 132 ++++++++++++++-------
 .../jetty/headers/ContentSecurityPolicyFilter.java |  58 +++++++++
 .../headers/StrictTransportSecurityFilter.java     |  58 +++++++++
 .../jetty/headers/XFrameOptionsFilter.java         |  58 +++++++++
 .../jetty/headers/XSSProtectionFilter.java         |  59 +++++++++
 .../properties/NiFiRegistryProperties.java         |   4 +
 nifi-registry-core/nifi-registry-web-api/pom.xml   |   6 +
 .../web/security/NiFiRegistrySecurityConfig.java   |   6 +
 8 files changed, 337 insertions(+), 44 deletions(-)

diff --git a/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java
index 45619f7..387857f 100644
--- a/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java
+++ b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java
@@ -17,6 +17,10 @@
 package org.apache.nifi.registry.jetty;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.jetty.headers.ContentSecurityPolicyFilter;
+import org.apache.nifi.registry.jetty.headers.StrictTransportSecurityFilter;
+import org.apache.nifi.registry.jetty.headers.XFrameOptionsFilter;
+import org.apache.nifi.registry.jetty.headers.XSSProtectionFilter;
 import org.apache.nifi.registry.properties.NiFiRegistryProperties;
 import org.apache.nifi.registry.security.crypto.CryptoKeyProvider;
 import org.eclipse.jetty.annotations.AnnotationConfiguration;
@@ -28,11 +32,10 @@ import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.server.handler.ContextHandler;
 import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.server.handler.ResourceHandler;
-import org.eclipse.jetty.util.resource.Resource;
-import org.eclipse.jetty.util.resource.ResourceCollection;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jetty.webapp.Configuration;
@@ -42,6 +45,8 @@ import org.eclipse.jetty.webapp.WebAppContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
 import java.io.File;
 import java.io.FileFilter;
 import java.io.IOException;
@@ -55,6 +60,7 @@ import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -106,6 +112,37 @@ public class JettyServer {
         }
     }
 
+    /**
+     * Returns a File object for the directory containing NIFI documentation.
+     * <p>
+     * Formerly, if the docsDirectory did not exist NIFI would fail to start
+     * with an IllegalStateException and a rather unhelpful log message.
+     * NIFI-2184 updates the process such that if the docsDirectory does not
+     * exist an attempt will be made to create the directory. If that is
+     * successful NIFI will no longer fail and will start successfully barring
+     * any other errors. The side effect of the docsDirectory not being present
+     * is that the documentation links under the 'General' portion of the help
+     * page will not be accessible, but at least the process will be running.
+     *
+     * @param docsDirectory Name of documentation directory in installation directory.
+     * @return A File object to the documentation directory; else startUpFailure called.
+     */
+    private File getDocsDir(final String docsDirectory) {
+        File docsDir;
+        try {
+            docsDir = Paths.get(docsDirectory).toRealPath().toFile();
+        } catch (IOException ex) {
+            logger.info("Directory '" + docsDirectory + "' is missing. Some documentation will be unavailable.");
+            docsDir = new File(docsDirectory).getAbsoluteFile();
+            final boolean made = docsDir.mkdirs();
+            if (!made) {
+                logger.error("Failed to create 'docs' directory!");
+                startUpFailure(new IOException(docsDir.getAbsolutePath() + " could not be created"));
+            }
+        }
+        return docsDir;
+    }
+
     private void configureConnectors() {
         // create the http configuration
         final HttpConfiguration httpConfiguration = new HttpConfiguration();
@@ -254,11 +291,11 @@ public class JettyServer {
 
         final String docsContextPath = "/nifi-registry-docs";
         webDocsContext = loadWar(webDocsWar, docsContextPath);
+        addDocsServlets(webDocsContext);
 
         final HandlerCollection handlers = new HandlerCollection();
         handlers.addHandler(webUiContext);
         handlers.addHandler(webApiContext);
-        handlers.addHandler(createDocsWebApp(docsContextPath));
         handlers.addHandler(webDocsContext);
         server.setHandler(handlers);
     }
@@ -301,6 +338,15 @@ public class JettyServer {
         // configure the max form size (3x the default)
         webappContext.setMaxFormContentSize(600000);
 
+        // add HTTP security headers to all responses
+        final String ALL_PATHS = "/*";
+        ArrayList<Class<? extends Filter>> filters = new ArrayList<>(Arrays.asList(XFrameOptionsFilter.class, ContentSecurityPolicyFilter.class, XSSProtectionFilter.class));
+        if(properties.isHTTPSConfigured()) {
+            filters.add(StrictTransportSecurityFilter.class);
+        }
+
+        filters.forEach( (filter) -> addFilters(filter, ALL_PATHS, webappContext));
+
         // start out assuming the system ClassLoader will be the parent, but if additional resources were specified then
         // inject a new ClassLoader in between the system and webapp ClassLoaders that contains the additional resources
         ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader();
@@ -315,6 +361,12 @@ public class JettyServer {
         return webappContext;
     }
 
+    private void addFilters(Class<? extends Filter> clazz, String path, WebAppContext webappContext) {
+        FilterHolder holder = new FilterHolder(clazz);
+        holder.setName(clazz.getSimpleName());
+        webappContext.addFilter(holder, path, EnumSet.allOf(DispatcherType.class));
+    }
+
     private URL[] getWebApiAdditionalClasspath() {
         final String dbDriverDir = properties.getDatabaseDriverDirectory();
 
@@ -370,51 +422,43 @@ public class JettyServer {
         return resources.toArray(new URL[resources.size()]);
     }
 
-    private ContextHandler createDocsWebApp(final String contextPath) throws IOException {
-        final ResourceHandler resourceHandler = new ResourceHandler();
-        resourceHandler.setDirectoriesListed(false);
-
-        // load the docs directory
-        String docsDirectory = docsLocation;
-        if (StringUtils.isBlank(docsDirectory)) {
-            docsDirectory = "docs";
-        }
-
-        File docsDir;
+    private void addDocsServlets(WebAppContext docsContext) {
         try {
-            docsDir = Paths.get(docsDirectory).toRealPath().toFile();
-        } catch (IOException ex) {
-            logger.warn("Directory '" + docsDirectory + "' is missing. Some documentation will be unavailable.");
-            docsDir = new File(docsDirectory).getAbsoluteFile();
-            final boolean made = docsDir.mkdirs();
-            if (!made) {
-                logger.error("Failed to create 'docs' directory!");
-                startUpFailure(new IOException(docsDir.getAbsolutePath() + " could not be created"));
+            // Load the nifi-registry/docs directory
+            final File docsDir = getDocsDir(docsLocation);
+
+            // Create the servlet which will serve the static resources
+            ServletHolder defaultHolder = new ServletHolder("default", DefaultServlet.class);
+            defaultHolder.setInitParameter("dirAllowed", "false");
+
+            ServletHolder docs = new ServletHolder("docs", DefaultServlet.class);
+            docs.setInitParameter("resourceBase", docsDir.getPath());
+            docs.setInitParameter("dirAllowed", "false");
+
+            docsContext.addServlet(docs, "/html/*");
+            docsContext.addServlet(defaultHolder, "/");
+
+            // load the rest documentation
+            final File webApiDocsDir = new File(webApiContext.getTempDirectory(), "webapp/docs");
+            if (!webApiDocsDir.exists()) {
+                final boolean made = webApiDocsDir.mkdirs();
+                if (!made) {
+                    throw new RuntimeException(webApiDocsDir.getAbsolutePath() + " could not be created");
+                }
             }
-        }
 
-        final Resource docsResource = Resource.newResource(docsDir);
+            ServletHolder apiDocs = new ServletHolder("apiDocs", DefaultServlet.class);
+            apiDocs.setInitParameter("resourceBase", webApiDocsDir.getPath());
+            apiDocs.setInitParameter("dirAllowed", "false");
 
-        // load the rest documentation
-        final File webApiDocsDir = new File(webApiContext.getTempDirectory(), "webapp/docs");
-        if (!webApiDocsDir.exists()) {
-            final boolean made = webApiDocsDir.mkdirs();
-            if (!made) {
-                throw new RuntimeException(webApiDocsDir.getAbsolutePath() + " could not be created");
-            }
-        }
-        final Resource webApiDocsResource = Resource.newResource(webApiDocsDir);
-
-        // create resources for both docs locations
-        final ResourceCollection resources = new ResourceCollection(docsResource, webApiDocsResource);
-        resourceHandler.setBaseResource(resources);
+            docsContext.addServlet(apiDocs, "/rest-api/*");
 
-        // create the context handler
-        final ContextHandler handler = new ContextHandler(contextPath);
-        handler.setHandler(resourceHandler);
+            logger.info("Loading documents web app with context path set to " + docsContext.getContextPath());
 
-        logger.info("Loading documents web app with context path set to " + contextPath);
-        return handler;
+        } catch (Exception ex) {
+            logger.error("Unhandled Exception in createDocsWebApp: " + ex.getMessage());
+            startUpFailure(ex);
+        }
     }
 
     public void start() {
diff --git a/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/ContentSecurityPolicyFilter.java b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/ContentSecurityPolicyFilter.java
new file mode 100644
index 0000000..758e939
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/ContentSecurityPolicyFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.nifi.registry.jetty.headers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * A filter to apply the Content Security Policy header.
+ *
+ */
+public class ContentSecurityPolicyFilter implements Filter {
+    private static final String HEADER = "Content-Security-Policy";
+    private static final String POLICY = "frame-ancestors 'self'";
+
+    private static final Logger logger = LoggerFactory.getLogger(ContentSecurityPolicyFilter.class);
+
+    @Override
+    public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        final HttpServletResponse response = (HttpServletResponse) resp;
+        response.setHeader(HEADER, POLICY);
+
+        filterChain.doFilter(req, resp);
+    }
+
+    @Override
+    public void init(final FilterConfig config) {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/StrictTransportSecurityFilter.java b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/StrictTransportSecurityFilter.java
new file mode 100644
index 0000000..7f0f913
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/StrictTransportSecurityFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.nifi.registry.jetty.headers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * A filter to apply the HTTP Strict Transport Security (HSTS) HTTP header. This forces the browser to use HTTPS for
+ * all
+ */
+public class StrictTransportSecurityFilter implements Filter {
+    private static final String HEADER = "Strict-Transport-Security";
+    private static final String POLICY = "max-age=31540000";
+
+    private static final Logger logger = LoggerFactory.getLogger(StrictTransportSecurityFilter.class);
+
+    @Override
+    public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        final HttpServletResponse response = (HttpServletResponse) resp;
+        response.setHeader(HEADER, POLICY);
+
+        filterChain.doFilter(req, resp);
+    }
+
+    @Override
+    public void init(final FilterConfig config) {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/XFrameOptionsFilter.java b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/XFrameOptionsFilter.java
new file mode 100644
index 0000000..fad5bbc
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/XFrameOptionsFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.nifi.registry.jetty.headers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * A filter to apply the X-Frame-Options header.
+ *
+ */
+public class XFrameOptionsFilter implements Filter {
+    private static final String HEADER = "X-Frame-Options";
+    private static final String POLICY = "SAMEORIGIN";
+
+    private static final Logger logger = LoggerFactory.getLogger(XFrameOptionsFilter.class);
+
+    @Override
+    public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        final HttpServletResponse response = (HttpServletResponse) resp;
+        response.setHeader(HEADER, POLICY);
+
+        filterChain.doFilter(req, resp);
+    }
+
+    @Override
+    public void init(final FilterConfig config) {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/XSSProtectionFilter.java b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/XSSProtectionFilter.java
new file mode 100644
index 0000000..62792f1
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/headers/XSSProtectionFilter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.nifi.registry.jetty.headers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * A filter to apply the Cross Site Scripting (XSS) HTTP header. Protects against reflected cross-site scripting attacks.
+ * The browser will prevent rendering of the page if an attack is detected.
+ */
+
+public class XSSProtectionFilter implements Filter {
+    private static final String HEADER = "X-XSS-Protection";
+    private static final String POLICY = "1; mode=block";
+
+    private static final Logger logger = LoggerFactory.getLogger(XSSProtectionFilter.class);
+
+    @Override
+    public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain filterChain)
+            throws IOException, ServletException {
+
+        final HttpServletResponse response = (HttpServletResponse) resp;
+        response.setHeader(HEADER, POLICY);
+
+        filterChain.doFilter(req, resp);
+    }
+
+    @Override
+    public void init(final FilterConfig config) {
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
index 31133eb..e1e9a39 100644
--- a/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
+++ b/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
@@ -118,6 +118,10 @@ public class NiFiRegistryProperties extends Properties {
         return getPropertyAsInteger(WEB_HTTPS_PORT);
     }
 
+    public boolean isHTTPSConfigured() {
+        return getSslPort() != null;
+    }
+
     public String getHttpsHost() {
         return getProperty(WEB_HTTPS_HOST);
     }
diff --git a/nifi-registry-core/nifi-registry-web-api/pom.xml b/nifi-registry-core/nifi-registry-web-api/pom.xml
index 2df5caf..e03c69d 100644
--- a/nifi-registry-core/nifi-registry-web-api/pom.xml
+++ b/nifi-registry-core/nifi-registry-web-api/pom.xml
@@ -445,5 +445,11 @@
             <version>${jetty.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi.registry</groupId>
+            <artifactId>nifi-registry-jetty</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
index 20a6e0d..b792830 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
@@ -100,6 +100,12 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
                 .sessionManagement()
                     .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
 
+        // Apply security headers for registry API. Security headers for docs and UI are applied with Jetty filters in registry-core.
+        http.headers().xssProtection();
+        http.headers().contentSecurityPolicy("frame-ancestors 'self'");
+        http.headers().httpStrictTransportSecurity().maxAgeInSeconds(31540000);
+        http.headers().frameOptions().sameOrigin();
+
         // x509
         http.addFilterBefore(x509AuthenticationFilter(), AnonymousAuthenticationFilter.class);