You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by jg...@apache.org on 2022/01/19 21:12:08 UTC

[nifi] branch main updated: NIFI-9481 Excluded Data Transfer REST methods from DoSFilter

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

jgresock pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new fc27b31  NIFI-9481 Excluded Data Transfer REST methods from DoSFilter
fc27b31 is described below

commit fc27b3138bba69c52d3c7e4158c77a265410981f
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Tue Jan 18 17:19:06 2022 -0600

    NIFI-9481 Excluded Data Transfer REST methods from DoSFilter
    
    - Added DataTransferDoSFilter with request URI evaluation
    - Added RequestFilterProvider and implementations to abstract Jetty Filter configuration
    
    Signed-off-by: Joe Gresock <jg...@gmail.com>
    
    This closes #5670.
---
 .../org/apache/nifi/web/server/JettyServer.java    | 165 +-------
 .../filter/DataTransferExcludedDoSFilter.java      |  54 +++
 .../nifi/web/server/filter/FilterParameter.java    |  24 ++
 .../web/server/filter/RequestFilterProvider.java   |  35 ++
 .../filter/RestApiRequestFilterProvider.java       |  62 +++
 .../filter/StandardRequestFilterProvider.java      | 127 ++++++
 .../nifi/web/server/JettyServerGroovyTest.groovy   | 435 ---------------------
 .../filter/DataTransferExcludedDoSFilterTest.java  |  80 ++++
 .../filter/RestApiRequestFilterProviderTest.java   |  77 ++++
 .../filter/StandardRequestFilterProviderTest.java  | 108 +++++
 10 files changed, 582 insertions(+), 585 deletions(-)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index d9aefa3..b5d5b7a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -54,13 +54,10 @@ import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.ContentAccess;
 import org.apache.nifi.web.NiFiWebConfigurationContext;
 import org.apache.nifi.web.UiExtensionType;
-import org.apache.nifi.web.security.headers.ContentSecurityPolicyFilter;
-import org.apache.nifi.web.security.headers.StrictTransportSecurityFilter;
-import org.apache.nifi.web.security.headers.XContentTypeOptionsFilter;
-import org.apache.nifi.web.security.headers.XFrameOptionsFilter;
-import org.apache.nifi.web.security.headers.XSSProtectionFilter;
-import org.apache.nifi.web.security.requests.ContentLengthFilter;
-import org.apache.nifi.web.server.log.RequestAuthenticationFilter;
+import org.apache.nifi.web.server.filter.FilterParameter;
+import org.apache.nifi.web.server.filter.RequestFilterProvider;
+import org.apache.nifi.web.server.filter.RestApiRequestFilterProvider;
+import org.apache.nifi.web.server.filter.StandardRequestFilterProvider;
 import org.apache.nifi.web.server.log.RequestLogProvider;
 import org.apache.nifi.web.server.log.StandardRequestLogProvider;
 import org.apache.nifi.web.server.util.TrustStoreScanner;
@@ -83,7 +80,6 @@ import org.eclipse.jetty.server.handler.gzip.GzipHandler;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.servlets.DoSFilter;
 import org.eclipse.jetty.util.ssl.KeyStoreScanner;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
@@ -99,7 +95,6 @@ import org.springframework.web.context.WebApplicationContext;
 import org.springframework.web.context.support.WebApplicationContextUtils;
 
 import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
 import javax.servlet.ServletContext;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -147,9 +142,9 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
     private static final String CONTEXT_PATH_NIFI_API = "/nifi-api";
     private static final String CONTEXT_PATH_NIFI_CONTENT_VIEWER = "/nifi-content-viewer";
     private static final String CONTEXT_PATH_NIFI_DOCS = "/nifi-docs";
-    private static final String RELATIVE_PATH_ACCESS_TOKEN = "/access/token";
 
-    private static final int DOS_FILTER_REJECT_REQUEST = -1;
+    private static final RequestFilterProvider REQUEST_FILTER_PROVIDER = new StandardRequestFilterProvider();
+    private static final RequestFilterProvider REST_API_REQUEST_FILTER_PROVIDER = new RestApiRequestFilterProvider();
 
     private static final FileFilter WAR_FILTER = pathname -> {
         final String nameToTest = pathname.getName().toLowerCase();
@@ -214,15 +209,11 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
         if (props.isHTTPSConfigured()) {
             // Create a handler for the host header and add it to the server
             final HostHeaderHandler hostHeaderHandler = new HostHeaderHandler(props);
-            logger.info("Created HostHeaderHandler [{}}]", hostHeaderHandler);
 
             // Add this before the WAR handlers
             allHandlers.addHandler(hostHeaderHandler);
-        } else {
-            logger.info("Running in HTTP mode; host headers not restricted");
         }
 
-
         final ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
         contextHandlers.addHandler(warHandlers);
         allHandlers.addHandler(contextHandlers);
@@ -239,14 +230,6 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
         server.setRequestLog(requestLog);
     }
 
-    /**
-     * Instantiates this object but does not perform any configuration. Used for unit testing.
-     */
-    JettyServer(Server server, NiFiProperties properties) {
-        this.server = server;
-        this.props = properties;
-    }
-
     private Handler loadInitialWars(final Set<Bundle> bundles) {
 
         // load WARs
@@ -633,25 +616,15 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
         // configure the max form size (3x the default)
         webappContext.setMaxFormContentSize(600000);
 
-        // add HTTP security headers to all responses
-        // TODO: Allow more granular path configuration (e.g. /nifi-api/site-to-site/ vs. /nifi-api/process-groups)
-        ArrayList<Class<? extends Filter>> filters =
-                new ArrayList<>(Arrays.asList(
-                        XFrameOptionsFilter.class,
-                        ContentSecurityPolicyFilter.class,
-                        XSSProtectionFilter.class,
-                        XContentTypeOptionsFilter.class));
+        final List<FilterHolder> requestFilters = CONTEXT_PATH_NIFI_API.equals(contextPath)
+                ? REST_API_REQUEST_FILTER_PROVIDER.getFilters(props)
+                : REQUEST_FILTER_PROVIDER.getFilters(props);
 
-        if (props.isHTTPSConfigured()) {
-            filters.add(StrictTransportSecurityFilter.class);
-            filters.add(RequestAuthenticationFilter.class);
-        }
-        filters.forEach((filter) -> addFilters(filter, webappContext));
-        addDenialOfServiceFilters(webappContext, props);
-
-        if (CONTEXT_PATH_NIFI_API.equals(contextPath)) {
-            addAccessTokenRequestFilter(webappContext, props);
-        }
+        requestFilters.forEach(filter -> {
+            final String pathSpecification = filter.getInitParameter(FilterParameter.PATH_SPECIFICATION.name());
+            final String filterPathSpecification = pathSpecification == null ? CONTEXT_PATH_ALL : pathSpecification;
+            webappContext.addFilter(filter, filterPathSpecification, EnumSet.allOf(DispatcherType.class));
+        });
 
         try {
             // configure the class loader - webappClassLoader -> jetty nar -> web app's nar -> ...
@@ -660,16 +633,10 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
             startUpFailure(ioe);
         }
 
-        logger.info("Loading WAR: " + warFile.getAbsolutePath() + " with context path set to " + contextPath);
+        logger.info("Loading WAR [{}] Context Path [{}]", warFile.getAbsolutePath(), contextPath);
         return webappContext;
     }
 
