【问题标题】:Set user-agent property in https connection header在 https 连接标头中设置用户代理属性
【发布时间】:2013-03-28 12:38:10
【问题描述】:

我无法为 https 连接正确设置 user-agent 属性。根据我收集的信息,可以通过-Dhttp.agent VM 选项或URLConnection.setRequestProperty() 设置http-header 属性。但是,通过 VM 选项设置用户代理会导致将“Java/[version]”附加到 http.agent 的任何值。同时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();

我通过使用 WireShark 检查 http 通信发现/验证了问题。有没有办法解决这个问题?

更新:添加信息

看来我对交流的了解不够深入。代码从代理后面运行,因此观察到的通信是针对代理的,通过-Dhttps.proxyHost 设置,而不是目标网站 (google.com)。无论如何,在https连接期间,方法是CONNECT,而不是GET。这是 https 通信尝试的wireshark 捕获。就像我上面提到的,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

顺便说一句,请求被禁止是因为代理过滤了用户代理,Java/1.7.0 导致了拒绝。我已将 Java/1.7.0 附加到 http 连接的用户代理,并且代理也拒绝连接。我希望我不会发疯:)。

【问题讨论】:

  • 我没有看到您描述的行为。使用您的代码块(并更改 url)我的访问日志显示: [11/Apr/2013:18:35:05 +0000] "GET / HTTP/1.1" 200 17353 "-" "Mozilla/5.0 (Windows NT 5.1 ; rv:19.0) Gecko/20100101 Firefox/19.0" "-"。这个请求也是通过 https,使用 OS X。
  • @JasonNichols 我正在运行带有 Java 1.7.0_17 的 Windows XP
  • 用HttpClient怎么样?
  • @Vitaly 当我遇到这个问题时,我实际上是在尝试使用 netbeans 测试 Web 服务连接。使用 HttpClient 将是最后的手段。
  • 刚刚在 java 7 上尝试过,并且能够设置标题并提取内容。回复 - <!doctype html><html itemscope="itemscope" itemtype= ...

标签: java https http-headers


【解决方案1】:

我通过使用 WireShark 检查 http 通信发现/验证了问题。有没有办法解决这个问题

这是不可能的。加密协议的随意观察完全掩盖了通过 SSL 套接字进行的通信。使用数据包捕获软件,您将能够查看 SSL 连接的启动和加密数据包的交换,但这些数据包的内容只能在连接的另一端(服务器)提取。如果不是这种情况,那么整个 HTTPS 协议将损坏,因为它的重点是保护 HTTP 通信免受中间人类型的攻击(在这种情况下MITM 是数据包嗅探器)。

HTTPS 请求捕获示例(部分):

.n....E... ...........5..3..9..2..8.. ..................@........................Ql.{....b.... OsR..!.4.$.T...-.-.T....Q...M..Ql.{...LM..L...um.M... ......秒。 ...n...p^0}..I..G4.HK.n......8Y....E....A.. >...0...0....... ).s.......0 ..*。H.. .....0F1.0...U....US1.0...U. . Google Inc1"0 ..U..Google Internet Authority0.. 130327132822Z。 131231155850Z0h1.0...U....US1.0...U... 加州1.0...你... 山景1.0...U。 . Google Inc1.0...U....www.google.com0..0

理论上,知道您的 User-Agent 标头是否实际上被排除的唯一方法是您是否有权访问 Google 服务器,但实际上 HTTPS 规范或 Java 的实现中没有任何内容排除标头通常会通过 HTTP 发送。

HTTP 请求捕获示例:

GET / HTTP/1.1
用户代理: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
连接:保持活动

两个示例捕获均使用完全相同相同的代码生成:

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”附加到用户代理标头,如following documentation 所述:

http.agent(默认:“Java/”)
定义在 http 请求中的 User-Agent 请求标头中发送的字符串。请注意,字符串“Java/”将附加到属性中提供的字符串(例如,如果使用 -Dhttp.agent=”foobar”,则 User-Agent 标头将包含“foobar Java/1.5.0”如果 VM 的版本是 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;

    // ...
}

解决这个“问题”的一个淫秽方法是这个 sn-p 代码,我 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.proxyHosthttps.proxyPorthttp.agent 一起使用会得到所需的结果:

连接 www.google.com:443 HTTP/1.1
用户代理: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
代理连接:保持活动

但是,是的,不要那样做。使用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);

【讨论】:

  • 感谢您提供的信息,但我认为我误入歧途了,抱歉。您关于无法看到 SSL 标头的评论使我再次查看该问题。我已经更新了我的问题。
  • @user845279 - 好的,您的问题现在更清楚了,添加了信息。我在回答中添加了一些关于为什么会发生这种情况的信息。
  • 太棒了。你的回答已经足够好了,但我希望你能再帮我做一件事。为什么我不能通过URLConnection.setRequestProperty() 设置值?你能给我建议一个解决方法吗?谢谢
  • @user845279 - setRequestProperty 确实有效,但是 Sun 安全套接字实现显式覆盖 User-Agent 标头作为其代​​码的一部分,无论是在 -Dhttp.agent + Java/ 附加。我仍在跟踪代码,但看起来只有在使用代理时才会这样做。不幸的是,没有直接的解决方法。
  • 感谢所有帮助。我已经向 Oracle 提交了错误报告。 Bug Id: 9001759
【解决方案2】:

我通过使用 WireShark 检查 http 通信发现/验证了问题。有没有办法解决这个问题?

这里没有问题。 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)。通过将根 CA 替换为自己的假 CA 的代理来传递请求,您可以看到流量。更简单的方法是只看目标服务器的访问日志。但是从上面的HTTPS请求sn-p可以看出,发送的User-Agent标头是正确的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 2016-03-20
    • 1970-01-01
    • 2013-05-20
    相关资源
    最近更新 更多