sourcecode

HTTP에서HTTPS로의 리다이렉트를 따르지 않음

copyscript 2023. 1. 15. 17:10
반응형

HTTP에서HTTPS로의 리다이렉트를 따르지 않음

왜 자바가HttpURLConnection는 HTTP에서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/301HTTP 응답 코드 수신: 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

HttpURLConnection는 리다이렉트를 따르지 않습니다.

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

리디렉션은 동일한 프로토콜을 사용하는 경우에만 수행됩니다.(소스내의 방법을 참조해 주세요).이 체크를 무효로 하는 방법은 없습니다.

HTTP를 반영하고 있는 것은 알고 있습니다만, HTTP 프로토콜의 관점에서 HTTPS는 전혀 다른 미지의 프로토콜에 불과합니다.사용자 승인 없이 리다이렉트를 따르는 것은 안전하지 않습니다.

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

설계상 HttpURL Connection은 HTTP에서HTTPS로 자동 리다이렉트 되지 않습니다(또는 그 반대).리다이렉트를 실행하면 심각한 보안 문제가 발생할 수 있습니다.SSL(따라서 HTTPS)은 사용자에게 고유한 세션을 만듭니다.이 세션은 여러 요청에 재사용할 수 있습니다.따라서 서버는 한 명의 사용자로부터 이루어진 모든 요청을 추적할 수 있습니다.이것은 정체성의 약한 형태이며 악용될 수 있습니다.또, SSL 핸드쉐이크는 클라이언트의 증명서를 요구할 수도 있습니다.서버에 송신되면, 클라이언트의 ID가 서버에 주어집니다.

Erickson이 지적한 바와 같이 어플리케이션이 클라이언트 인증을 자동으로 실행하도록 설정되어 있다고 가정합니다.사용자는 HTTP를 사용하고 있기 때문에 익명으로 서핑을 할 수 있습니다.그러나 의뢰인이 묻지도 않고 HTTPS를 팔로우하면 그의 신원이 서버에 공개됩니다.

프로그래머는 HTTP에서HTTPS로 리다이렉트하기 전에 credential, 클라이언트 증명서 또는 SSL 세션 ID가 전송되지 않도록 하기 위해 추가 절차를 수행해야 합니다.디폴트로는, 이것들을 송신합니다.리다이렉션이 사용자에게 해를 끼칠 경우 리다이렉션을 따르지 마십시오.이것이 자동 리다이렉트가 지원되지 않는 이유입니다.

이해하셨으니, 리다이렉트 뒤에 오는 코드를 알려드리겠습니다.

  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();
  ...

라고 하는 것이 있다.HttpURLConnection.setFollowRedirects(false)혹시?

언제든지 전화할 수 있어

conn.setInstanceFollowRedirects(true);

앱의 나머지 동작에 영향을 주지 않도록 하기 위해서입니다.

위에서 설명한 바와 같이 setFollowRedirect와 setInstanceFollowRedirects는 리다이렉트된 프로토콜이 동일한 경우에만 자동으로 작동합니다. 즉, http에서https로, https에서https로.

setFolloRedirect는 클래스레벨이며 URL 접속의 모든 인스턴스에 대해 이 값을 설정합니다.단, setInstanceFollowRedirects는 특정 인스턴스에 대해서만 설정됩니다.이렇게 하면 인스턴스마다 다른 동작을 할 수 있습니다.

http://www.mkyong.com/java/java-httpurlconnection-follow-redirect-example/ 에서 좋은 예를 찾았습니다.

Apache Http Components Client를 사용할 수도 있습니다.

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

샘플 코드:

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

HTTPUrlConnection은 객체의 응답을 처리하지 않습니다.역시 퍼포먼스입니다.요청된 URL의 내용을 가져옵니다.응답을 해석하는 것은 기능 사용자에게 달려 있습니다.사양 없이는 개발자의 의도를 읽을 수 없습니다.

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

반응형