programing tip

HTTPURLConnection이 HTTP에서 HTTPS 로의 리디렉션을 따르지 않음

itbloger 2020. 9. 1. 07:18
반응형

HTTPURLConnection이 HTTP에서 HTTPS 로의 리디렉션을 따르지 않음


Java가 HttpURLConnectionHTTP에서 HTTPS URL 로의 HTTP 리디렉션을 따르지 않는 이유를 이해할 수 없습니다 . 다음 코드를 사용하여 https://httpstat.us/ 페이지를 가져옵니다 .

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStream;

public class Tester {

    public static void main(String argv[]) throws Exception{
        InputStream is = null;

        try {
            String httpUrl = "http://httpstat.us/301";
            URL resourceUrl = new URL(httpUrl);
            HttpURLConnection conn = (HttpURLConnection)resourceUrl.openConnection();
            conn.setConnectTimeout(15000);
            conn.setReadTimeout(15000);
            conn.connect();
            is = conn.getInputStream();
            System.out.println("Original URL: "+httpUrl);
            System.out.println("Connected to: "+conn.getURL());
            System.out.println("HTTP response code received: "+conn.getResponseCode());
            System.out.println("HTTP response message received: "+conn.getResponseMessage());
       } finally {
            if (is != null) is.close();
        }
    }
}

이 프로그램의 출력은 다음과 같습니다.

원래 URL : http://httpstat.us/301
연결 : http://httpstat.us/301
수신 된 HTTP 응답 코드 : 301
수신 된 HTTP 응답 메시지 : 영구적으로 이동 됨

http://httpstat.us/301에 대한 요청 은 다음과 같은 (단축 된) 응답을 반환합니다 (절대적으로 옳은 것 같습니다!).

HTTP/1.1 301 Moved Permanently
Cache-Control: private
Content-Length: 21
Content-Type: text/plain; charset=utf-8
Location: https://httpstat.us

불행히도 Java HttpURLConnection는 리디렉션을 따르지 않습니다!

원래 URL을 HTTPS ( https://httpstat.us/301 ) 로 변경하면 Java 예상대로 리디렉션을 따릅니다!?


리디렉션은 동일한 프로토콜을 사용하는 경우에만 수행됩니다. ( 소스 followRedirect()방법참조하십시오 .)이 검사를 비활성화 할 수있는 방법은 없습니다.

HTTP를 미러링한다는 것을 알고 있지만 HTTP 프로토콜 관점에서 보면 HTTPS는 완전히 다른 알려지지 않은 프로토콜 일뿐입니다. 사용자 승인없이 리디렉션을 따르는 것은 안전하지 않습니다.

예를 들어 응용 프로그램이 클라이언트 인증을 자동으로 수행하도록 설정되어 있다고 가정합니다. 사용자는 HTTP를 사용하기 때문에 익명으로 서핑을 기대합니다. 그러나 그의 클라이언트가 묻지 않고 HTTPS를 따르면 그의 신원이 서버에 공개됩니다.


설계 상 HttpURLConnection 은 HTTP에서 HTTPS로 (또는 그 반대로) 자동으로 리디렉션되지 않습니다. 리디렉션 후에는 심각한 보안 문제가 발생할 수 있습니다. SSL (따라서 HTTPS)은 사용자에게 고유 한 세션을 생성합니다. 이 세션은 여러 요청에 재사용 할 수 있습니다. 따라서 서버는 한 사람이 만든 모든 요청을 추적 할 수 있습니다. 이것은 약한 형태의 정체성이며 악용 될 수 있습니다. 또한 SSL 핸드 셰이크는 클라이언트의 인증서를 요청할 수 있습니다. 서버로 전송되면 클라이언트의 ID가 서버에 제공됩니다.

As erickson points out, suppose the application is set up to perform client authentication automatically. The user expects to be surfing anonymously because he's using HTTP. But if his client follows HTTPS without asking, his identity is revealed to the server.

The programmer has to take extra steps to ensure that credentials, client certificates or SSL session id will not be sent before redirecting from HTTP to HTTPS. The default is to send these. If the redirection hurts the user, do not follow the redirection. This is why automatic redirect is not supported.

With that understood, here's the code which will follow the redirects.

  URL resourceUrl, base, next;
  Map<String, Integer> visited;
  HttpURLConnection conn;
  String location;
  int times;

  ...
  visited = new HashMap<>();

  while (true)
  {
     times = visited.compute(url, (key, count) -> count == null ? 1 : count + 1);

     if (times > 3)
        throw new IOException("Stuck in redirect loop");

     resourceUrl = new URL(url);
     conn        = (HttpURLConnection) resourceUrl.openConnection();

     conn.setConnectTimeout(15000);
     conn.setReadTimeout(15000);
     conn.setInstanceFollowRedirects(false);   // Make the logic below easier to detect redirections
     conn.setRequestProperty("User-Agent", "Mozilla/5.0...");

     switch (conn.getResponseCode())
     {
        case HttpURLConnection.HTTP_MOVED_PERM:
        case HttpURLConnection.HTTP_MOVED_TEMP:
           location = conn.getHeaderField("Location");
           location = URLDecoder.decode(location, "UTF-8");
           base     = new URL(url);               
           next     = new URL(base, location);  // Deal with relative URLs
           url      = next.toExternalForm();
           continue;
     }

     break;
  }

  is = conn.openStream();
  ...

Has something called HttpURLConnection.setFollowRedirects(false) by any chance?

You could always call

conn.setInstanceFollowRedirects(true);

if you want to make sure you don't affect the rest of the behaviour of the app.


As mentioned by some of you above, the setFollowRedirect and setInstanceFollowRedirects only work automatically when the redirected protocol is same . ie from http to http and https to https.

setFolloRedirect is at class level and sets this for all instances of the url connection, whereas setInstanceFollowRedirects is only for a given instance. This way we can have different behavior for different instances.

I found a very good example here http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/


Another option can be to use Apache HttpComponents Client:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

Sample code:

CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("https://media-hearth.cursecdn.com/avatars/330/498/212.png");
CloseableHttpResponse response = httpclient.execute(httpget);
final HttpEntity entity = response.getEntity();
final InputStream is = entity.getContent();

HTTPUrlConnection is not responsible for handling the response of the object. It is performance as expected, it grabs the content of the URL requested. It is up to you the user of the functionality to interpret the response. It is not able to read the intentions of the developer without specification.

참고URL : https://stackoverflow.com/questions/1884230/httpurlconnection-doesnt-follow-redirect-from-http-to-https

반응형