Skip to content

Commit dd0e577

Browse files
Will Bengtsonzoewangg
authored andcommitted
CredentialsEndpointProvider.java: Adds the sending of a User-Agent other than default User-Agent in Java when making requests to metadata service
Similar to AWS Golang SDK and Ruby SDK, send a User-Agent other than the default User-Agent when making requests to the EC2 metadata for credentials. aws/aws-sdk-java#1562 for aws-sdk-java addresses this also. PR #1445 for botocore implements this too. This will enable protection of AWS credentials from Server Side Request Forgery (SSRF) vectors by being able to use a metadata proxy to block requests that do not have the right User-Agent set. User-Agent is not controllable by an attacker via SSRF.
1 parent bd235b1 commit dd0e577

File tree

3 files changed

+29
-10
lines changed

3 files changed

+29
-10
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"type": "feature",
4+
"description": "Add a standard User-Agent when making requests to the metadata service. User-Agent pattern: aws-sdk-java/<version>"
5+
}

core/src/main/java/software/amazon/awssdk/core/internal/CredentialsEndpointProvider.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717

1818
import java.io.IOException;
1919
import java.net.URI;
20-
import java.util.Collections;
20+
import java.util.HashMap;
2121
import java.util.Map;
2222
import software.amazon.awssdk.annotations.SdkInternalApi;
2323
import software.amazon.awssdk.core.retry.internal.CredentialsEndpointRetryPolicy;
24+
import software.amazon.awssdk.core.util.VersionInfo;
2425

2526
/**
2627
* <p>
@@ -56,7 +57,12 @@ default CredentialsEndpointRetryPolicy retryPolicy() {
5657
* Allows passing additional headers to the request
5758
*/
5859
default Map<String, String> headers() {
59-
return Collections.emptyMap();
60+
Map<String, String> requestHeaders = new HashMap<>();
61+
requestHeaders.put("User-Agent", String.format("aws-sdk-java/%s", VersionInfo.SDK_VERSION));
62+
requestHeaders.put("Accept", "*/*");
63+
requestHeaders.put("Connection", "keep-alive");
64+
65+
return requestHeaders;
6066
}
6167

6268
}

core/src/test/java/software/amazon/awssdk/core/internal/HttpCredentialsUtilsTest.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import java.io.IOException;
3030
import java.net.URI;
3131
import java.net.URISyntaxException;
32-
import java.util.Collections;
32+
import java.util.HashMap;
3333
import java.util.Map;
3434
import org.junit.BeforeClass;
3535
import org.junit.ClassRule;
@@ -43,6 +43,7 @@
4343
import software.amazon.awssdk.core.internal.net.ConnectionUtils;
4444
import software.amazon.awssdk.core.retry.internal.CredentialsEndpointRetryParameters;
4545
import software.amazon.awssdk.core.retry.internal.CredentialsEndpointRetryPolicy;
46+
import software.amazon.awssdk.core.util.VersionInfo;
4647
import utils.http.SocketUtils;
4748

4849
@RunWith(MockitoJUnitRunner.class)
@@ -54,7 +55,14 @@ public class HttpCredentialsUtilsTest {
5455
private static final String SUCCESS_BODY = "{\"AccessKeyId\":\"ACCESS_KEY_ID\",\"SecretAccessKey\":\"SECRET_ACCESS_KEY\","
5556
+ "\"Token\":\"TOKEN_TOKEN_TOKEN\",\"Expiration\":\"3000-05-03T04:55:54Z\"}";
5657
private static URI endpoint;
57-
private final Map<String, String> emptyHeaders = Collections.emptyMap();
58+
private static Map<String, String> headers = new HashMap<String, String>()
59+
{
60+
{
61+
put("User-Agent", String.format("aws-sdk-java/%s", VersionInfo.SDK_VERSION));
62+
put("Accept", "*/*");
63+
put("Connection", "keep-alive");
64+
}
65+
};
5866

5967
private static CustomRetryPolicy customRetryPolicy;
6068

@@ -153,13 +161,13 @@ public void readResouceNonJsonErrorBody() throws IOException {
153161
*/
154162
@Test
155163
public void readResouceWithDefaultRetryPolicy_DoesNotRetry_ForIoException() throws IOException {
156-
Mockito.when(mockConnection.connectToEndpoint(endpoint, emptyHeaders)).thenThrow(new IOException());
164+
Mockito.when(mockConnection.connectToEndpoint(endpoint, headers)).thenThrow(new IOException());
157165

158166
try {
159167
new HttpCredentialsUtils(mockConnection).readResource(endpoint);
160168
fail("Expected an IOexception");
161169
} catch (IOException exception) {
162-
Mockito.verify(mockConnection, Mockito.times(1)).connectToEndpoint(endpoint, emptyHeaders);
170+
Mockito.verify(mockConnection, Mockito.times(1)).connectToEndpoint(endpoint, headers);
163171
}
164172
}
165173

@@ -170,13 +178,13 @@ public void readResouceWithDefaultRetryPolicy_DoesNotRetry_ForIoException() thro
170178
*/
171179
@Test
172180
public void readResouceWithCustomRetryPolicy_DoesRetry_ForIoException() throws IOException {
173-
Mockito.when(mockConnection.connectToEndpoint(endpoint, emptyHeaders)).thenThrow(new IOException());
181+
Mockito.when(mockConnection.connectToEndpoint(endpoint, headers)).thenThrow(new IOException());
174182

175183
try {
176184
new HttpCredentialsUtils(mockConnection).readResource(endpointProvider(endpoint, customRetryPolicy));
177185
fail("Expected an IOexception");
178186
} catch (IOException exception) {
179-
Mockito.verify(mockConnection, Mockito.times(CustomRetryPolicy.MAX_RETRIES + 1)).connectToEndpoint(endpoint, emptyHeaders);
187+
Mockito.verify(mockConnection, Mockito.times(CustomRetryPolicy.MAX_RETRIES + 1)).connectToEndpoint(endpoint, headers);
180188
}
181189
}
182190

@@ -188,13 +196,13 @@ public void readResouceWithCustomRetryPolicy_DoesRetry_ForIoException() throws I
188196
@Test
189197
public void readResouceWithCustomRetryPolicy_DoesNotRetry_ForNonIoException() throws IOException {
190198
generateStub(500, "Non Json error body");
191-
Mockito.when(mockConnection.connectToEndpoint(endpoint, emptyHeaders)).thenCallRealMethod();
199+
Mockito.when(mockConnection.connectToEndpoint(endpoint, headers)).thenCallRealMethod();
192200

193201
try {
194202
new HttpCredentialsUtils(mockConnection).readResource(endpointProvider(endpoint, customRetryPolicy));
195203
fail("Expected an SdkServiceException");
196204
} catch (SdkServiceException exception) {
197-
Mockito.verify(mockConnection, Mockito.times(1)).connectToEndpoint(endpoint, emptyHeaders);
205+
Mockito.verify(mockConnection, Mockito.times(1)).connectToEndpoint(endpoint, headers);
198206
}
199207
}
200208

0 commit comments

Comments
 (0)