Задать свойство user-agent в заголовке соединения https

Я не могу правильно установить user-agent свойство для соединения https. Из того, что я собрал, свойства http-заголовка могут быть установлены через -Dhttp.agent опция VM или через URLConnection.setRequestProperty(). Однако установка user-agent через параметр VM приводит к добавлению" Java/[version] " к любому значению http.агент. В то же время setRequestProperty() работает только для http-соединений, а не https (по крайней мере, когда я пробовал).

java.net.URL url = new java.net.URL( "https://www.google.com" );
java.net.URLConnection conn = url.openConnection();
conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0");
conn.connect();
java.io.BufferedReader serverResponse = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream()));
System.out.println(serverResponse.readLine());
serverResponse.close();

Я нашел / проверил проблему, проверив http-связи с помощью WireShark. Есть ли способ обойти это?

Обновление: Дополнительная Информация

кажется, я недостаточно глубоко заглянул в сообщение. Код выполняется из-за прокси-сервера, поэтому наблюдаемая связь против прокси-сервера, установленного через -Dhttps.proxyHost, а не целевой веб-сайт (google.com). Во всяком случае, во время https-соединения метод CONNECT, а не GET. Вот как Wireshark захват попытки связи https. Как я уже упоминал выше, user-agent устанавливается через -Dhttp.agent, потому что URLConnection.setRequestProperty() не имеет эффекта (user-agent = Java/1.7.0). В этом случае обратите внимание на прилагаемое Java / 1.7.0. Вопрос остается тем же самым, почему это происходит и как мне обойти это?

CONNECT www.google.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0 Java/1.7.0
Host: www.google.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Proxy-Connection: keep-alive

HTTP/1.1 403 Forbidden
X-Bst-Request-Id: MWPwwh:m7d:39175
X-Bst-Info: ch=req,t=1366218861,h=14g,p=4037_7213:1_156,f=PEFilter,r=PEBlockCatchAllRule,c=1905,v=7.8.14771.200 1363881886
Content-Type: text/html; charset=utf-8
Pragma: No-cache
Content-Language: en
Cache-Control: No-cache
Content-Length: 2491

кстати, запрос запрещен, потому что прокси фильтрует user-agent,Java / 1.7.0 вызывает отторжение. Я добавил Java / 1.7.0 пользователю-агенту http-соединения и прокси тоже отказывается от соединения. Надеюсь, я не схожу с ума :).

2 ответов


Я нашел / проверил проблему, проверив http-коммуникации с помощью WireShark. Есть ли способ обойти это

это невозможно. Связь через SSL-сокет полностью скрыта от случайного наблюдения протоколом шифрования. Используя программное обеспечение для захвата пакетов, вы сможете просмотреть инициализацию SSL-соединения и обмен зашифрованными пакетами, но содержимое этих пакетов может быть извлечено только на другом конце соединение (сервер). Если бы это было не так, то протокол HTTPS в целом был бы сломанные, поскольку весь смысл в том, чтобы защитить HTTP-связь от атак типа "человек в середине" (где в этом случае MITM является сниффером пакетов).

пример захвата запроса HTTPS (частичный):

.северный....Е... .........../..5..3..9..2..8.. ..............@........................КЖ.{...си....ЛРН..!.4.$.Т...-.-.Т....Вопрос:..М..КЖ.{...ЛЮМЕН..Л...ГМ.М...........s. ...n ... p^0}..I..G4.HK.n......8Y...............E ... A..> ... 0 ... 0......... ).с.......Ноль ..*.Х.. .....0F1.0...у....США1.0...США . Google В Ходе МКП-1"0 ..У....Google Internet Authority0.. 130327132822Z. 131231155850Z0h1.0 ... U....US1.0 ... U... California1.0...У... Горы Представление1.0...У. . Google Inc1.0 ... U....www.Гугл.com0..0

теоретически единственный способ узнать, если ваш User-Agent заголовок фактически исключается, если у вас есть доступ к серверам Google, но на самом деле нет ничего в спецификации HTTPS или реализации Java, которая исключает заголовки, которые обычно были бы отправлены по HTTP.

пример захвата HTTP-запроса:

