DRILL-7351: Added tokens to Web forms to prevent CSRF attacks
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/DrillApplicationMaster.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/DrillApplicationMaster.java
index c0db9a1..e32a531 100644
--- a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/DrillApplicationMaster.java
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/DrillApplicationMaster.java
@@ -19,6 +19,8 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.drill.common.util.GuavaPatcher;
+import org.apache.drill.common.util.ProtobufPatcher;
 import org.apache.drill.yarn.appMaster.ControllerFactory.ControllerFactoryException;
 import org.apache.drill.yarn.appMaster.http.WebServer;
 import org.apache.drill.yarn.core.DoyConfigException;
@@ -44,6 +46,22 @@
  */
 
 public class DrillApplicationMaster {
+
+  static {
+    /*
+     * Drill-on-YARN uses Hadoop dependencies that use older version of protobuf,
+     * and override some methods that became final in recent protobuf versions.
+     * This code removes these final modifiers.
+     */
+    ProtobufPatcher.patch();
+    /*
+     * HBase client uses older version of Guava's Stopwatch API,
+     * while Drill ships with 18.x which has changes the scope of
+     * these API to 'package', this code make them accessible.
+     */
+    GuavaPatcher.patch();
+  }
+
   private static final Log LOG = LogFactory
       .getLog(DrillApplicationMaster.class);
 
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/CsrfTokenInjectFilter.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/CsrfTokenInjectFilter.java
new file mode 100644
index 0000000..1f282d8
--- /dev/null
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/CsrfTokenInjectFilter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.drill.yarn.appMaster.http;
+
+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.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.HttpMethod;
+import java.io.IOException;
+
+/**
+ * Generates and adds a CSRF token to a HTTP session. It is used to prevent CSRF attacks.
+ */
+public class CsrfTokenInjectFilter implements Filter {
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+    if (HttpMethod.GET.equalsIgnoreCase(httpRequest.getMethod())) {
+      // We don't create a session with this call as we need to check if there is a session already (i.e. if a user is logged in).
+      HttpSession session = httpRequest.getSession(false);
+      if (session != null) {
+        String csrfToken = (String) session.getAttribute(WebConstants.CSRF_TOKEN);
+        if (csrfToken == null) {
+          csrfToken = WebUtils.generateCsrfToken();
+          httpRequest.getSession().setAttribute(WebConstants.CSRF_TOKEN, csrfToken);
+        }
+      }
+    }
+    chain.doFilter(request, response);
+  }
+
+  @Override
+  public void destroy() {
+  }
+}
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/CsrfTokenValidateFilter.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/CsrfTokenValidateFilter.java
new file mode 100644
index 0000000..6e00e21
--- /dev/null
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/CsrfTokenValidateFilter.java
@@ -0,0 +1,63 @@
+/*
+ * 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.drill.yarn.appMaster.http;
+
+import org.apache.drill.exec.server.rest.WebServerConstants;
+import org.apache.drill.exec.server.rest.WebUtils;
+
+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.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.HttpMethod;
+import java.io.IOException;
+
+/**
+ * All forms on site have a field with a CSRF token injected by server.
+ * This filter compares the token with one stored in the session to ensure that the POST request is not a CSRF attack.
+ */
+public class CsrfTokenValidateFilter implements Filter {
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+    HttpServletResponse httpResponse = (HttpServletResponse) response;
+    if (HttpMethod.POST.equalsIgnoreCase(httpRequest.getMethod())) {
+      String csrfTokenSession = WebUtils.getCsrfTokenFromHttpRequest(httpRequest);
+      String csrfTokenRequest = httpRequest.getParameter(WebServerConstants.CSRF_TOKEN);
+      if (csrfTokenSession != null && !csrfTokenSession.equals(csrfTokenRequest)) {
+        httpResponse.reset();
+        httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Potential CSRF detected");
+        return;
+      }
+    }
+    chain.doFilter(request, response);
+  }
+
+  @Override
+  public void destroy() {
+  }
+}
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/PageTree.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/PageTree.java
index e4d5dc1..0308883 100644
--- a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/PageTree.java
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/PageTree.java
@@ -20,6 +20,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.SecurityContext;
 
 import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
@@ -58,8 +59,15 @@
    */
 
   public static Map<String, Object> toModel(SecurityContext sc, Object base) {
+    return toModel(sc, base, null);
+  }
+
+  public static Map<String, Object> toModel(SecurityContext sc, Object base, HttpServletRequest request) {
     Map<String, Object> model = new HashMap<>();
     model.put("model", base);
+    if (request != null) {
+      model.put(WebConstants.CSRF_TOKEN, WebUtils.getCsrfTokenFromHttpRequest(request));
+    }
     return toMapModel(sc, model);
   }
 
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebConstants.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebConstants.java
new file mode 100644
index 0000000..365af2c
--- /dev/null
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebConstants.java
@@ -0,0 +1,29 @@
+/*
+ * 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.drill.yarn.appMaster.http;
+
+/**
+ * Holds various constants used by WebServer components.
+ */
+public final class WebConstants {
+
+  private WebConstants() {}
+
+  // Name of the CSRF protection token attribute
+  public static final String CSRF_TOKEN = "csrfToken";
+}
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java
index 75d99d9..dd10b70 100644
--- a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java
@@ -28,8 +28,10 @@
 import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.Date;
