12

我无法正确设置user-agenthttps 连接的属性。根据我收集到的信息,http-header 属性可以通过-Dhttp.agentVM 选项或通过URLConnection.setRequestProperty(). 但是,通过 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 捕获。就像我上面提到的,用户代理是通过设置的,-Dhttp.agent因为URLConnection.setRequestProperty()没有效果(用户代理 = 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 连接的用户代理,并且代理也拒绝连接。我希望我不会发疯:)。

4

2 回答 2

13

我通过使用 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....s. ...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...California1.0...U...Mountain View1.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
接受:text/html, image/gif, image/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”附加到用户代理标头,如以下文档所述:

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

    // ...
}

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

连接 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
接受:text/html, image/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);
于 2013-04-16T02:21:36.203 回答
0

我通过使用 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 请求片段可以看出,User-Agent发送的标头是正确的。

于 2013-04-17T16:53:19.830 回答