Index: apache-tomcat-6.0.53-src/conf/server.xml
===================================================================
--- apache-tomcat-6.0.53-src/conf/server.xml.orig
+++ apache-tomcat-6.0.53-src/conf/server.xml
@@ -87,8 +87,11 @@
     -->
 
     <!-- Define an AJP 1.3 Connector on port 8009 -->
-    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-
+    <!--
+    <Connector port="8009"
+               protocol="AJP/1.3"
+               redirectPort="8443" />
+    -->
 
     <!-- An Engine represents the entry point (within Catalina) that processes
          every request.  The Engine implementation for Tomcat stand alone
Index: apache-tomcat-6.0.53-src/java/org/apache/tomcat/util/compat/Jre7Compat.java
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/tomcat/util/compat/Jre7Compat.java.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/tomcat/util/compat/Jre7Compat.java
@@ -18,23 +18,27 @@ package org.apache.tomcat.util.compat;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.net.InetAddress;
 import java.util.Locale;
 
 class Jre7Compat extends Jre6Compat {
 
     private static final Method forLanguageTagMethod;
-
+    private static final Method getLoopbackAddress;
 
     static {
         Method m = null;
+        Method m2 = null;
         try {
             m = Locale.class.getMethod("forLanguageTag", String.class);
+            m2 = InetAddress.class.getMethod("getLoopbackAddress");
         } catch (SecurityException e) {
             // Should never happen
         } catch (NoSuchMethodException e) {
             // Expected on Java < 7
         }
         forLanguageTagMethod = m;
+        getLoopbackAddress = m2;
     }
 
 
@@ -55,4 +59,18 @@ class Jre7Compat extends Jre6Compat {
             return null;
         }
     }
