Avoid logging sensitive URL parameters in the Subsonic API

In case of exceptions, Airsonic logs the full URL that triggered it
since 417583cc, including possibly sensitive query parameters such as
the authentication password/tokens passed to the Subsonic API.

This replaces the value set for this parameter in the URL by the
"<hidden>" string.
master
François-Xavier Thomas 6 years ago
parent 1463f75b06
commit 820a4faec2
  1. 2
      airsonic-main/src/main/java/org/airsonic/player/controller/StreamController.java
  2. 4
      airsonic-main/src/main/java/org/airsonic/player/spring/LoggingExceptionResolver.java
  3. 24
      airsonic-main/src/main/java/org/airsonic/player/util/Util.java

@ -257,7 +257,7 @@ public class StreamController {
shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException"); shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException");
shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException"); shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException");
if (shouldCatch) { if (shouldCatch) {
LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getAnonymizedURLForRequest(request), e.getCause().toString());
return; return;
} }

@ -25,12 +25,12 @@ public class LoggingExceptionResolver implements HandlerExceptionResolver, Order
shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException"); shouldCatch |= Util.isInstanceOfClassName(e, "org.apache.catalina.connector.ClientAbortException");
shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException"); shouldCatch |= Util.isInstanceOfClassName(e, "org.eclipse.jetty.io.EofException");
if (shouldCatch) { if (shouldCatch) {
LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getURLForRequest(request), e.getCause().toString()); LOG.info("{}: Client unexpectedly closed connection while loading {} ({})", request.getRemoteAddr(), Util.getAnonymizedURLForRequest(request), e.getCause().toString());
return null; return null;
} }
// Display a full stack trace in all other cases // Display a full stack trace in all other cases
LOG.error("{}: An exception occurred while loading {}", request.getRemoteAddr(), Util.getURLForRequest(request), e); LOG.error("{}: An exception occurred while loading {}", request.getRemoteAddr(), Util.getAnonymizedURLForRequest(request), e);
return null; return null;
} }

@ -23,6 +23,8 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -40,6 +42,7 @@ import java.util.List;
public final class Util { public final class Util {
private static final Logger LOG = LoggerFactory.getLogger(Util.class); private static final Logger LOG = LoggerFactory.getLogger(Util.class);
private static final String URL_SENSITIVE_REPLACEMENT_STRING = "<hidden>";
/** /**
* Disallow external instantiation. * Disallow external instantiation.
@ -133,6 +136,27 @@ public final class Util {
return url; return url;
} }
/**
* Return an URL for the given HTTP request, with anonymized sensitive parameters.
*
* @param request An HTTP request instance
* @return The associated anonymized URL
*/
public static String getAnonymizedURLForRequest(HttpServletRequest request) {
String url = getURLForRequest(request);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
MultiValueMap<String, String> components = builder.build().getQueryParams();
// Subsonic REST API authentication (see RESTRequestParameterProcessingFilter)
if (components.containsKey("p")) builder.replaceQueryParam("p", URL_SENSITIVE_REPLACEMENT_STRING); // Cleartext password
if (components.containsKey("t")) builder.replaceQueryParam("t", URL_SENSITIVE_REPLACEMENT_STRING); // Token
if (components.containsKey("s")) builder.replaceQueryParam("s", URL_SENSITIVE_REPLACEMENT_STRING); // Salt
if (components.containsKey("u")) builder.replaceQueryParam("u", URL_SENSITIVE_REPLACEMENT_STRING); // Username
return builder.build().toUriString();
}
/** /**
* Return true if the given object is an instance of the class name in argument. * Return true if the given object is an instance of the class name in argument.
* If the class doesn't exist, returns false. * If the class doesn't exist, returns false.

Loading…
Cancel
Save