juanpablo-santos commented on code in PR #517:
URL: https://github.com/apache/jspwiki/pull/517#discussion_r4045922575
##########
jspwiki-util/src/main/java/org/apache/wiki/util/HttpUtil.java:
##########
@@ -46,27 +50,107 @@ public final class HttpUtil {
private static final int ONE = 48;
private static final int NINE = 57;
private static final int DOT = 46;
+
+ /**
+ * Name of the system property holding the comma-separated list of reverse
proxies (exact IPs or IPv4 CIDR
+ * ranges) whose {@code X-Forwarded-For} header may be trusted. Empty or
unset means the header is never trusted.
+ */
+ public static final String PROP_TRUSTED_PROXIES = "jspwiki.trustedProxies";
+
+ /** Characters allowed in an IPv4 / IPv6 address. */
+ private static final Pattern IP_ADDRESS_CHARS = Pattern.compile(
"[0-9A-Fa-f.:]{1,45}" );
+
+ /** Cached parse of {@value #PROP_TRUSTED_PROXIES}; {@code null} until
first used. */
+ private static volatile Set<String> trustedProxies;
/** Private constructor to prevent direct instantiation. */
private HttpUtil() {
}
/**
- * returns the remote address by looking into {@code x-forwarded-for}
header or, if unavailable,
- * into {@link HttpServletRequest#getRemoteAddr()}.
- *
+ * Returns the remote address of the request. By default this is {@link
HttpServletRequest#getRemoteAddr()}:
+ * the client-supplied {@code X-Forwarded-For} header is only consulted
when the immediate peer is listed in
+ * the {@value #PROP_TRUSTED_PROXIES} system property. When it is, the
forwarded entries are walked from the
+ * one appended by the nearest trusted proxy towards the client, and the
first address that does not belong to
+ * a trusted proxy is returned, so a client cannot spoof its address by
pre-populating the header. Values that
+ * do not look like an IP address fall back to {@link
HttpServletRequest#getRemoteAddr()}.
+ *
* @param req http request
* @return remote address associated to the request.
*/
public static String getRemoteAddress( final HttpServletRequest req ) {
- String realIP = StringUtils.isNotEmpty ( req.getHeader(
"X-Forwarded-For" ) ) ? req.getHeader( "X-Forwarded-For" ) :
-
req.getRemoteAddr();
- // can be a comma-separated list of IPs
- if (realIP.contains(","))
- realIP = realIP.substring(realIP.indexOf(","));
-
- return realIP;
-
+ final String remoteAddr = req.getRemoteAddr();
+ if( !isTrustedProxy( remoteAddr ) ) {
+ return remoteAddr;
+ }
+
+ // there may be several X-Forwarded-For headers, each holding a
comma-separated list of addresses
+ final StringBuilder forwarded = new StringBuilder();
+ final Enumeration<String> headers = req.getHeaders( "X-Forwarded-For"
);
+ while( headers != null && headers.hasMoreElements() ) {
+ if( forwarded.length() > 0 ) {
+ forwarded.append( ',' );
+ }
+ forwarded.append( headers.nextElement() );
+ }
+ if( forwarded.length() == 0 ) {
+ return remoteAddr;
+ }
+
+ final String[] hops = forwarded.toString().split( "," );
+ for( int i = hops.length - 1; i >= 0; i-- ) {
+ final String hop = hops[ i ].trim();
+ if( !isTrustedProxy( hop ) ) {
+ return IP_ADDRESS_CHARS.matcher( hop ).matches() ? hop :
remoteAddr;
+ }
+ }
+ return remoteAddr;
+ }
+
+ /**
+ * Returns whether the given address matches an entry of the {@value
#PROP_TRUSTED_PROXIES} system property.
+ *
+ * @param addr address to check.
+ * @return {@code true} if the address is a trusted proxy, {@code false}
otherwise.
+ */
+ static boolean isTrustedProxy( final String addr ) {
Review Comment:
it'd be better if `PROP_TRUSTED_PROXIES` could also be configured through
wiki properties or env vars.. check with comments at JSPWIKI-1294
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]