GET/HTTP / 1.1
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox / 19.0
Хозяин: www.google.com
Примите: текст / html, изображение / gif, изображение / jpeg,*; q=.2, /; q=.2
Соединение: keep-alive

оба примера захвата были сгенерированы с помощью точно тот же код:

URL url = new URL(target);
URLConnection conn = url.openConnection();
conn.setRequestProperty("User-Agent",
        "Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0");
conn.connect();
BufferedReader serverResponse = new BufferedReader(
        new InputStreamReader(conn.getInputStream()));
System.out.println(serverResponse.readLine());
serverResponse.close();

за исключением того, что для HTTPS целью было"https://www.google.com", а для HTTP это было"http://www.google.com".


изменить 1:

на основе вашего обновленного вопроса, используя -Dhttp.agent свойства действительно добавьте "Java / version" в заголовок агента пользователя, как описано в следующая документация:

протоколу HTTP.агент!--16--> (по умолчанию: "Java/")
Определяет строку, отправленную в заголовке запроса User-Agent в http-запросах. Обратите внимание, что строка "Java/" будет добавлена к строке, указанной в собственность (например, если -Dhttp.agent=используется" foobar", заголовок User-Agent будет содержать" Foobar Java/1.5.0", если версия виртуальной машины 1.5.0). Это свойство проверяется только один раз при запуске.

"оскорбительный" код находится в статическом инициализаторе блока sun.net.www.protocol.http.HttpURLConnection:

static {
    // ...
    String agent = java.security.AccessController
            .doPrivileged(new sun.security.action.GetPropertyAction(
                    "http.agent"));
    if (agent == null) {
        agent = "Java/" + version;
    } else {
        agent = agent + " Java/" + version;
    }
    userAgent = agent;

    // ...
}

непристойным способом обойти эту "проблему" является этот фрагмент кода, который я 1000% рекомендую вам не использование:

protected void forceAgentHeader(final String header) throws Exception {
    final Class<?> clazz = Class
            .forName("sun.net.www.protocol.http.HttpURLConnection");

    final Field field = clazz.getField("userAgent");
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, header);
}

использование этого переопределения с https.proxyHost, https.proxyPort и http.agent set дает желаемый результат:

подключение www.google.com:443 HTTP / 1.1
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox / 19.0
Ведущий: www.google.com
Примите: текст / html, изображение / gif, изображение / jpeg,*; q=.2, /; q=.2
Прокси-соединение: keep-alive

Но да, не делай этого. Гораздо безопаснее просто использовать Apache HttpComponents:

final DefaultHttpClient client = new DefaultHttpClient();
HttpHost proxy = new HttpHost("127.0.0.1", 8888, "http");
HttpHost target = new HttpHost("www.google.com", 443, "https");
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
HttpProtocolParams
        .setUserAgent(client.getParams(),
                "Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0");
final HttpGet get = new HttpGet("/");

HttpResponse response = client.execute(target, get);

Я нашел / проверил проблему, проверив http-коммуникации с помощью WireShark. Есть ли способ обойти это?

здесь нет никаких проблем. Заголовок User-Agent задает, будет ли запрос транспортироваться через HTTP / HTTPS. Даже установить его на что-то неразумное, как blah blah работает по HTTPS. Заголовки, показанные ниже, были захвачены, когда используемый базовый протокол был HTTPS.

заголовки запросов, отправленные через Протоколу HTTPS

User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

User-Agent: blah blah
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

вот код, который запускает запрос.

        // localhost:52999 is a reverse proxy to xxx:443
        java.net.URL url = new java.net.URL( "https://localhost:52999/" );
        java.net.URLConnection conn = url.openConnection();
        conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0");
        conn.connect();
        java.io.BufferedReader serverResponse = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream()));
        System.out.println(serverResponse.readLine());
        serverResponse.close();

обычно HTTPS-запросы не могут быть обнюханы (например, @Perception). Передача запроса через прокси-сервер, который заменяет корневой ЦС собственным поддельным ЦС, позволит вам видеть трафик. Более простой способ-просто посмотреть журнал доступа целевого сервера. Но, как вы можете видеть из фрагмента запроса HTTPS выше,User-Agent заголовок, который направляется правильный.