-    private void addFilters(Class<? extends Filter> clazz, WebAppContext webappContext) {
-        FilterHolder holder = new FilterHolder(clazz);
-        holder.setName(clazz.getSimpleName());
-        webappContext.addFilter(holder, CONTEXT_PATH_ALL, EnumSet.allOf(DispatcherType.class));
-    }
-
     private void addDocsServlets(WebAppContext docsContext) {
         try {
             // Load the nifi/docs directory
@@ -713,108 +680,6 @@ public class JettyServer implements NiFiServer, ExtensionUiLoader {
     }
 
     /**
-     * Adds configurable filters relating to preventing denial of service attacks to the given context.
-     * Currently, this implementation adds
-     * {@link org.eclipse.jetty.servlets.DoSFilter} and {@link ContentLengthFilter} filters.
-     *
-     * @param webAppContext context to which filters will be added
-     * @param props         the {@link NiFiProperties}
-     */
-    private static void addDenialOfServiceFilters(final WebAppContext webAppContext, final NiFiProperties props) {
-        addWebRequestLimitingFilter(webAppContext, props.getMaxWebRequestsPerSecond(), getWebRequestTimeoutMs(props), props.getWebRequestIpWhitelist());
-
-        // Only add the ContentLengthFilter if the property is explicitly set (empty by default)
-        final int maxRequestSize = determineMaxRequestSize(props);
-        if (maxRequestSize > 0) {
-            addContentLengthFilter(webAppContext, maxRequestSize);
-        } else {
-            logger.debug("Not adding content-length filter because {} is not set in nifi.properties", NiFiProperties.WEB_MAX_CONTENT_SIZE);
-        }
-    }
-
-    private static long getWebRequestTimeoutMs(final NiFiProperties props) {
-        final long defaultRequestTimeout = Math.round(FormatUtils.getPreciseTimeDuration(NiFiProperties.DEFAULT_WEB_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS));
-        long configuredRequestTimeout = 0L;
-        try {
-            configuredRequestTimeout = Math.round(FormatUtils.getPreciseTimeDuration(props.getWebRequestTimeout(), TimeUnit.MILLISECONDS));
-        } catch (final NumberFormatException e) {
-            logger.warn("Exception parsing property [{}]; using default value: [{}]", NiFiProperties.WEB_REQUEST_TIMEOUT, defaultRequestTimeout);
-        }
-
-        return configuredRequestTimeout > 0 ? configuredRequestTimeout : defaultRequestTimeout;
-    }
-
-    /**
-     * Adds the {@link org.eclipse.jetty.servlets.DoSFilter} to the specified context and path. Limits incoming web requests to {@code maxWebRequestsPerSecond} per second.
-     * In order to allow clients to make more requests than the maximum rate, clients can be added to the {@code ipWhitelist}.
-     * The {@code requestTimeoutInMilliseconds} value limits requests to the given request timeout amount, and will close connections that run longer than this time.
-     *
-     * @param webAppContext     Web Application Context where Filter will be added
-     * @param maxRequestsPerSec Maximum number of allowed requests per second
-     * @param maxRequestMs      Maximum amount of time in milliseconds before a connection will be automatically closed
-     * @param allowed           Comma-separated string of IP addresses that should not be rate limited. Does not apply to request timeout
-     */
-    private static void addWebRequestLimitingFilter(final WebAppContext webAppContext, final int maxRequestsPerSec, final long maxRequestMs, final String allowed) {
-        final FilterHolder holder = new FilterHolder(DoSFilter.class);
-        holder.setInitParameters(new HashMap<String, String>() {{
-            put("maxRequestsPerSec", Integer.toString(maxRequestsPerSec));
-            put("maxRequestMs", Long.toString(maxRequestMs));
-            put("ipWhitelist", allowed);
-        }});
-        holder.setName(DoSFilter.class.getSimpleName());
-
-        webAppContext.addFilter(holder, CONTEXT_PATH_ALL, EnumSet.allOf(DispatcherType.class));
-        logger.debug("Added DoSFilter Path [{}] Max Requests Per Second [{}] Request Timeout [{} ms] Allowed [{}]", CONTEXT_PATH_ALL, maxRequestsPerSec, maxRequestMs, allowed);
-    }
-
-    private static void addAccessTokenRequestFilter(final WebAppContext webAppContext, final NiFiProperties properties) {
-        final int maxRequestsPerSec = properties.getMaxWebAccessTokenRequestsPerSecond();
-        final long maxRequestMs = getWebRequestTimeoutMs(properties);
-
-        final String webRequestAllowed = properties.getWebRequestIpWhitelist();
-        final FilterHolder holder = new FilterHolder(DoSFilter.class);
-        holder.setInitParameters(new HashMap<String, String>() {{
-            put("maxRequestsPerSec", Integer.toString(maxRequestsPerSec));
-            put("maxRequestMs", Long.toString(maxRequestMs));
-            put("ipWhitelist", webRequestAllowed);
-            put("maxWaitMs", Integer.toString(DOS_FILTER_REJECT_REQUEST));
-            put("delayMs", Integer.toString(DOS_FILTER_REJECT_REQUEST));
-        }});
-        holder.setName("AccessTokenRequest-DoSFilter");
-
-        webAppContext.addFilter(holder, RELATIVE_PATH_ACCESS_TOKEN, EnumSet.allOf(DispatcherType.class));
-        logger.debug("Added DoSFilter Path [{}] Max Requests Per Second [{}] Request Timeout [{} ms] Allowed [{}]", RELATIVE_PATH_ACCESS_TOKEN, maxRequestsPerSec, maxRequestMs, webRequestAllowed);
-    }
-
-    private static int determineMaxRequestSize(NiFiProperties props) {
-        try {
-            final String webMaxContentSize = props.getWebMaxContentSize();
-            logger.debug("Read {} as {}", NiFiProperties.WEB_MAX_CONTENT_SIZE, webMaxContentSize);
-            if (StringUtils.isNotBlank(webMaxContentSize)) {
-                int configuredMaxRequestSize = DataUnit.parseDataSize(webMaxContentSize, DataUnit.B).intValue();
-                logger.debug("Parsed max content length as {} bytes", configuredMaxRequestSize);
-                return configuredMaxRequestSize;
-            } else {
-                logger.debug("{} read from nifi.properties is empty", NiFiProperties.WEB_MAX_CONTENT_SIZE);
-            }
-        } catch (final IllegalArgumentException e) {
-            logger.warn("Exception parsing property {}; disabling content length filter", NiFiProperties.WEB_MAX_CONTENT_SIZE);
-            logger.debug("Error during parsing: ", e);
-        }
-        return -1;
-    }
-
-    private static void addContentLengthFilter(final WebAppContext webAppContext, int maxContentLength) {
-        final FilterHolder holder = new FilterHolder(ContentLengthFilter.class);
-        holder.setInitParameters(new HashMap<String, String>() {{
-            put("maxContentLength", String.valueOf(maxContentLength));
-        }});
-        holder.setName(ContentLengthFilter.class.getSimpleName());
-        logger.debug("Adding ContentLengthFilter to Path [{}] with Maximum Content Length [{}B]", CONTEXT_PATH_ALL, maxContentLength);
-        webAppContext.addFilter(holder, CONTEXT_PATH_ALL, EnumSet.allOf(DispatcherType.class));
-    }
-
-    /**
      * Returns a File object for the directory containing NIFI documentation.
      * <p>
      * Formerly, if the docsDirectory did not exist NIFI would fail to start
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/DataTransferExcludedDoSFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/DataTransferExcludedDoSFilter.java
new file mode 100644
index 0000000..83629dc
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/DataTransferExcludedDoSFilter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.web.server.filter;
+
+import org.eclipse.jetty.servlets.DoSFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Denial-of-Service Filter extended to exclude Data Transfer operations
+ */
+public class DataTransferExcludedDoSFilter extends DoSFilter {
+    protected static final String DATA_TRANSFER_URI_ATTRIBUTE = "nifi-api-data-transfer-uri";
+
+    private static final String DATA_TRANSFER_PATH = "/nifi-api/data-transfer";
+
+    /**
+     * Handle Filter Chain and override service filter for Data Transfer requests
+     *
+     * @param filterChain Filter Chain
+     * @param request HTTP Servlet Request to be evaluated
+     * @param response HTTP Servlet Response
+     * @throws ServletException Thrown on FilterChain.doFilter() failures
+     * @throws IOException Thrown on FilterChain.doFilter() failures
+     */
+    @Override
+    protected void doFilterChain(final FilterChain filterChain, final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+        final String requestUri = request.getRequestURI();
+        if (requestUri.startsWith(DATA_TRANSFER_PATH)) {
+            request.setAttribute(DATA_TRANSFER_URI_ATTRIBUTE, requestUri);
+            filterChain.doFilter(request, response);
+        } else {
+            super.doFilterChain(filterChain, request, response);
+        }
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/FilterParameter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/FilterParameter.java
new file mode 100644
index 0000000..8b773d5
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/FilterParameter.java
@@ -0,0 +1,24 @@
+/*
+ * 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.web.server.filter;
+
+/**
+ * Filter Parameter enumeration
+ */
+public enum FilterParameter {
+    PATH_SPECIFICATION
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/RequestFilterProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/RequestFilterProvider.java
new file mode 100644
index 0000000..4551997
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/RequestFilterProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.web.server.filter;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.eclipse.jetty.servlet.FilterHolder;
+
+import java.util.List;
+
+/**
+ * Request Filter Provider for abstracting configuration of HTTP Request Filters
+ */
+public interface RequestFilterProvider {
+    /**
+     * Get Filters using provided NiFi Properties
+     *
+     * @param properties NiFi Properties required
+     * @return List of Filter Holder
+     */
+    List<FilterHolder> getFilters(NiFiProperties properties);
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProvider.java
new file mode 100644
index 0000000..ee114fc
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProvider.java
@@ -0,0 +1,62 @@
+/*
+ * 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.web.server.filter;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlets.DoSFilter;
+
+import java.util.List;
+
+/**
+ * Request Filter Provider for REST API Web Application
+ */
+public class RestApiRequestFilterProvider extends StandardRequestFilterProvider {
+    public static final String RELATIVE_PATH_ACCESS_TOKEN = "/access/token";
+
+    private static final int DOS_FILTER_REJECT_REQUEST = -1;
+
+    /**
+     * Get Filters using provided NiFi Properties and append filters for Access Token Requests
+     *
+     * @param properties NiFi Properties required
+     * @return List of Filter Holders
+     */
+    @Override
+    public List<FilterHolder> getFilters(final NiFiProperties properties) {
+        final List<FilterHolder> filters = super.getFilters(properties);
+
+        final FilterHolder accessTokenDenialOfServiceFilter = getAccessTokenDenialOfServiceFilter(properties);
+        filters.add(accessTokenDenialOfServiceFilter);
+
+        return filters;
+    }
+
+    private FilterHolder getAccessTokenDenialOfServiceFilter(final NiFiProperties properties) {
+        final FilterHolder filter = getDenialOfServiceFilter(properties, DoSFilter.class);
+
+        final int maxWebAccessTokenRequestsPerSecond = properties.getMaxWebAccessTokenRequestsPerSecond();
+        filter.setInitParameter("maxRequestsPerSec", Integer.toString(maxWebAccessTokenRequestsPerSecond));
+
+        filter.setInitParameter("maxWaitMs", Integer.toString(DOS_FILTER_REJECT_REQUEST));
+        filter.setInitParameter("delayMs", Integer.toString(DOS_FILTER_REJECT_REQUEST));
+
+        filter.setInitParameter(FilterParameter.PATH_SPECIFICATION.name(), RELATIVE_PATH_ACCESS_TOKEN);
+        filter.setName("AccessToken-DoSFilter");
+        return filter;
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java
new file mode 100644
index 0000000..eab2f70
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/filter/StandardRequestFilterProvider.java
@@ -0,0 +1,127 @@
+/*
+ * 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.web.server.filter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.processor.DataUnit;
+import org.apache.nifi.util.FormatUtils;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.headers.ContentSecurityPolicyFilter;
+import org.apache.nifi.web.security.headers.StrictTransportSecurityFilter;
+import org.apache.nifi.web.security.headers.XContentTypeOptionsFilter;
+import org.apache.nifi.web.security.headers.XFrameOptionsFilter;
+import org.apache.nifi.web.security.headers.XSSProtectionFilter;
+import org.apache.nifi.web.security.requests.ContentLengthFilter;
+import org.apache.nifi.web.server.log.RequestAuthenticationFilter;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlets.DoSFilter;
+
+import javax.servlet.Filter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Standard implementation of Request Filter Provider
+ */
+public class StandardRequestFilterProvider implements RequestFilterProvider {
+    private static final int MAX_CONTENT_SIZE_DISABLED = 0;
+
+    /**
+     * Get Filters using provided NiFi Properties
+     *
+     * @param properties NiFi Properties required
+     * @return List of Filter Holders
+     */
+    @Override
+    public List<FilterHolder> getFilters(final NiFiProperties properties) {
+        Objects.requireNonNull(properties, "Properties required");
+
+        final List<FilterHolder> filters = new ArrayList<>();
+
+        filters.add(getFilterHolder(XFrameOptionsFilter.class));
+        filters.add(getFilterHolder(ContentSecurityPolicyFilter.class));
+        filters.add(getFilterHolder(XSSProtectionFilter.class));
+        filters.add(getFilterHolder(XContentTypeOptionsFilter.class));
+
+        if (properties.isHTTPSConfigured()) {
+            filters.add(getFilterHolder(StrictTransportSecurityFilter.class));
+            filters.add(getFilterHolder(RequestAuthenticationFilter.class));
+        }
+
+        final int maxContentSize = getMaxContentSize(properties);
+        if (maxContentSize > MAX_CONTENT_SIZE_DISABLED) {
+            final FilterHolder contentLengthFilter = getContentLengthFilter(maxContentSize);
+            filters.add(contentLengthFilter);
+        }
+
+        final FilterHolder denialOfServiceFilter = getDenialOfServiceFilter(properties, DataTransferExcludedDoSFilter.class);
+        filters.add(denialOfServiceFilter);
+
+        return filters;
+    }
+
+    protected FilterHolder getDenialOfServiceFilter(final NiFiProperties properties, final Class<? extends DoSFilter> filterClass) {
+        final FilterHolder filter = new FilterHolder(filterClass);
+
+        final int maxWebRequestsPerSecond = properties.getMaxWebRequestsPerSecond();
+        filter.setInitParameter("maxRequestsPerSec", Integer.toString(maxWebRequestsPerSecond));
+
+        final long webRequestTimeout = getWebRequestTimeout(properties);
+        filter.setInitParameter("maxRequestMs", Long.toString(webRequestTimeout));
+
+        final String webRequestIpWhitelist = properties.getWebRequestIpWhitelist();
+        filter.setInitParameter("ipWhitelist", webRequestIpWhitelist);
+
+        filter.setName(DoSFilter.class.getSimpleName());
+        return filter;
+    }
+
+    private FilterHolder getFilterHolder(final Class<? extends Filter> filterClass) {
+        final FilterHolder filter = new FilterHolder(filterClass);
+        filter.setName(filterClass.getSimpleName());
+        return filter;
+    }
+
+    private FilterHolder getContentLengthFilter(final int maxContentSize) {
+        final FilterHolder filter = new FilterHolder(ContentLengthFilter.class);
+        filter.setInitParameter(ContentLengthFilter.MAX_LENGTH_INIT_PARAM, Integer.toString(maxContentSize));
+        filter.setName(ContentLengthFilter.class.getSimpleName());
+        return filter;
+    }
+
+    private int getMaxContentSize(final NiFiProperties properties) {
+        final String webMaxContentSize = properties.getWebMaxContentSize();
+        try {
+            return StringUtils.isBlank(webMaxContentSize) ? MAX_CONTENT_SIZE_DISABLED : DataUnit.parseDataSize(webMaxContentSize, DataUnit.B).intValue();
+        } catch (final IllegalArgumentException e) {
+            throw new IllegalStateException(String.format("Property [%s] format invalid", NiFiProperties.WEB_MAX_CONTENT_SIZE), e);
+        }
+    }
+
+    protected long getWebRequestTimeout(final NiFiProperties properties) {
+        final String webRequestTimeout = properties.getWebRequestTimeout();
+
+        try {
+            final double webRequestTimeoutParsed = FormatUtils.getPreciseTimeDuration(webRequestTimeout, TimeUnit.MILLISECONDS);
+            return Math.round(webRequestTimeoutParsed);
+        } catch (final NumberFormatException e) {
+            throw new IllegalStateException(String.format("Property [%s] format invalid", NiFiProperties.WEB_REQUEST_TIMEOUT), e);
+        }
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy
deleted file mode 100644
index e7804d7..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/groovy/org/apache/nifi/web/server/JettyServerGroovyTest.groovy
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * 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.web.server
-
-import org.apache.nifi.bundle.Bundle
-import org.apache.nifi.nar.ExtensionManagerHolder
-import org.apache.nifi.processor.DataUnit
-import org.apache.nifi.remote.io.socket.NetworkUtils
-import org.apache.nifi.security.util.StandardTlsConfiguration
-import org.apache.nifi.security.util.TemporaryKeyStoreBuilder
-import org.apache.nifi.security.util.TlsConfiguration
-import org.apache.nifi.security.util.TlsPlatform
-import org.apache.nifi.util.NiFiProperties
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.eclipse.jetty.server.Connector
-import org.eclipse.jetty.server.HttpConfiguration
-import org.eclipse.jetty.server.Server
-import org.eclipse.jetty.server.ServerConnector
-import org.eclipse.jetty.server.SslConnectionFactory
-import org.eclipse.jetty.servlet.FilterHolder
-import org.eclipse.jetty.util.ssl.SslContextFactory
-import org.eclipse.jetty.webapp.WebAppContext
-import org.junit.After
-import org.junit.Assume
-import org.junit.BeforeClass
-import org.junit.Rule
-import org.junit.Test
-import org.junit.contrib.java.lang.system.Assertion
-import org.junit.contrib.java.lang.system.ExpectedSystemExit
-import org.junit.contrib.java.lang.system.SystemErrRule
-import org.junit.contrib.java.lang.system.SystemOutRule
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito
-import org.mockito.invocation.InvocationOnMock
-import org.mockito.stubbing.Answer
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import javax.net.ssl.SSLContext
-import javax.net.ssl.SSLSocket
-import javax.net.ssl.SSLSocketFactory
-import javax.servlet.DispatcherType
-import java.nio.charset.StandardCharsets
-import java.security.Security
-
-@RunWith(JUnit4.class)
-class JettyServerGroovyTest extends GroovyTestCase {
-    private static final Logger logger = LoggerFactory.getLogger(JettyServerGroovyTest.class)
-
-    @Rule
-    public final ExpectedSystemExit exit = ExpectedSystemExit.none()
-
-    @Rule
-    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog()
-
-    @Rule
-    public final SystemErrRule systemErrRule = new SystemErrRule().enableLog()
-
-    private static final int DEFAULT_HTTP_PORT = 8080
-    private static final int DEFAULT_HTTPS_PORT = 8443
-
-    private static final int HTTPS_PORT = NetworkUtils.getAvailableTcpPort()
-    private static final String HTTPS_HOSTNAME = "localhost"
-
-    private static final String TLS_1_3_PROTOCOL = "TLSv1.3"
-    private static final List<String> TLS_1_3_CIPHER_SUITES = ["TLS_AES_128_GCM_SHA256"]
-
-    private static final TlsConfiguration TLS_CONFIGURATION = new TemporaryKeyStoreBuilder().build()
-
-    // These protocol versions should not ever be supported
-    static private final List<String> LEGACY_TLS_PROTOCOLS = ["TLS", "TLSv1", "TLSv1.1", "SSL", "SSLv2", "SSLv2Hello", "SSLv3"]
-
-    NiFiProperties httpsProps = new NiFiProperties(new Properties([
-            (NiFiProperties.WEB_HTTPS_PORT)            : HTTPS_PORT as String,
-            (NiFiProperties.WEB_HTTPS_HOST)            : HTTPS_HOSTNAME,
-            (NiFiProperties.SECURITY_KEYSTORE)         : TLS_CONFIGURATION.keystorePath,
-            (NiFiProperties.SECURITY_KEYSTORE_PASSWD)  : TLS_CONFIGURATION.keystorePassword,
-            (NiFiProperties.SECURITY_KEYSTORE_TYPE)    : TLS_CONFIGURATION.keystoreType.type,
-            (NiFiProperties.SECURITY_TRUSTSTORE)       : TLS_CONFIGURATION.truststorePath,
-            (NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): TLS_CONFIGURATION.truststorePassword,
-            (NiFiProperties.SECURITY_TRUSTSTORE_TYPE)  : TLS_CONFIGURATION.truststoreType.type,
-    ]))
-
-    @BeforeClass
-    static void setUpOnce() throws Exception {
-        new File(TLS_CONFIGURATION.keystorePath).deleteOnExit()
-        new File(TLS_CONFIGURATION.truststorePath).deleteOnExit()
-
-        Security.addProvider(new BouncyCastleProvider())
-
-        logger.metaClass.methodMissing = { String name, args ->
-            logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
-        }
-    }
-
-    @After
-    void tearDown() throws Exception {
-        // Cleans up the EMH so it can be reinitialized when a new Jetty server starts
-        ExtensionManagerHolder.INSTANCE = null
-    }
-
-    @Test
-    void testShouldDetectHttpAndHttpsConfigurationsBothPresent() {
-        // Arrange
-        Map badProps = [
-                (NiFiProperties.WEB_HTTP_HOST) : "localhost",
-                (NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
-                (NiFiProperties.WEB_THREADS)   : NiFiProperties.DEFAULT_WEB_THREADS
-        ]
-        NiFiProperties mockProps = Mockito.mock(NiFiProperties.class)
-        Mockito.when(mockProps.getPort()).thenReturn(DEFAULT_HTTP_PORT)
-        Mockito.when(mockProps.getSslPort()).thenReturn(DEFAULT_HTTPS_PORT)
-
-        Mockito.when(mockProps.getProperty(ArgumentMatchers.anyString())).thenAnswer(new Answer<Object>() {
-            @Override
-            Object answer(InvocationOnMock invocation) throws Throwable {
-                badProps[(String) invocation.getArgument(0)] ?: "no_value"
-            }
-        })
-
-        // Act
-        boolean bothConfigsPresent = JettyServer.bothHttpAndHttpsConnectorsConfigured(mockProps)
-        logger.info("Both configs present: ${bothConfigsPresent}")
-
-        // Assert
-        assert bothConfigsPresent
-    }
-
-    @Test
-    void testDetectHttpAndHttpsConfigurationsShouldAllowEither() {
-        // Arrange
-        Map httpMap = [
-                (NiFiProperties.WEB_HTTP_HOST) : "localhost",
-                (NiFiProperties.WEB_HTTPS_HOST): null,
-        ]
-        NiFiProperties httpProps = [
-                getPort    : { -> DEFAULT_HTTP_PORT },
-                getSslPort : { -> null },
-                getProperty: { String prop ->
-                    String value = httpMap[prop] ?: "no_value"
-                    value
-                },
-        ] as NiFiProperties
-
-        Map httpsMap = [
-                (NiFiProperties.WEB_HTTP_HOST) : null,
-                (NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
-        ]
-        NiFiProperties httpsProps = [
-                getPort    : { -> null },
-                getSslPort : { -> DEFAULT_HTTPS_PORT },
-                getProperty: { String prop ->
-                    String value = httpsMap[prop] ?: "no_value"
-                    value
-                },
-        ] as NiFiProperties
-
-        // Act
-        boolean bothConfigsPresentForHttp = JettyServer.bothHttpAndHttpsConnectorsConfigured(httpProps)
-        logger.info("Both configs present for HTTP properties: ${bothConfigsPresentForHttp}")
-
-        boolean bothConfigsPresentForHttps = JettyServer.bothHttpAndHttpsConnectorsConfigured(httpsProps)
-        logger.info("Both configs present for HTTPS properties: ${bothConfigsPresentForHttps}")
-
-        // Assert
-        assert !bothConfigsPresentForHttp
-        assert !bothConfigsPresentForHttps
-    }
-
-    @Test
-    void testShouldFailToStartWithHttpAndHttpsConfigurationsBothPresent() {
-        // Arrange
-        Map badProps = [
-                (NiFiProperties.WEB_HTTP_HOST) : "localhost",
-                (NiFiProperties.WEB_HTTPS_HOST): "secure.host.com",
-        ]
-        NiFiProperties mockProps = [
-                getPort            : { -> DEFAULT_HTTP_PORT },
-                getSslPort         : { -> DEFAULT_HTTPS_PORT },
-                getProperty        : { String prop ->
-                    String value = badProps[prop] ?: "no_value"
-                    logger.mock("getProperty(${prop}) -> ${value}")
-                    value
-                },
-                getWebThreads      : { -> NiFiProperties.DEFAULT_WEB_THREADS },
-                getWebMaxHeaderSize: { -> NiFiProperties.DEFAULT_WEB_MAX_HEADER_SIZE },
-                isHTTPSConfigured  : { -> true }
-        ] as NiFiProperties
-
-        // The web server should fail to start and exit Java
-        exit.expectSystemExitWithStatus(1)
-        exit.checkAssertionAfterwards(new Assertion() {
-            void checkAssertion() {
-                final String standardErr = systemErrRule.getLog()
-                List<String> errLines = standardErr.split("\n")
-
-                assert errLines.any { it =~ "Failed to start web server: " }
-                assert errLines.any { it =~ "Shutting down..." }
-            }
-        })
-
-        // Act
-        JettyServer jettyServer = new JettyServer()
-        jettyServer.initialize(mockProps, null, [] as Set<Bundle>, null)
-        
-        // Assert
-
-        // Assertions defined above
-    }
-
-    @Test
-    void testShouldConfigureHTTPSConnector() {
-        // Arrange
-        final String externalHostname = "localhost"
-
-        NiFiProperties httpsProps = new NiFiProperties(new Properties([
-                (NiFiProperties.WEB_HTTPS_PORT): HTTPS_PORT as String,
-                (NiFiProperties.WEB_HTTPS_HOST): externalHostname,
-        ]))
-
-        Server internalServer = new Server()
-        JettyServer jetty = new JettyServer(internalServer, httpsProps)
-
-        // Act
-        jetty.configureHttpsConnector(internalServer, new HttpConfiguration())
-        List<Connector> connectors = Arrays.asList(internalServer.connectors)
-
-        // Assert
-        assertServerConnector(connectors, externalHostname, HTTPS_PORT)
-    }
-
-    @Test
-    void testShouldSupportTLSv1_3WhenProtocolFound() {
-        // Arrange
-        String[] defaultProtocols = SSLContext.getDefault().defaultSSLParameters.protocols
-        Assume.assumeTrue("This test should only run when TLSv1.3 is found in the set of default protocols", defaultProtocols.contains(TLS_1_3_PROTOCOL))
-
-        Server internalServer = new Server()
-        JettyServer jetty = new JettyServer(internalServer, httpsProps)
-
-        jetty.configureConnectors(internalServer)
-        List<Connector> connectors = Arrays.asList(internalServer.connectors)
-        internalServer.start()
-
-        // Create a (client) socket which only supports TLSv1.3
-        TlsConfiguration tls13ClientConf = StandardTlsConfiguration.fromNiFiProperties(httpsProps)
-        SSLSocketFactory socketFactory = org.apache.nifi.security.util.SslContextFactory.createSSLSocketFactory(tls13ClientConf)
-
-        SSLSocket socket = (SSLSocket) socketFactory.createSocket(HTTPS_HOSTNAME, HTTPS_PORT)
-        socket.setEnabledProtocols([TLS_1_3_PROTOCOL] as String[])
-        socket.setEnabledCipherSuites(TLS_1_3_CIPHER_SUITES as String[])
-
-        // Act
-        String response = makeTLSRequest(socket, "This is a TLS 1.3 request")
-
-        // Assert
-        assert response =~ "HTTP/1.1 400"
-
-        assertServerConnector(connectors)
-
-        // Clean up
-        internalServer.stop()
-    }
-
-    @Test
-    void testShouldNotSupportTLSv1_3WhenProtocolNotFound() {
-        // Arrange
-        Assume.assumeTrue("This test should only run when TLSv1.3 is not found in the set of default protocols", !TlsPlatform.supportedProtocols.contains(TLS_1_3_PROTOCOL))
-
-        Server internalServer = new Server()
-        JettyServer jetty = new JettyServer(internalServer, httpsProps)
-
-        jetty.configureConnectors(internalServer)
-        List<Connector> connectors = Arrays.asList(internalServer.connectors)
-        internalServer.start()
-
-        TlsConfiguration tlsConfiguration = StandardTlsConfiguration.fromNiFiProperties(httpsProps)
-
-        // Create a "default" (client) socket (which supports TLSv1.2)
-        SSLSocketFactory defaultSocketFactory = org.apache.nifi.security.util.SslContextFactory.createSSLSocketFactory(tlsConfiguration)
-        SSLSocket defaultSocket = (SSLSocket) defaultSocketFactory.createSocket(HTTPS_HOSTNAME, HTTPS_PORT)
-
-        // Act
-        String tls12Response = makeTLSRequest(defaultSocket, "This is a default socket request")
-
-        def msg = shouldFail() {
-            // Create a (client) socket which only supports TLSv1.3
-            SSLSocketFactory tls13SocketFactory = org.apache.nifi.security.util.SslContextFactory.createSSLSocketFactory(tlsConfiguration)
-
-            SSLSocket tls13Socket = (SSLSocket) tls13SocketFactory.createSocket(HTTPS_HOSTNAME, HTTPS_PORT)
-            tls13Socket.setEnabledProtocols([TLS_1_3_PROTOCOL] as String[])
-            tls13Socket.setEnabledCipherSuites(TLS_1_3_CIPHER_SUITES as String[])
-
-            makeTLSRequest(tls13Socket, "This is a TLSv1.3 socket request")
-        }
-        logger.expected(msg)
-
-        // Assert
-        assert tls12Response =~ "HTTP"
-
-        assertServerConnector(connectors)
-
-        // Clean up
-        internalServer.stop()
-    }
-
-    /**
-     * Returns the server's response body as a String. Closes the socket connection.
-     *
-     * @param socket
-     * @param requestMessage
-     * @return
-     */
-    private static String makeTLSRequest(Socket socket, String requestMessage) {
-        InputStream socketInputStream = new BufferedInputStream(socket.getInputStream())
-        OutputStream socketOutputStream = new BufferedOutputStream(socket.getOutputStream())
-
-        socketOutputStream.write(requestMessage.getBytes())
-        socketOutputStream.flush()
-
-        byte[] data = new byte[2048]
-        int len = socketInputStream.read(data)
-        if (len <= 0) {
-            throw new IOException("no data received")
-        }
-        final String trimmedResponse = new String(data, 0, len, StandardCharsets.UTF_8)
-        logger.info("Client received ${len} bytes from server: \n${trimmedResponse}\n----End of response----")
-        socket.close()
-        trimmedResponse
-    }
-
-    private static void assertServerConnector(List<Connector> connectors,
-                                              String EXPECTED_HOSTNAME = HTTPS_HOSTNAME,
-                                              int EXPECTED_PORT = HTTPS_PORT) {
-        // Assert the server connector is correct
-        assert connectors.size() == 1
-        ServerConnector connector = connectors.first() as ServerConnector
-        assert connector.host == EXPECTED_HOSTNAME
-        assert connector.port == EXPECTED_PORT
-        assert connector.getProtocols() == ['ssl', 'http/1.1']
-
-        SslConnectionFactory connectionFactory = connector.getConnectionFactory("ssl") as SslConnectionFactory
-        SslContextFactory sslContextFactory = connectionFactory.getSslContextFactory()
-        assert (sslContextFactory.getExcludeProtocols() as List<String>).containsAll(LEGACY_TLS_PROTOCOLS)
-    }
-
-    @Test
-    void testShouldEnableContentLengthFilterIfWebMaxContentSizeSet() {
-        // Arrange
-        Map defaultProps = [
-                (NiFiProperties.WEB_HTTP_PORT)       : DEFAULT_HTTP_PORT as String,
-                (NiFiProperties.WEB_HTTP_HOST)       : "localhost",
-                (NiFiProperties.WEB_MAX_CONTENT_SIZE): "1 MB",
-        ]
-        NiFiProperties mockProps = new NiFiProperties(new Properties(defaultProps))
-
-        List<FilterHolder> filters = []
-        def mockWebContext = [
-                addFilter: { FilterHolder fh, String path, EnumSet<DispatcherType> d ->
-                    logger.mock("Called addFilter(${fh.name}, ${path}, ${d})")
-                    filters.add(fh)
-                    fh
-                }] as WebAppContext
-
-        JettyServer jettyServer = new JettyServer(new Server(), mockProps)
-        logger.info("Created JettyServer: ${jettyServer.dump()}")
-
-        final int MAX_CONTENT_LENGTH_BYTES = DataUnit.parseDataSize(defaultProps[NiFiProperties.WEB_MAX_CONTENT_SIZE], DataUnit.B).intValue()
-
-        // Act
-        jettyServer.addDenialOfServiceFilters(mockWebContext, mockProps)
-
-        // Assert
-        assert filters.size() == 2
-        def filterNames = filters*.name
-        logger.info("Web API Context has ${filters.size()} filters: ${filterNames.join(", ")}".toString())
-        assert filterNames.contains("DoSFilter")
-        assert filterNames.contains("ContentLengthFilter")
-
-        FilterHolder clfHolder = filters.find { it.name == "ContentLengthFilter" }
-        String maxContentLength = clfHolder.getInitParameter("maxContentLength")
-        assert maxContentLength == MAX_CONTENT_LENGTH_BYTES as String
-
-        // Filter is not instantiated just by adding it
-//        ContentLengthFilter clf = filters?.find { it.className == "ContentLengthFilter" }?.filter as ContentLengthFilter
-//        assert clf.getMaxContentLength() == MAX_CONTENT_LENGTH_BYTES
-    }
-
-    @Test
-    void testShouldNotEnableContentLengthFilterIfWebMaxContentSizeEmpty() {
-        // Arrange
-        Map defaultProps = [
-                (NiFiProperties.WEB_HTTP_PORT): DEFAULT_HTTP_PORT as String,
-                (NiFiProperties.WEB_HTTP_HOST): "localhost",
-        ]
-        NiFiProperties mockProps = new NiFiProperties(new Properties(defaultProps))
-
-        List<FilterHolder> filters = []
-        def mockWebContext = [
-                addFilter: { FilterHolder fh, String path, EnumSet<DispatcherType> d ->
-                    logger.mock("Called addFilter(${fh.name}, ${path}, ${d})")
-                    filters.add(fh)
-                    fh
-                }] as WebAppContext
-
-        JettyServer jettyServer = new JettyServer(new Server(), mockProps)
-        logger.info("Created JettyServer: ${jettyServer.dump()}")
-
-        // Act
-        jettyServer.addDenialOfServiceFilters(mockWebContext, mockProps)
-
-        // Assert
-        assert filters.size() == 1
-        def filterNames = filters*.name
-        logger.info("Web API Context has ${filters.size()} filters: ${filterNames.join(", ")}".toString())
-        assert filterNames.contains("DoSFilter")
-        assert !filterNames.contains("ContentLengthFilter")
-    }
-}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/DataTransferExcludedDoSFilterTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/DataTransferExcludedDoSFilterTest.java
new file mode 100644
index 0000000..5017133
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/DataTransferExcludedDoSFilterTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.web.server.filter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class DataTransferExcludedDoSFilterTest {
+    private static final String DATA_TRANSFER_URI = "/nifi-api/data-transfer";
+
+    private static final String ACCESS_URI = "/nifi-api/access";
+
+    @Mock
+    private FilterConfig filterConfig;
+
+    @Mock
+    private FilterChain filterChain;
+
+    @Mock
+    private HttpServletRequest request;
+
+    @Mock
+    private HttpServletResponse response;
+
+    private DataTransferExcludedDoSFilter filter;
+
+    @BeforeEach
+    public void setFilter() throws ServletException {
+        filter = new DataTransferExcludedDoSFilter();
+        filter.init(filterConfig);
+    }
+
+    @Test
+    public void testDoFilterChain() throws ServletException, IOException {
+        when(request.getRequestURI()).thenReturn(ACCESS_URI);
+
+        filter.doFilterChain(filterChain, request, response);
+
+        verify(request, never()).setAttribute(eq(DataTransferExcludedDoSFilter.DATA_TRANSFER_URI_ATTRIBUTE), eq(DATA_TRANSFER_URI));
+    }
+
+    @Test
+    public void testDoFilterChainDataTransfer() throws ServletException, IOException {
+        when(request.getRequestURI()).thenReturn(DATA_TRANSFER_URI);
+
+        filter.doFilterChain(filterChain, request, response);
+
+        verify(request).setAttribute(eq(DataTransferExcludedDoSFilter.DATA_TRANSFER_URI_ATTRIBUTE), eq(DATA_TRANSFER_URI));
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java
new file mode 100644
index 0000000..b00f308
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/RestApiRequestFilterProviderTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.web.server.filter;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.headers.ContentSecurityPolicyFilter;
+import org.apache.nifi.web.security.headers.XContentTypeOptionsFilter;
+import org.apache.nifi.web.security.headers.XFrameOptionsFilter;
+import org.apache.nifi.web.security.headers.XSSProtectionFilter;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlets.DoSFilter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.Filter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class RestApiRequestFilterProviderTest {
+    private RestApiRequestFilterProvider provider;
+
+    @BeforeEach
+    public void setProvider() {
+        provider = new RestApiRequestFilterProvider();
+    }
+
+    @Test
+    public void testGetFilters() {
+        final NiFiProperties properties = getProperties(Collections.emptyMap());
+        final List<FilterHolder> filters = provider.getFilters(properties);
+
+        final Optional<FilterHolder> filterHolder = filters.stream().filter(filter -> filter.getInitParameter(FilterParameter.PATH_SPECIFICATION.name()) != null).findFirst();
+        assertTrue(filterHolder.isPresent());
+
+        final FilterHolder holder = filterHolder.get();
+        assertEquals(DoSFilter.class, holder.getHeldClass());
+
+        assertNotNull(filters);
+        assertFalse(filters.isEmpty());
+
+        assertFilterClassFound(filters, DataTransferExcludedDoSFilter.class);
+        assertFilterClassFound(filters, XFrameOptionsFilter.class);
+        assertFilterClassFound(filters, ContentSecurityPolicyFilter.class);
+        assertFilterClassFound(filters, XSSProtectionFilter.class);
+        assertFilterClassFound(filters, XContentTypeOptionsFilter.class);
+    }
+
+    private void assertFilterClassFound(final List<FilterHolder> filters, final Class<? extends Filter> filterClass) {
+        final Optional<FilterHolder> filterHolder = filters.stream().filter(filter -> filterClass.equals(filter.getHeldClass())).findFirst();
+        assertTrue(filterHolder.isPresent(), String.format("Filter Class [%s] not found", filterClass));
+    }
+
+    private NiFiProperties getProperties(final Map<String, String> properties) {
+        return NiFiProperties.createBasicNiFiProperties(null, properties);
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java
new file mode 100644
index 0000000..feb4ba0
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/filter/StandardRequestFilterProviderTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.web.server.filter;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.security.headers.ContentSecurityPolicyFilter;
+import org.apache.nifi.web.security.headers.StrictTransportSecurityFilter;
+import org.apache.nifi.web.security.headers.XContentTypeOptionsFilter;
+import org.apache.nifi.web.security.headers.XFrameOptionsFilter;
+import org.apache.nifi.web.security.headers.XSSProtectionFilter;
+import org.apache.nifi.web.security.requests.ContentLengthFilter;
+import org.apache.nifi.web.server.log.RequestAuthenticationFilter;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.Filter;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class StandardRequestFilterProviderTest {
+    private static final String HTTPS_PORT = "8443";
+
+    private static final String MAX_CONTENT_SIZE = "1 MB";
+
+    private StandardRequestFilterProvider provider;
+
+    @BeforeEach
+    public void setProvider() {
+        provider = new StandardRequestFilterProvider();
+    }
+
+    @Test
+    public void testGetFilters() {
+        final NiFiProperties properties = getProperties(Collections.emptyMap());
+        final List<FilterHolder> filters = provider.getFilters(properties);
+
+        assertStandardFiltersFound(filters);
+    }
+
+    @Test
+    public void testGetFiltersContentLengthEnabled() {
+        final Map<String, String> configurationProperties = new LinkedHashMap<>();
+        configurationProperties.put(NiFiProperties.WEB_MAX_CONTENT_SIZE, MAX_CONTENT_SIZE);
+
+        final NiFiProperties properties = getProperties(configurationProperties);
+        final List<FilterHolder> filters = provider.getFilters(properties);
+
+        assertStandardFiltersFound(filters);
+
+        assertFilterClassFound(filters, ContentLengthFilter.class);
+    }
+
+    @Test
+    public void testGetFiltersHttpsEnabled() {
+        final Map<String, String> configurationProperties = new LinkedHashMap<>();
+        configurationProperties.put(NiFiProperties.WEB_HTTPS_PORT, HTTPS_PORT);
+
+        final NiFiProperties properties = getProperties(configurationProperties);
+        final List<FilterHolder> filters = provider.getFilters(properties);
+
+        assertStandardFiltersFound(filters);
+
+        assertFilterClassFound(filters, RequestAuthenticationFilter.class);
+        assertFilterClassFound(filters, StrictTransportSecurityFilter.class);
+    }
+
+    private void assertStandardFiltersFound(final List<FilterHolder> filters) {
+        assertNotNull(filters);
+        assertFalse(filters.isEmpty());
+
+        assertFilterClassFound(filters, DataTransferExcludedDoSFilter.class);
+        assertFilterClassFound(filters, XFrameOptionsFilter.class);
+        assertFilterClassFound(filters, ContentSecurityPolicyFilter.class);
+        assertFilterClassFound(filters, XSSProtectionFilter.class);
+        assertFilterClassFound(filters, XContentTypeOptionsFilter.class);
+    }
+
+    private void assertFilterClassFound(final List<FilterHolder> filters, final Class<? extends Filter> filterClass) {
+        final Optional<FilterHolder> filterHolder = filters.stream().filter(filter -> filterClass.equals(filter.getHeldClass())).findFirst();
+        assertTrue(filterHolder.isPresent(), String.format("Filter Class [%s] not found", filterClass));
+    }
+
+    private NiFiProperties getProperties(final Map<String, String> properties) {
+        return NiFiProperties.createBasicNiFiProperties(null, properties);
+    }
+}