+
+    @Override
+    public InetAddress getLoopbackAddress() {
+        try {
+            return (InetAddress) getLoopbackAddress.invoke(null);
+        } catch (IllegalArgumentException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (IllegalAccessException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (InvocationTargetException e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+
 }
Index: apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpAprProtocol.java
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpAprProtocol.java.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpAprProtocol.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentLi
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
 
 import javax.management.MBeanRegistration;
 import javax.management.MBeanServer;
@@ -34,6 +35,7 @@ import org.apache.coyote.ActionCode;
 import org.apache.coyote.Adapter;
 import org.apache.coyote.RequestGroupInfo;
 import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.compat.JreCompat;
 import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.net.AbstractEndpoint;
 import org.apache.tomcat.util.net.AprEndpoint;
@@ -68,6 +70,7 @@ public class AjpAprProtocol extends Abst
 
 
     public AjpAprProtocol() {
+        endpoint.setAddress(JreCompat.getInstance().getLoopbackAddress());
         cHandler = new AjpConnectionHandler(this);
         setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
         setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
@@ -155,6 +158,12 @@ public class AjpAprProtocol extends Abst
     /** Start the protocol
      */
     public void init() throws Exception {
+        if (getSecretRequired()) {
+            String secret = getSecret();
+            if (secret == null || secret.length() == 0) {
+                throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
+            }
+        }
         endpoint.setName(getName());
         endpoint.setHandler(cHandler);
         endpoint.setUseSendfile(false);
@@ -172,6 +181,12 @@ public class AjpAprProtocol extends Abst
 
 
     public void start() throws Exception {
+        if (getSecretRequired()) {
+            String secret = getSecret();
+            if (secret == null || secret.length() == 0) {
+                throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
+            }
+        }
         if (this.domain != null ) {
             try {
                 tpOname = new ObjectName
@@ -280,7 +295,45 @@ public class AjpAprProtocol extends Abst
      * Required secret.
      */
     protected String requiredSecret = null;
-    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+    private String secret = null;
+    /**
+     * Set the secret that must be included with every request.
+     *
+     * @param secret The required secret
+     */
+    public void setSecret(String secret) {
+        this.secret = secret;
+        this.requiredSecret = secret;
+    }
+    protected String getSecret() {
+        return secret;
+    }
+    @Deprecated
+    public void setRequiredSecret(String requiredSecret) { setSecret(requiredSecret); }
+
+    @Deprecated
+    protected String getRequiredSecret() {
+        return getSecret();
+    }
+
+    private boolean secretRequired = false;
+    public void setSecretRequired(boolean secretRequired) {
+        this.secretRequired = secretRequired;
+    }
+    public boolean getSecretRequired() {
+        return secretRequired;
+    }
+
+    private Pattern allowedRequestAttributesPatternPattern;
+    public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
+        this.allowedRequestAttributesPatternPattern = Pattern.compile(allowedRequestAttributesPattern);
+    }
+    public String getAllowedRequestAttributesPattern() {
+        return allowedRequestAttributesPatternPattern.pattern();
+    }
+    protected Pattern getAllowedRequestAttributesPatternPattern() {
+        return allowedRequestAttributesPatternPattern;
+    }
 
     /**
      * AJP packet size.
@@ -432,9 +485,10 @@ public class AjpAprProtocol extends Abst
             AjpAprProcessor processor = new AjpAprProcessor(proto.packetSize, proto.endpoint);
             processor.setAdapter(proto.adapter);
             processor.setTomcatAuthentication(proto.tomcatAuthentication);
-            processor.setRequiredSecret(proto.requiredSecret);
+            processor.setSecret(proto.getSecret());
             processor.setClientCertProvider(proto.getClientCertProvider());
             processor.setMaxCookieCount(proto.getMaxCookieCount());
+            processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
             register(processor);
             return processor;
         }
Index: apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpProcessor.java
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpProcessor.java.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -27,6 +27,11 @@ import java.net.Socket;
 import java.security.NoSuchProviderException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletResponse;
 
@@ -270,6 +275,8 @@ public class AjpProcessor implements Act
     protected static final byte[] pongMessageArray;
 
 
+    private static final Set<String> javaxAttributes;
+
     /**
      * End message array.
      */
@@ -316,6 +323,13 @@ public class AjpProcessor implements Act
         System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
                 flushMessage.getLen());
 
+        // Build the Set of javax attributes
+        Set<String> s = new HashSet<String>();
+        s.add("javax.servlet.request.cipher_suite");
+        s.add("javax.servlet.request.key_size");
+        s.add("javax.servlet.request.ssl_session");
+        s.add("javax.servlet.request.X509Certificate");
+        javaxAttributes= Collections.unmodifiableSet(s);
     }
 
 
@@ -333,8 +347,15 @@ public class AjpProcessor implements Act
     /**
      * Required secret.
      */
+    @Deprecated
     protected String requiredSecret = null;
-    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+    protected String secret = null;
+    public void setSecret(String secret) {
+        this.secret = secret;
+        this.requiredSecret = secret;
+    }
+    @Deprecated
+    public void setRequiredSecret(String requiredSecret) { setSecret(requiredSecret); }
 
 
     /**
@@ -365,7 +386,13 @@ public class AjpProcessor implements Act
     public int getMaxCookieCount() { return maxCookieCount; }
     public void setMaxCookieCount(int maxCookieCount) { this.maxCookieCount = maxCookieCount; }
 
-    
+
+    private Pattern allowedRequestAttributesPatternPattern;
+    public void setAllowedRequestAttributesPatternPattern(Pattern allowedRequestAttributesPatternPattern) {
+        this.allowedRequestAttributesPatternPattern = allowedRequestAttributesPatternPattern;
+    }
+
+
     // --------------------------------------------------------- Public Methods
 
 
@@ -768,7 +795,7 @@ public class AjpProcessor implements Act
         }
 
         // Decode extra attributes
-        boolean secret = false;
+        boolean secretPresentInRequest = false;
         byte attributeCode;
         while ((attributeCode = requestHeaderMessage.getByte())
                 != Constants.SC_A_ARE_DONE) {
@@ -794,8 +821,25 @@ public class AjpProcessor implements Act
                         request.setRemotePort(Integer.parseInt(v));
                     } catch (NumberFormatException nfe) {
                     }
+                } else if (n.equals("JK_LB_ACTIVATION")) {
+                    request.setAttribute(n, v);
+                } else if (javaxAttributes.contains(n)) {
+                    request.setAttribute(n, v);
                 } else {
-                    request.setAttribute(n, v );
+                    // All 'known' attributes will be processed by the previous
+                    // blocks. Any remaining attribute is an 'arbitrary' one.
+                    if (allowedRequestAttributesPatternPattern == null) {
+                        response.setStatus(403);
+                        error = true;
+                    } else {
+                        Matcher m = allowedRequestAttributesPatternPattern.matcher(n);
+                        if (m.matches()) {
+                            request.setAttribute(n, v);
+                        } else {
+                            response.setStatus(403);
+                            error = true;
+                        }
+                    }
                 }
                 break;
 
@@ -866,9 +910,9 @@ public class AjpProcessor implements Act
 
             case Constants.SC_A_SECRET:
                 requestHeaderMessage.getBytes(tmpMB);
-                if (requiredSecret != null) {
-                    secret = true;
-                    if (!tmpMB.equals(requiredSecret)) {
+                if (secret != null) {
+                    secretPresentInRequest = true;
+                    if (!tmpMB.equals(secret)) {
                         response.setStatus(403);
                         error = true;
                     }
@@ -884,7 +928,7 @@ public class AjpProcessor implements Act
         }
 
         // Check if secret was submitted if required
-        if ((requiredSecret != null) && !secret) {
+        if ((secret != null) && !secretPresentInRequest) {
             response.setStatus(403);
             error = true;
         }
Index: apache-tomcat-6.0.53-src/java/org/apache/tomcat/util/compat/JreCompat.java
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/tomcat/util/compat/JreCompat.java.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/tomcat/util/compat/JreCompat.java
@@ -16,6 +16,8 @@
  */
 package org.apache.tomcat.util.compat;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.Locale;
 
 import javax.net.ssl.SSLEngine;
@@ -179,4 +181,29 @@ public class JreCompat {
         // Exception does not exist prior to Java 9
         return false;
     }
+
+    public InetAddress getLoopbackAddress() {
+        // Javadoc for getByName() states that calling with null will return one
+        // of the loopback addresses
+        InetAddress result = null;
+        try {
+            result = InetAddress.getByName(null);
+        } catch (UnknownHostException e) {
+            // This would be unusual but ignore it in this case.
+        }
+        if (result == null) {
+            // Fallback to default IPv4 loopback address.
+            // Not perfect but good enough and if the address is not valid the
+            // bind will fail later with an appropriate error message
+            try {
+                result = InetAddress.getByName("127.0.0.1");
+            } catch (UnknownHostException e) {
+                // Unreachable.
+                // For text representations of IP addresses only the format is
+                // checked.
+            }
+        }
+
+        return result;
+    }
 }
Index: apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpProtocol.java
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpProtocol.java.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpProtocol.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentLi
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
 
 import javax.management.MBeanRegistration;
 import javax.management.MBeanServer;
@@ -35,6 +36,7 @@ import org.apache.coyote.ActionCode;
 import org.apache.coyote.Adapter;
 import org.apache.coyote.RequestGroupInfo;
 import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.compat.JreCompat;
 import org.apache.tomcat.util.modeler.Registry;
 import org.apache.tomcat.util.net.AbstractEndpoint;
 import org.apache.tomcat.util.net.JIoEndpoint;
@@ -68,6 +70,7 @@ public class AjpProtocol extends Abstrac
 
 
     public AjpProtocol() {
+        endpoint.setAddress(JreCompat.getInstance().getLoopbackAddress());
         cHandler = new AjpConnectionHandler(this);
         setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
         setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
@@ -171,6 +174,12 @@ public class AjpProtocol extends Abstrac
 
 
     public void start() throws Exception {
+        if (getSecretRequired()) {
+            String secret = getSecret();
+            if (secret == null || secret.length() == 0) {
+                throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
+            }
+        }
         if (this.domain != null ) {
             try {
                 tpOname = new ObjectName
@@ -279,7 +288,46 @@ public class AjpProtocol extends Abstrac
      * Required secret.
      */
     protected String requiredSecret = null;
-    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+    private String secret = null;
+    /**
+     * Set the secret that must be included with every request.
+     *
+     * @param secret The required secret
+     */
+    public void setSecret(String secret) {
+        this.secret = secret;
+        this.requiredSecret = secret;
+    }
+    protected String getSecret() {
+        return secret;
+    }
+
+    @Deprecated
+    public void setRequiredSecret(String requiredSecret) { setSecret(requiredSecret); }
+
+    @Deprecated
+    protected String getRequiredSecret() {
+        return getSecret();
+    }
+
+    private boolean secretRequired = false;
+    public void setSecretRequired(boolean secretRequired) {
+        this.secretRequired = secretRequired;
+    }
+    public boolean getSecretRequired() {
+        return secretRequired;
+    }
+
+    private Pattern allowedRequestAttributesPatternPattern;
+    public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
+        this.allowedRequestAttributesPatternPattern = Pattern.compile(allowedRequestAttributesPattern);
+    }
+    public String getAllowedRequestAttributesPattern() {
+        return allowedRequestAttributesPatternPattern.pattern();
+    }
+    protected Pattern getAllowedRequestAttributesPatternPattern() {
+        return allowedRequestAttributesPatternPattern;
+    }
 
     /**
      * AJP packet size.
@@ -415,10 +463,11 @@ public class AjpProtocol extends Abstrac
             AjpProcessor processor = new AjpProcessor(proto.packetSize, proto.endpoint);
             processor.setAdapter(proto.adapter);
             processor.setTomcatAuthentication(proto.tomcatAuthentication);
-            processor.setRequiredSecret(proto.requiredSecret);
+            processor.setSecret(proto.getSecret());
             processor.setKeepAliveTimeout(proto.keepAliveTimeout);
             processor.setClientCertProvider(proto.getClientCertProvider());
             processor.setMaxCookieCount(proto.getMaxCookieCount());
+            processor.setAllowedRequestAttributesPatternPattern(proto.getAllowedRequestAttributesPatternPattern());
             register(processor);
             return processor;
         }
Index: apache-tomcat-6.0.53-src/webapps/docs/config/ajp.xml
===================================================================
--- apache-tomcat-6.0.53-src/webapps/docs/config/ajp.xml.orig
+++ apache-tomcat-6.0.53-src/webapps/docs/config/ajp.xml
@@ -267,10 +267,10 @@
     <attribute name="address" required="false">
       <p>For servers with more than one IP address, this attribute
       specifies which address will be used for listening on the specified
-      port.  By default, this port will be used on all IP addresses
-      associated with the server. A value of <code>127.0.0.1</code>
-      indicates that the Connector will only listen on the loopback
-      interface.</p>
+        port.  By default, this port will be used on all IP addresses
+        associated with the server. A value of <code>127.0.0.1</code>
+        indicates that the Connector will only listen on the loopback
+        interface.</p>
     </attribute>
 
     <attribute name="backlog" required="false">
Index: apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/LocalStrings.properties
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/LocalStrings.properties.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/LocalStrings.properties
@@ -24,6 +24,7 @@
 ajpprotocol.endpoint.initerror=Error initializing endpoint
 ajpprotocol.endpoint.starterror=Error starting endpoint
 ajpprotocol.init=Initializing Coyote AJP/1.3 on {0}
+ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
 ajpprotocol.proto.error=Error reading request, ignored
 ajpprotocol.getattribute=Attribute {0}
 ajpprotocol.setattribute=Attribute {0}: {1}
Index: apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpAprProcessor.java
===================================================================
--- apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpAprProcessor.java.orig
+++ apache-tomcat-6.0.53-src/java/org/apache/coyote/ajp/AjpAprProcessor.java
@@ -25,6 +25,11 @@ import java.nio.ByteBuffer;
 import java.security.NoSuchProviderException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletResponse;
 
@@ -268,6 +273,8 @@ public class AjpAprProcessor implements
      */
     protected static final ByteBuffer pongMessageBuffer;
 
+    private static final Set<String> javaxAttributes;
+
 
     /**
      * End message array.
@@ -316,6 +323,13 @@ public class AjpAprProcessor implements
         flushMessageBuffer.put(flushMessage.getBuffer(), 0,
                 flushMessage.getLen());
 
+        // Build the Set of javax attributes
+        Set<String> s = new HashSet<String>();
+        s.add("javax.servlet.request.cipher_suite");
+        s.add("javax.servlet.request.key_size");
+        s.add("javax.servlet.request.ssl_session");
+        s.add("javax.servlet.request.X509Certificate");
+        javaxAttributes= Collections.unmodifiableSet(s);
     }
 
 
@@ -333,8 +347,15 @@ public class AjpAprProcessor implements
     /**
      * Required secret.
      */
+    @Deprecated
     protected String requiredSecret = null;
-    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+    protected String secret = null;
+    public void setSecret(String secret) {
+        this.secret = secret;
+        this.requiredSecret = secret;
+    }
+    @Deprecated
+    public void setRequiredSecret(String requiredSecret) { setSecret(requiredSecret); }
 
 
     /**
@@ -355,7 +376,13 @@ public class AjpAprProcessor implements
     public int getMaxCookieCount() { return maxCookieCount; }
     public void setMaxCookieCount(int maxCookieCount) { this.maxCookieCount = maxCookieCount; }
 
-    
+    private Pattern allowedRequestAttributesPatternPattern;
+    public void setAllowedRequestAttributesPatternPattern(Pattern allowedRequestAttributesPatternPattern) {
+        this.allowedRequestAttributesPatternPattern = allowedRequestAttributesPatternPattern;
+    }
+
+
+
     // --------------------------------------------------------- Public Methods
 
 
@@ -763,7 +790,7 @@ public class AjpAprProcessor implements
         }
 
         // Decode extra attributes
-        boolean secret = false;
+        boolean secretPresentInRequest = false;
         byte attributeCode;
         while ((attributeCode = requestHeaderMessage.getByte())
                 != Constants.SC_A_ARE_DONE) {
@@ -789,8 +816,25 @@ public class AjpAprProcessor implements
                         request.setRemotePort(Integer.parseInt(v));
                     } catch (NumberFormatException nfe) {
                     }
+                } else if (n.equals("JK_LB_ACTIVATION")) {
+                    request.setAttribute(n, v);
+                } else if (javaxAttributes.contains(n)) {
+                    request.setAttribute(n, v);
                 } else {
-                    request.setAttribute(n, v );
+                    // All 'known' attributes will be processed by the previous
+                    // blocks. Any remaining attribute is an 'arbitrary' one.
+                    if (allowedRequestAttributesPatternPattern == null) {
+                        response.setStatus(403);
+                        error = true;
+                    } else {
+                        Matcher m = allowedRequestAttributesPatternPattern.matcher(n);
+                        if (m.matches()) {
+                            request.setAttribute(n, v);
+                        } else {
+                            response.setStatus(403);
+                            error = true;
+                        }
+                    }
                 }
                 break;
 
@@ -861,9 +905,9 @@ public class AjpAprProcessor implements
 
             case Constants.SC_A_SECRET:
                 requestHeaderMessage.getBytes(tmpMB);
-                if (requiredSecret != null) {
-                    secret = true;
-                    if (!tmpMB.equals(requiredSecret)) {
+                if (secret != null) {
+                    secretPresentInRequest = true;
+                    if (!tmpMB.equals(secret)) {
                         response.setStatus(403);
                         error = true;
                     }
@@ -879,7 +923,7 @@ public class AjpAprProcessor implements
         }
 
         // Check if secret was submitted if required
-        if ((requiredSecret != null) && !secret) {
+        if ((secret != null) && !secretPresentInRequest) {
             response.setStatus(403);
             error = true;
         }
