Hello Elli, I am afraid there may be a flaw in the algorythm looking for the first IP of the coma delimited x-forwarded-for header without ensuring that this first IP has been set by a trusted proxy and not by the requester ( getFirstIP(xforwardedForHeaderValue) ). Such spoofing can easily be achieved with tools like Firefox add-ons Modify Headers (1) and X-Forwarded-For Spoofer (2) .
The forthcoming version of Apache Httpd will offer a secure mechanism to handle X-Forwarded-For with a module called mod_remoteip (3). It relies on the concept of trusted proxies which IP address can be 'swallowed'. The first IP of the list that is not a trusted proxy is seen as the real remote ip. mod_remoteip would not have been tricked by such x-forwarded-for header spoofing. Here are two java ports of mod_remoteip to handle X-Forwarded-For at the Tomcat level with a valve and at the WAR level with a servlet filter : RemoteIpValve (4) and XForwardedFilter (5). In addition to handle X-Forwarded-For, they also integrate X-Forwarded-Proto (ssl). These java ports integrate the same trusted proxies concept to prevent spoofing. Cyrille -- Cyrille Le Clerc clecl...@xebia.fr cyri...@cyrilleleclerc.com http://blog.xebia.fr (1) https://addons.mozilla.org/en-US/firefox/addon/967 (2) https://addons.mozilla.org/en-US/firefox/addon/5948 (3) http://httpd.apache.org/docs/trunk/mod/mod_remoteip.html (4) http://code.google.com/p/xebia-france/wiki/RemoteIpValve (5) http://code.google.com/p/xebia-france/wiki/XForwardedFilter On Mon, Oct 5, 2009 at 11:19 PM, Elli Albek <e...@sustainlane.com> wrote: > > Hi, > > We can add the header to the custom valves, but then in addition we have to > change a few log file configurations, create a servlet filter and maybe > something else I cant think of now. Basically doing the same thing a few > times and keeping track of all the places that depend on the header. Ideally > this would all be corrected once in the beginning of the request processing > pipeline, so log file configuration, other valves and the war files will > remain unchanged. > > > > Attached a Valve that does that. This is the minimum code necessary, so it > should not have any significant performance impact. > > Feel free to use as is, not guaranteed to work, no expressed on implied > warranties, not FDIC insured and may loose value. > > > > To configure Tomcat add to server.xml: > > > > <Service name="Catalina"> > > <Connector port="8080" .../> > > <Engine defaultHost="localhost" name="Catalina"> > > <!-- This should precede all other configuration in the engine > --> > > <Valve className="org.apache.catalina.connector.RemoteIPValve"/> > > > > Java class/jar should be placed in /server/lib or /server/classes > > > > E > > > > > > > > package org.apache.catalina.connector; > > > > import java.io.IOException; > > import java.util.regex.Matcher; > > import java.util.regex.Pattern; > > > > import javax.servlet.ServletException; > > > > import org.apache.catalina.connector.Request; > > import org.apache.catalina.connector.Response; > > import org.apache.catalina.valves.ValveBase; > > > > /** > > * A valve that extracts the remote IP of the client from an HTTP header > field > > * passed by the proxy, and set it in the request as the original client IP. > > * This valve should be the first valve in the engine, so log valves (and > > * others) will see the real client IP without requiring the same code > again. > > * > > * @author Elli Albek, www.sustainlane.com > > */ > > public class RemoteIPValve extends ValveBase { > > > > private static final Pattern ipExpr = > Pattern.compile("^[\\da-fA-F]+(\\.[\\da-fA-F]+)+"); > > > > private String forwardedForHeader = "X-Forwarded-For"; > > > > public void invoke(Request request, Response response) throws > IOException, ServletException { > > > > String header = request.getHeader(forwardedForHeader); > > String forwardedIP = getFirstIP(header); > > if (forwardedIP != null) > > request.remoteAddr = forwardedIP; > > > > next.invoke(request, response); > > } > > > > /** > > * Return the first IP address in a string that may contain an IP list > > */ > > static final String getFirstIP(String header) { > > if (header == null) > > return null; > > Matcher m = ipExpr.matcher(header); > > if (m.find()) { > > return m.group(); > > } > > return null; > > } > > > > public void setForwardedForHeader(String forwardedForHeader) { > > this.forwardedForHeader = forwardedForHeader; > > } > > > > public String getInfo() { > > return "RemoteIPValve"; > > } > > } > --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org