在https连接头中设置user-agent属性

时间:2013-04-05 23:11:24

标签: java https http-headers

我无法为https连接正确设置user-agent属性。根据我收集的内容,可以通过-Dhttp.agent VM选项或URLConnection.setRequestProperty()设置http-header属性。但是,通过VM选项设置user-agent会导致“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()没有效果(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连接的用户代理,代理也拒绝连接。我希望我不会发疯:)。

2 个答案:

答案 0 :(得分:12)

  

我通过使用WireShark检查http通信找到/验证了问题。有什么方法围绕这个

这是不可能的。通过加密协议的偶然观察,SSL套接字上的通信完全变得模糊。使用数据包捕获软件,您将能够查看SSL连接的启动和加密数据包的交换,但这些数据包的内容只能在连接的另一端(服务器)中提取。如果不是这种情况那么整个HTTPS协议就会损坏,因为它的全部意义在于保护来自中间人类型攻击的HTTP通信(在这种情况下) MITM是数据包嗅探器。

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

  

.n .... E ... ........... / .. 5..3..9..2..8 ..   .............. @ ........................ QL。{。B .... OSR ..!4。$:T ...,..牛逼.... Q ... M..Ql。{... ... LM..L ..... um.M ......秒。 ... N ... P 1 0} .. I..G4.HK.n ...... 8Y ...............è...一个... > ... 0 ... 0 .........   ).S ....... 0   ..*。H..   ..... 0F1.0 ...ü.... US1.0 ...ü。   。   谷歌Inc1“0 ..U .... Google Internet Authority0 ..   130327132822Z。   131231155850Z0h1.0 ...ü.... 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
  User-Agent: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 = 0.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'附加到用户代理标头,如{ {3}}:

  

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.proxyHosthttps.proxyPorthttp.agent设置一起使用会得到所需的结果:

  

连接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
  接受:text / html,image / gif,image / jpeg,*; q = .2, / ; Q = 0.2
  代理连接:保持活着

但是,不要这样做。使用following documentation

更安全
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);

答案 1 :(得分:0)

  

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

这里没有问题。无论请求是通过HTTP / HTTPS传输,都设置User-Agent标头。甚至将它设置为像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标头是正确的。