+import java.util.EnumSet;
 import java.util.Set;
 
+import javax.servlet.DispatcherType;
 import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSessionEvent;
@@ -38,6 +40,8 @@
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.drill.exec.server.rest.CsrfTokenInjectFilter;
+import org.apache.drill.exec.server.rest.CsrfTokenValidateFilter;
 import org.apache.drill.yarn.appMaster.Dispatcher;
 import org.apache.drill.yarn.core.DrillOnYarnConfig;
 import org.bouncycastle.asn1.x500.X500NameBuilder;
@@ -167,6 +171,13 @@
     restHolder.setInitOrder(2);
     servletContextHandler.addServlet(restHolder, "/rest/*");
 
+    // Applying filters for CSRF protection.
+
+    servletContextHandler.addFilter(CsrfTokenInjectFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+    for (String path : new String[]{"/resize", "/stop", "/cancel"}) {
+      servletContextHandler.addFilter(CsrfTokenValidateFilter.class, path, EnumSet.of(DispatcherType.REQUEST));
+    }
+
     // Static resources (CSS, images, etc.)
 
     setupStaticResources(servletContextHandler);
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUiPageTree.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUiPageTree.java
index 58a59d3..8ad01ca 100644
--- a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUiPageTree.java
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUiPageTree.java
@@ -244,6 +244,9 @@
     @Inject
     private SecurityContext sc;
 
+    @Inject
+    private HttpServletRequest request;
+
     @QueryParam("id")
     private int id;
 
@@ -257,7 +260,7 @@
         confirm = new ConfirmShrink(ConfirmShrink.Mode.CANCEL);
       }
       confirm.id = id;
-      return new Viewable("/drill-am/shrink-warning.ftl", toModel(sc, confirm));
+      return new Viewable("/drill-am/shrink-warning.ftl", toModel(sc, confirm, request));
     }
 
     @POST
@@ -309,12 +312,15 @@
     @Inject
     SecurityContext sc;
 
+    @Inject
+    HttpServletRequest request;
+
     @GET
     @Produces(MediaType.TEXT_HTML)
     public Viewable getRoot() {
       ControllerModel model = new ControllerModel();
       dispatcher.getController().visit(model);
-      return new Viewable("/drill-am/manage.ftl", toModel(sc, model));
+      return new Viewable("/drill-am/manage.ftl", toModel(sc, model, request));
     }
   }
 
@@ -406,6 +412,9 @@
     @Inject
     SecurityContext sc;
 
+    @Inject
+    HttpServletRequest request;
+
     @FormParam("n")
     int n;
     @FormParam("type")
@@ -453,7 +462,7 @@
         ConfirmShrink confirm = new ConfirmShrink(ConfirmShrink.Mode.SHRINK);
         confirm.value = curSize - newSize;
         return new Viewable("/drill-am/shrink-warning.ftl",
-            toModel(sc, confirm));
+            toModel(sc, confirm, request));
       }
     }
   }
@@ -472,11 +481,14 @@
     @Inject
     SecurityContext sc;
 
+    @Inject
+    HttpServletRequest request;
+
     @GET
     @Produces(MediaType.TEXT_HTML)
     public Viewable requestStop() {
       ConfirmShrink confirm = new ConfirmShrink(ConfirmShrink.Mode.STOP);
-      return new Viewable("/drill-am/shrink-warning.ftl", toModel(sc, confirm));
+      return new Viewable("/drill-am/shrink-warning.ftl", toModel(sc, confirm, request));
     }
 
     @POST
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUtils.java b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUtils.java
new file mode 100644
index 0000000..246f3a6
--- /dev/null
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.drill.yarn.appMaster.http;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.security.SecureRandom;
+import java.util.Base64;
+
+public class WebUtils {
+
+  /**
+   * Retrieves the CSRF protection token from the HTTP request.
+   *
+   * @param request HTTP request that contains a session that stores a CSRF protection token.
+   *                If there is no session, that means that authentication is disabled.
+   * @return CSRF protection token, or an empty string if there is no session present.
+   */
+  public static String getCsrfTokenFromHttpRequest(HttpServletRequest request) {
+    // No need to create a session if not present (i.e. if a user is logged in)
+    HttpSession session = request.getSession(false);
+    return session == null ? "" : (String) session.getAttribute(WebConstants.CSRF_TOKEN);
+  }
+
+  /**
+   * Generates a BASE64 encoded CSRF token from randomly generated 256-bit buffer
+   * according to the <a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html">OWASP CSRF Prevention Cheat Sheet</a>
+   *
+   * @return randomly generated CSRF token.
+   */
+  public static String generateCsrfToken() {
+    byte[] buffer = new byte[32];
+    new SecureRandom().nextBytes(buffer);
+    return Base64.getUrlEncoder().withoutPadding().encodeToString(buffer);
+  }
+}
diff --git a/drill-yarn/src/main/java/org/apache/drill/yarn/client/DrillOnYarn.java b/drill-yarn/src/main/java/org/apache/drill/yarn/client/DrillOnYarn.java
index 587766a..54900d3 100644
--- a/drill-yarn/src/main/java/org/apache/drill/yarn/client/DrillOnYarn.java
+++ b/drill-yarn/src/main/java/org/apache/drill/yarn/client/DrillOnYarn.java
@@ -18,6 +18,8 @@
 package org.apache.drill.yarn.client;
 
 
