Skip to content

Commit 6b2fbb7

Browse files
committed
Strip credentials on cross-domain redirects and HTTPS-to-HTTP downgrades
Authorization and Proxy-Authorization headers, along with Realm credentials, were forwarded to arbitrary redirect targets regardless of origin.
1 parent 4722f1e commit 6b2fbb7

File tree

2 files changed

+447
-8
lines changed

2 files changed

+447
-8
lines changed

client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,33 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
104104
(statusCode == MOVED_PERMANENTLY_301 || statusCode == SEE_OTHER_303 || statusCode == FOUND_302 && !config.isStrict302Handling());
105105
boolean keepBody = statusCode == TEMPORARY_REDIRECT_307 || statusCode == PERMANENT_REDIRECT_308 || statusCode == FOUND_302 && config.isStrict302Handling();
106106

107+
HttpHeaders responseHeaders = response.headers();
108+
String location = responseHeaders.get(LOCATION);
109+
Uri newUri = Uri.create(future.getUri(), location);
110+
LOGGER.debug("Redirecting to {}", newUri);
111+
112+
boolean sameBase = request.getUri().isSameBase(newUri);
113+
boolean schemeDowngrade = request.getUri().isSecured() && !newUri.isSecured();
114+
boolean stripAuth = !sameBase || schemeDowngrade || stripAuthorizationOnRedirect;
115+
116+
if (stripAuth && (request.getRealm() != null || request.getHeaders().contains(AUTHORIZATION))) {
117+
LOGGER.debug("Stripping credentials on redirect to {}", newUri);
118+
}
119+
107120
final RequestBuilder requestBuilder = new RequestBuilder(switchToGet ? GET : originalMethod)
108121
.setChannelPoolPartitioning(request.getChannelPoolPartitioning())
109122
.setFollowRedirect(true)
110123
.setLocalAddress(request.getLocalAddress())
111124
.setNameResolver(request.getNameResolver())
112125
.setProxyServer(request.getProxyServer())
113-
.setRealm(request.getRealm())
126+
.setRealm(stripAuth ? null : request.getRealm())
114127
.setRequestTimeout(request.getRequestTimeout());
115128

129+
if (stripAuth) {
130+
future.setRealm(null);
131+
future.setProxyRealm(null);
132+
}
133+
116134
if (keepBody) {
117135
requestBuilder.setCharset(request.getCharset());
118136
if (isNonEmpty(request.getFormParams())) {
@@ -130,18 +148,13 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
130148
}
131149
}
132150

133-
requestBuilder.setHeaders(propagatedHeaders(request, realm, keepBody, stripAuthorizationOnRedirect));
151+
requestBuilder.setHeaders(propagatedHeaders(request, realm, keepBody, stripAuth));
134152

135153
// in case of a redirect from HTTP to HTTPS, future
136154
// attributes might change
137155
final boolean initialConnectionKeepAlive = future.isKeepAlive();
138156
final Object initialPartitionKey = future.getPartitionKey();
139157

140-
HttpHeaders responseHeaders = response.headers();
141-
String location = responseHeaders.get(LOCATION);
142-
Uri newUri = Uri.create(future.getUri(), location);
143-
LOGGER.debug("Redirecting to {}", newUri);
144-
145158
CookieStore cookieStore = config.getCookieStore();
146159
if (cookieStore != null) {
147160
// Update request's cookies assuming that cookie store is already updated by Interceptors
@@ -150,7 +163,6 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
150163
}
151164
}
152165

153-
boolean sameBase = request.getUri().isSameBase(newUri);
154166
if (sameBase) {
155167
// we can only assume the virtual host is still valid if the baseUrl is the same
156168
requestBuilder.setVirtualHost(request.getVirtualHost());

0 commit comments

Comments
 (0)