+import org.apache.drill.common.util.GuavaPatcher;
+import org.apache.drill.common.util.ProtobufPatcher;
 import org.apache.drill.yarn.core.DoyConfigException;
 import org.apache.drill.yarn.core.DrillOnYarnConfig;
 import org.apache.log4j.BasicConfigurator;
@@ -73,6 +75,22 @@
  */
 
 public class DrillOnYarn {
+
+  static {
+    /*
+     * Drill-on-YARN uses Hadoop dependencies that use older version of protobuf,
+     * and override some methods that became final in recent protobuf versions.
+     * This code removes these final modifiers.
+     */
+    ProtobufPatcher.patch();
+    /*
+     * HBase client uses older version of Guava's Stopwatch API,
+     * while Drill ships with 18.x which has changes the scope of
+     * these API to 'package', this code make them accessible.
+     */
+    GuavaPatcher.patch();
+  }
+
   public static void main(String argv[]) {
     BasicConfigurator.configure();
     ClientContext.init();
@@ -107,35 +125,35 @@
 
     ClientCommand cmd;
     switch (opts.getCommand()) {
-    case UPLOAD:
-      cmd = new StartCommand(true, false);
-      break;
-    case START:
-      cmd = new StartCommand(true, true);
-      break;
-    // Removed at QA request. QA wants a "real" restart. Also, upload of the
-    // archive is fast enough that a "start without upload" option is not really
-    // needed.
+      case UPLOAD:
+        cmd = new StartCommand(true, false);
+        break;
+      case START:
+        cmd = new StartCommand(true, true);
+        break;
+      // Removed at QA request. QA wants a "real" restart. Also, upload of the
+      // archive is fast enough that a "start without upload" option is not really
+      // needed.
 //    case RESTART:
 //      cmd = new StartCommand(false, true);
 //      break;
-    case DESCRIBE:
-      cmd = new PrintConfigCommand();
-      break;
-    case STATUS:
-      cmd = new StatusCommand();
-      break;
-    case STOP:
-      cmd = new StopCommand();
-      break;
-    case CLEAN:
-      cmd = new CleanCommand();
-      break;
-    case RESIZE:
-      cmd = new ResizeCommand();
-      break;
-    default:
-      cmd = new HelpCommand();
+      case DESCRIBE:
+        cmd = new PrintConfigCommand();
+        break;
+      case STATUS:
+        cmd = new StatusCommand();
+        break;
+      case STOP:
+        cmd = new StopCommand();
+        break;
+      case CLEAN:
+        cmd = new CleanCommand();
+        break;
+      case RESIZE:
+        cmd = new ResizeCommand();
+        break;
+      default:
+        cmd = new HelpCommand();
     }
 
     // Run the command.
diff --git a/drill-yarn/src/main/resources/drill-am/manage.ftl b/drill-yarn/src/main/resources/drill-am/manage.ftl
index 2c26143..be16385 100644
--- a/drill-yarn/src/main/resources/drill-am/manage.ftl
+++ b/drill-yarn/src/main/resources/drill-am/manage.ftl
@@ -69,6 +69,7 @@
           drillbits.
           <button type="submit" class="btn btn-primary" style="margin: 0 1em;">Go</button>
         </div>
+        <input type="hidden" name="csrfToken" value="${csrfToken}">
       </form>
     </td></tr>
     <tr><td>
diff --git a/drill-yarn/src/main/resources/drill-am/shrink-warning.ftl b/drill-yarn/src/main/resources/drill-am/shrink-warning.ftl
index 3f5a9e3..4b0bbcc 100644
--- a/drill-yarn/src/main/resources/drill-am/shrink-warning.ftl
+++ b/drill-yarn/src/main/resources/drill-am/shrink-warning.ftl
@@ -60,6 +60,7 @@
   </#if>
   <input type="submit" value="Confirm"> or
   <a href="/">Cancel</a>.
+      <input type="hidden" name="csrfToken" value="${csrfToken}">
   </form>
 </#macro>
 
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/CsrfTokenInjectFilter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/CsrfTokenInjectFilter.java
new file mode 100644
index 0000000..dcedab2
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/CsrfTokenInjectFilter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.drill.exec.server.rest;
+
+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.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.HttpMethod;
+import java.io.IOException;
+
+/**
+ * Generates and adds a CSRF token to a HTTP session. It is used to prevent CSRF attacks.
+ */
+public class CsrfTokenInjectFilter implements Filter {
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+    if (HttpMethod.GET.equalsIgnoreCase(httpRequest.getMethod())) {
+      // We don't create a session with this call as we need to check if there is a session already (i.e. if a user is logged in).
+      HttpSession session = httpRequest.getSession(false);
+      if (session != null) {
+        String csrfToken = (String) session.getAttribute(WebServerConstants.CSRF_TOKEN);
+        if (csrfToken == null) {
+          csrfToken = WebUtils.generateCsrfToken();
+          httpRequest.getSession().setAttribute(WebServerConstants.CSRF_TOKEN, csrfToken);
+        }
+      }
+    }
+    chain.doFilter(request, response);
+  }
+
+  @Override
+  public void destroy() {
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/CsrfTokenValidateFilter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/CsrfTokenValidateFilter.java
new file mode 100644
index 0000000..439d89f
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/CsrfTokenValidateFilter.java
@@ -0,0 +1,61 @@
+/*
+ * 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.drill.exec.server.rest;
+
+
+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.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.HttpMethod;
+import java.io.IOException;
+
+/**
+ * All forms on site have a field with a CSRF token injected by server.
+ * This filter compares the token with one stored in the session to ensure that the POST request is not a CSRF attack.
+ */
+public class CsrfTokenValidateFilter implements Filter {
+
+  @Override
+  public void init(FilterConfig filterConfig) throws ServletException {
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest httpRequest = (HttpServletRequest) request;
+    HttpServletResponse httpResponse = (HttpServletResponse) response;
+    if (HttpMethod.POST.equalsIgnoreCase(httpRequest.getMethod())) {
+      String csrfTokenSession = WebUtils.getCsrfTokenFromHttpRequest(httpRequest);
+      String csrfTokenRequest = httpRequest.getParameter(WebServerConstants.CSRF_TOKEN);
+      if (csrfTokenSession != null && !csrfTokenSession.equals(csrfTokenRequest)) {
+        httpResponse.reset();
+        httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Potential CSRF detected");
+        return;
+      }
+    }
+    chain.doFilter(request, response);
+  }
+
+  @Override
+  public void destroy() {
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
index 60746e9..75d6653 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
@@ -28,9 +28,12 @@
 import org.apache.drill.exec.server.rest.QueryWrapper.QueryResult;
 import org.apache.drill.exec.work.WorkManager;
 import org.glassfish.jersey.server.mvc.Viewable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
@@ -47,13 +50,22 @@
 @Path("/")
 @RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
 public class QueryResources {
-  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(QueryResources.class);
+   private static final Logger logger = LoggerFactory.getLogger(QueryResources.class);
 
-  @Inject UserAuthEnabled authEnabled;
-  @Inject WorkManager work;
-  @Inject SecurityContext sc;
-  @Inject WebUserConnection webUserConnection;
+  @Inject
+  UserAuthEnabled authEnabled;
 
+  @Inject
+  WorkManager work;
+
+  @Inject
+  SecurityContext sc;
+
+  @Inject
+  WebUserConnection webUserConnection;
+
+  @Inject
+  HttpServletRequest request;
 
   @GET
   @Path("/query")
@@ -61,7 +73,7 @@
   public Viewable getQuery() {
     return ViewableWithPermissions.create(
         authEnabled.get(), "/rest/query/query.ftl",
-        sc, new QueryPage(work));
+        sc, new QueryPage(work, request));
   }
 
   @POST
@@ -105,13 +117,15 @@
     private final boolean onlyImpersonationEnabled;
     private final boolean autoLimitEnabled;
     private final int defaultRowsAutoLimited;
+    private final String csrfToken;
 
-    public QueryPage(WorkManager work) {
+    public QueryPage(WorkManager work, HttpServletRequest request) {
       DrillConfig config = work.getContext().getConfig();
       //if impersonation is enabled without authentication, will provide mechanism to add user name to request header from Web UI
       onlyImpersonationEnabled = WebServer.isOnlyImpersonationEnabled(config);
       autoLimitEnabled = config.getBoolean(ExecConstants.HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_CHECKED);
       defaultRowsAutoLimited = config.getInt(ExecConstants.HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_ROWS);
+      csrfToken = WebUtils.getCsrfTokenFromHttpRequest(request);
     }
 
     public boolean isOnlyImpersonationEnabled() {
@@ -125,6 +139,10 @@
     public int getDefaultRowsAutoLimited() {
       return defaultRowsAutoLimited;
     }
+
+    public String getCsrfToken() {
+      return csrfToken;
+    }
   }
 
   /**
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
index f57a8b5..a27d9c1 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
@@ -26,6 +26,7 @@
 import javax.annotation.security.PermitAll;
 import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
@@ -70,9 +71,17 @@
   //Used to access current filter state in WebUI
   private static final String CURRENT_FILTER_PARAM = "filter";
 
-  @Inject UserAuthEnabled authEnabled;
-  @Inject WorkManager work;
-  @Inject SecurityContext sc;
+  @Inject
+  UserAuthEnabled authEnabled;
+
+  @Inject
+  WorkManager work;
+
+  @Inject
+  SecurityContext sc;
+
+  @Inject
+  HttpServletRequest request;
 
   @GET
   @Path(StatusResources.PATH_STATUS_JSON)
@@ -135,7 +144,7 @@
     return ViewableWithPermissions.create(authEnabled.get(),
       "/rest/options.ftl",
       sc,
-      new OptionsListing(options, fltrList, currFilter));
+      new OptionsListing(options, fltrList, currFilter, request));
   }
 
   @GET
@@ -185,22 +194,30 @@
     private final List<OptionWrapper> options;
     private final List<String> filters;
     private final String dynamicFilter;
+    private final String csrfToken;
 
-    public OptionsListing(List<OptionWrapper> optList, List<String> fltrList, String currFilter) {
+    public OptionsListing(List<OptionWrapper> optList, List<String> fltrList, String currFilter, HttpServletRequest request) {
       this.options = optList;
       this.filters = fltrList;
       this.dynamicFilter = currFilter;
+      csrfToken = WebUtils.getCsrfTokenFromHttpRequest(request);
     }
 
     public List<OptionWrapper> getOptions() {
       return options;
     }
+
     public List<String> getFilters() {
       return filters;
     }
+
     public String getDynamicFilter() {
       return dynamicFilter;
     }
+
+    public String getCsrfToken() {
+      return csrfToken;
+    }
   }
 
   @XmlRootElement
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
index c3de7f5..f1ccc2f 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StorageResources.java
@@ -30,6 +30,7 @@
 
 import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.FormParam;
@@ -56,18 +57,30 @@
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import static org.apache.drill.exec.server.rest.auth.DrillUserPrincipal.ADMIN_ROLE;
 
 @Path("/")
 @RolesAllowed(ADMIN_ROLE)
 public class StorageResources {
-  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(StorageResources.class);
+  private static final Logger logger = LoggerFactory.getLogger(StorageResources.class);
 
-  @Inject UserAuthEnabled authEnabled;
-  @Inject StoragePluginRegistry storage;
-  @Inject ObjectMapper mapper;
-  @Inject SecurityContext sc;
+  @Inject
+  UserAuthEnabled authEnabled;
+
+  @Inject
+  StoragePluginRegistry storage;
+
+  @Inject
+  ObjectMapper mapper;
+
+  @Inject
+  SecurityContext sc;
+
+  @Inject
+  HttpServletRequest request;
 
   private static final String JSON_FORMAT = "json";
   private static final String HOCON_FORMAT = "conf";
@@ -112,8 +125,14 @@
   @Path("/storage")
   @Produces(MediaType.TEXT_HTML)
   public Viewable getPlugins() {
-    List<PluginConfigWrapper> list = getPluginsJSON();
-    return ViewableWithPermissions.create(authEnabled.get(), "/rest/storage/list.ftl", sc, list);
+    List<StoragePluginModel> model = getPluginsJSON().stream()
+        .map(plugin -> new StoragePluginModel(plugin, request))
+        .collect(Collectors.toList());
+    // Creating an empty model with CSRF token, if there are no storage plugins
+    if (model.size() == 0) {
+      model.add(new StoragePluginModel(null, request));
+    }
+    return ViewableWithPermissions.create(authEnabled.get(), "/rest/storage/list.ftl", sc, model);
   }
 
   @GET
@@ -135,8 +154,9 @@
   @Path("/storage/{name}")
   @Produces(MediaType.TEXT_HTML)
   public Viewable getPlugin(@PathParam("name") String name) {
+    StoragePluginModel model = new StoragePluginModel(getPluginConfig(name), request);
     return ViewableWithPermissions.create(authEnabled.get(), "/rest/storage/update.ftl", sc,
-        getPluginConfig(name));
+        model);
   }
 
   @GET
@@ -301,4 +321,26 @@
     }
 
   }
+
+  /**
+   * Model class for Storage Plugin page.
+   * It contains a storage plugin as well as the CSRK token for the page.
+   */
+  public static class StoragePluginModel {
+    private final PluginConfigWrapper plugin;
+    private final String csrfToken;
+
+    public StoragePluginModel(PluginConfigWrapper plugin, HttpServletRequest request) {
+      this.plugin = plugin;
+      csrfToken = WebUtils.getCsrfTokenFromHttpRequest(request);
+    }
+
+    public PluginConfigWrapper getPlugin() {
+      return plugin;
+    }
+
+    public String getCsrfToken() {
+      return csrfToken;
+    }
+  }
 }
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
index bc093ad..90fd629 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
@@ -242,6 +242,12 @@
       servletContextHandler.setSessionHandler(createSessionHandler(servletContextHandler.getSecurityHandler()));
     }
 
+    // Applying filters for CSRF protection.
+    servletContextHandler.addFilter(CsrfTokenInjectFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+    for (String path : new String[]{"/query", "/storage/create_update", "/option/*"}) {
+      servletContextHandler.addFilter(CsrfTokenValidateFilter.class, path, EnumSet.of(DispatcherType.REQUEST));
+    }
+
     if (isOnlyImpersonationEnabled(workManager.getContext().getConfig())) {
       for (String path : new String[]{"/query", "/query.json"}) {
         servletContextHandler.addFilter(UserNameFilter.class, path, EnumSet.of(DispatcherType.REQUEST));
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java
index 5cd1c5f..f64aa3c 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServerConstants.java
@@ -42,4 +42,7 @@
   // Logout page
   public static final String LOGOUT_RESOURCE_NAME = "logout";
   public static final String LOGOUT_RESOURCE_PATH = WEBSERVER_ROOT_PATH + LOGOUT_RESOURCE_NAME;
-}
\ No newline at end of file
+
+  // Name of the CSRF protection token attribute
+  public static final String CSRF_TOKEN = "csrfToken";
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebUtils.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebUtils.java
new file mode 100644
index 0000000..4e1b99a
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.drill.exec.server.rest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.security.SecureRandom;
+import java.util.Base64;
+
+public class WebUtils {
+
+  /**
+   * Retrieves the CSRF protection token from the HTTP request.
+   *
+   * @param request HTTP request that contains a session that stores a CSRF protection token.
+   *                If there is no session, that means that authentication is disabled.
+   * @return CSRF protection token, or an empty string if there is no session present.
+   */
+  public static String getCsrfTokenFromHttpRequest(HttpServletRequest request) {
+    // No need to create a session if not present (i.e. if a user is logged in)
+    HttpSession session = request.getSession(false);
+    return session == null ? "" : (String) session.getAttribute(WebServerConstants.CSRF_TOKEN);
+  }
+
+  /**
+   * Generates a BASE64 encoded CSRF token from randomly generated 256-bit buffer
+   * according to the <a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html">OWASP CSRF Prevention Cheat Sheet</a>
+   *
+   * @return randomly generated CSRF token.
+   */
+  public static String generateCsrfToken() {
+    byte[] buffer = new byte[32];
+    new SecureRandom().nextBytes(buffer);
+    return Base64.getUrlEncoder().withoutPadding().encodeToString(buffer);
+  }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
index 1819962..e14e696 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java
@@ -27,6 +27,7 @@
 
 import javax.annotation.security.RolesAllowed;
 import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -65,10 +66,20 @@
 public class ProfileResources {
   private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProfileResources.class);
 
-  @Inject UserAuthEnabled authEnabled;
-  @Inject WorkManager work;
-  @Inject DrillUserPrincipal principal;
-  @Inject SecurityContext sc;
+  @Inject
+  UserAuthEnabled authEnabled;
+
+  @Inject
+  WorkManager work;
+
+  @Inject
+  DrillUserPrincipal principal;
+
+  @Inject
+  SecurityContext sc;
+
+  @Inject
+  HttpServletRequest request;
 
   public static class ProfileInfo implements Comparable<ProfileInfo> {
     private static final int QUERY_SNIPPET_MAX_CHAR = 150;
@@ -375,7 +386,7 @@
   @Produces(MediaType.TEXT_HTML)
   public Viewable getProfile(@PathParam("queryid") String queryId){
     try {
-      ProfileWrapper wrapper = new ProfileWrapper(getQueryProfile(queryId), work.getContext().getConfig());
+      ProfileWrapper wrapper = new ProfileWrapper(getQueryProfile(queryId), work.getContext().getConfig(), request);
       return ViewableWithPermissions.create(authEnabled.get(), "/rest/profile/profile.ftl", sc, wrapper);
     } catch (Exception | Error e) {
       logger.error("Exception was thrown when fetching profile {} :\n{}", queryId, e);
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
index 5c144eb..e5c4133 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
@@ -41,10 +41,13 @@
 import org.apache.drill.exec.server.options.OptionList;
 import org.apache.drill.exec.server.options.OptionValue;
 import org.apache.drill.exec.server.rest.WebServer;
+import org.apache.drill.exec.server.rest.WebUtils;
 import org.apache.drill.shaded.guava.com.google.common.base.CaseFormat;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * Wrapper class for a {@link #profile query profile}, so it to be presented through web UI.
  */
@@ -66,14 +69,16 @@
   private final String noProgressWarningThreshold;
   private final int defaultAutoLimit;
   private boolean showEstimatedRows;
+  private final String csrfToken;
 
-  public ProfileWrapper(final QueryProfile profile, DrillConfig drillConfig) {
+  public ProfileWrapper(final QueryProfile profile, DrillConfig drillConfig, HttpServletRequest request) {
     this.profile = profile;
     this.id = profile.hasQueryId() ? profile.getQueryId() : QueryIdHelper.getQueryId(profile.getId());
     this.defaultAutoLimit = drillConfig.getInt(ExecConstants.HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_ROWS);
     //Generating Operator Name map (DRILL-6140)
     String profileTextPlan = profile.hasPlan()? profile.getPlan(): "";
     generateOpMap(profileTextPlan);
+    csrfToken = WebUtils.getCsrfTokenFromHttpRequest(request);
 
     final List<FragmentWrapper> fragmentProfiles = new ArrayList<>();
 
@@ -280,10 +285,10 @@
   }
 
   //Threshold to be used by WebServer in issuing warning
+
   public String getNoProgressWarningThreshold() {
     return this.noProgressWarningThreshold;
   }
-
   public List<FragmentWrapper> getFragmentProfiles() {
     return fragmentProfiles;
   }
@@ -373,6 +378,7 @@
   }
 
   //Generates operator names inferred from physical plan
+
   private void generateOpMap(String plan) {
     this.physicalOperatorMap = new HashMap<>();
     if (plan.isEmpty()) {
@@ -392,8 +398,11 @@
       physicalOperatorMap.put(operatorPath, extractedOperatorName);
     }
   }
-
   public boolean showEstimatedRows() {
     return showEstimatedRows;
   }
+
+  public String getCsrfToken() {
+    return csrfToken;
+  }
 }
diff --git a/exec/java-exec/src/main/resources/rest/options.ftl b/exec/java-exec/src/main/resources/rest/options.ftl
index d05dd7f..c01207d 100644
--- a/exec/java-exec/src/main/resources/rest/options.ftl
+++ b/exec/java-exec/src/main/resources/rest/options.ftl
@@ -33,7 +33,12 @@
         } else { //Apply filter for updated field
             redirectHref = redirectHref + "?filter=" + optionName;
         }
-        $.post("/option/"+optionName, {kind: optionKind, name: optionName, value: optionValue}, function () {
+        $.post("/option/"+optionName, {
+          kind: optionKind,
+          name: optionName,
+          value: optionValue,
+          csrfToken: "${model.getCsrfToken()}"
+        }, function () {
             //Remove existing filters
             location.href=redirectHref;
         });
diff --git a/exec/java-exec/src/main/resources/rest/profile/profile.ftl b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
index 303d83d..704c2dd 100644
--- a/exec/java-exec/src/main/resources/rest/profile/profile.ftl
+++ b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
@@ -201,6 +201,7 @@
               </button>
               <input type="checkbox" name="forceLimit" value="limit" <#if model.hasAutoLimit()>checked</#if>> Limit results to <input type="text" id="autoLimit" name="autoLimit" min="0" value="<#if model.hasAutoLimit()>${model.getAutoLimit()?c}<#else>${model.getDefaultAutoLimit()?c}</#if>" size="6" pattern="[0-9]*"> rows <span class="glyphicon glyphicon-info-sign" title="Limits the number of records retrieved in the query. Ignored if query has a limit already" style="cursor:pointer"></span>
             </div>
+            <input type="hidden" name="csrfToken" value="${model.getCsrfToken()}">
           </form>
       </p>
 
diff --git a/exec/java-exec/src/main/resources/rest/query/query.ftl b/exec/java-exec/src/main/resources/rest/query/query.ftl
index 8a4942c..aea8bb3 100644
--- a/exec/java-exec/src/main/resources/rest/query/query.ftl
+++ b/exec/java-exec/src/main/resources/rest/query/query.ftl
@@ -83,6 +83,7 @@
       Submit
     </button>
     <input type="checkbox" name="forceLimit" value="limit" <#if model.isAutoLimitEnabled()>checked</#if>> Limit results to <input type="text" id="autoLimit" name="autoLimit" min="0" value="${model.getDefaultRowsAutoLimited()?c}" size="6" pattern="[0-9]*"> rows <span class="glyphicon glyphicon-info-sign" title="Limits the number of records retrieved in the query. Ignored if query has a limit already" style="cursor:pointer"></span>
+    <input type="hidden" name="csrfToken" value="${model.getCsrfToken()}">
   </form>
 
   <script>
diff --git a/exec/java-exec/src/main/resources/rest/storage/list.ftl b/exec/java-exec/src/main/resources/rest/storage/list.ftl
index f79485c..3bf547e 100644
--- a/exec/java-exec/src/main/resources/rest/storage/list.ftl
+++ b/exec/java-exec/src/main/resources/rest/storage/list.ftl
@@ -56,20 +56,20 @@
     <h4>Enabled Storage Plugins</h4>
     <table class="table table-hover">
       <tbody>
-        <#list model as plugin>
-          <#if plugin.enabled() == true>
+        <#list model as pluginModel>
+          <#if pluginModel.getPlugin()?? && pluginModel.getPlugin().enabled() == true>
             <tr>
               <td style="border:none; max-width: 200px; overflow: hidden; text-overflow: ellipsis;">
-                ${plugin.getName()}
+                ${pluginModel.getPlugin().getName()}
               </td>
               <td style="border:none;">
-                <button type="button" class="btn btn-primary" onclick="doUpdate('${plugin.getName()}')">
+                <button type="button" class="btn btn-primary" onclick="doUpdate('${pluginModel.getPlugin().getName()}')">
                   Update
                 </button>
-                <button type="button" class="btn btn-warning" onclick="doEnable('${plugin.getName()}', false)">
+                <button type="button" class="btn btn-warning" onclick="doEnable('${pluginModel.getPlugin().getName()}', false)">
                   Disable
                 </button>
-                <button type="button" class="btn" name="${plugin.getName()}" data-toggle="modal"
+                <button type="button" class="btn" name="${pluginModel.getPlugin().getName()}" data-toggle="modal"
                         data-target="#pluginsModal">
                   Export
                 </button>
@@ -85,20 +85,20 @@
     <h4>Disabled Storage Plugins</h4>
     <table class="table table-hover">
       <tbody>
-        <#list model as plugin>
-          <#if plugin.enabled() == false>
+        <#list model as pluginModel>
+          <#if pluginModel.getPlugin()?? && pluginModel.getPlugin().enabled() == false>
             <tr>
               <td style="border:none; max-width: 200px; overflow: hidden; text-overflow: ellipsis;">
-                ${plugin.getName()}
+                ${pluginModel.getPlugin().getName()}
               </td>
               <td style="border:none;">
-                <button type="button" class="btn btn-primary" onclick="doUpdate('${plugin.getName()}')">
+                <button type="button" class="btn btn-primary" onclick="doUpdate('${pluginModel.getPlugin().getName()}')">
                   Update
                 </button>
-                <button type="button" class="btn btn-success" onclick="doEnable('${plugin.getName()}', true)">
+                <button type="button" class="btn btn-success" onclick="doEnable('${pluginModel.getPlugin().getName()}', true)">
                   Enable
                 </button>
-                <button type="button" class="btn" name="${plugin.getName()}" data-toggle="modal"
+                <button type="button" class="btn" name="${pluginModel.getPlugin().getName()}" data-toggle="modal"
                         data-target="#pluginsModal">
                   Export
                 </button>
@@ -190,6 +190,7 @@
               <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
               <button type="submit" class="btn btn-primary" onclick="doCreate()">Create</button>
             </div>
+            <input type="hidden" name="csrfToken" value="${model[0].getCsrfToken()}">
           </form>
 
           <div id="message" class="hidden alert alert-info">
diff --git a/exec/java-exec/src/main/resources/rest/storage/update.ftl b/exec/java-exec/src/main/resources/rest/storage/update.ftl
index d077ea0..9d7b577 100644
--- a/exec/java-exec/src/main/resources/rest/storage/update.ftl
+++ b/exec/java-exec/src/main/resources/rest/storage/update.ftl
@@ -32,7 +32,7 @@
   </div>
   <h3>Configuration</h3>
   <form id="updateForm" role="form" action="/storage/create_update" method="POST">
-    <input type="hidden" name="name" value="${model.getName()}" />
+    <input type="hidden" name="name" value="${model.getPlugin().getName()}" />
     <div class="form-group">
       <div id="editor" class="form-control"></div>
       <textarea class="form-control" id="config" name="config" data-editor="json" style="display: none;" >
@@ -40,16 +40,17 @@
     </div>
     <a class="btn btn-default" href="/storage">Back</a>
     <button class="btn btn-default" type="submit" onclick="doUpdate();">Update</button>
-    <#if model.enabled()>
+    <#if model.getPlugin().enabled()>
       <a id="enabled" class="btn btn-default">Disable</a>
     <#else>
       <a id="enabled" class="btn btn-primary">Enable</a>
     </#if>
-    <button type="button" class="btn btn-default export" name="${model.getName()}" data-toggle="modal"
+    <button type="button" class="btn btn-default export" name="${model.getPlugin().getName()}" data-toggle="modal"
             data-target="#pluginsModal">
       Export
     </button>
     <a id="del" class="btn btn-danger" onclick="deleteFunction()">Delete</a>
+    <input type="hidden" name="csrfToken" value="${model.getCsrfToken()}">
   </form>
   <br>
   <div id="message" class="hidden alert alert-info">
@@ -108,21 +109,21 @@
       textarea.val(editor.getSession().getValue());
     });
 
-    $.get("/storage/" + encodeURIComponent("${model.getName()}") + ".json", function(data) {
+    $.get("/storage/" + encodeURIComponent("${model.getPlugin().getName()}") + ".json", function(data) {
       $("#config").val(JSON.stringify(data.config, null, 2));
       editor.getSession().setValue( JSON.stringify(data.config, null, 2) );
     });
 
 
     $("#enabled").click(function() {
-      const enabled = ${model.enabled()?c};
+      const enabled = ${model.getPlugin().enabled()?c};
       if (enabled) {
-        showConfirmationDialog('"${model.getName()}"' + ' plugin will be disabled. Proceed?', proceed);
+        showConfirmationDialog('"${model.getPlugin().getName()}"' + ' plugin will be disabled. Proceed?', proceed);
       } else {
         proceed();
       }
       function proceed() {
-        $.get("/storage/" + encodeURIComponent("${model.getName()}") + "/enable/<#if model.enabled()>false<#else>true</#if>", function(data) {
+        $.get("/storage/" + encodeURIComponent("${model.getPlugin().getName()}") + "/enable/<#if model.getPlugin().enabled()>false<#else>true</#if>", function(data) {
           $("#message").removeClass("hidden").text(data.result).alert();
           setTimeout(function() { location.reload(); }, 800);
         });
@@ -137,8 +138,8 @@
     }
 
     function deleteFunction() {
-      showConfirmationDialog('"${model.getName()}"' + ' plugin will be deleted. Proceed?', function() {
-        $.get("/storage/" + encodeURIComponent("${model.getName()}") + "/delete", serverMessage);
+      showConfirmationDialog('"${model.getPlugin().getName()}"' + ' plugin will be deleted. Proceed?', function() {
+        $.get("/storage/" + encodeURIComponent("${model.getPlugin().getName()}") + "/delete", serverMessage);
       });
     }