前端学HTTP之安全HTTP

  HTTP的主要不足包括通信使用明文(不加密),内容可能会被窃听;不验证通信方的身份,有可能遭遇伪装;无法证明报文的完整性,有可能被篡改

  基本认证摘要认证能够使得用户识别后较安全的访问服务器,但在实际环境中,它们并不足以保护那些重要的事务处理。这时,就需要一种更复杂、更安全的技术,通过数字密码来保护HTTP事务免受窃听和篡改的侵害。本文将详细介绍安全HTTP

 

HTTPS介绍

  人们会用Web事务来处理一些很重要的事情。如果没有强有力的安全保证,人们就无法安心地进行网络购物或使用银行业务。如果无法严格限制访问权限,公司就不能将重要的文档放在Web服务器上。Web需要一种安全的HTTP形式

  前面的博客讨论了一些提供认证(基本认证和摘要认证)和报文完整性检査(摘要qop="auth-int")的轻量级方法。对很多网络事务来说,这些方法都是很好用的,但对大规模的购物、银行事务,或者对访问机密数据来说,并不足够强大。这些更为重要的事务需要将HTTP和数字加密技术结合起来使用,才能确保安全

  HTTP的安全版本要高效、可移植且易于管理,不但能够适应不断变化的情况而且还应该能满足社会和政府的各项要求。我们需要一种能够提供下列功能的HTTP安全技术:服务器认证(客户端知道它们是在与真正的而不是伪造的服务器通话);客户端认证(服务器知道它们是在与真正的而不是伪造的客户端通话);完整性(客户端和服务器的数据不会被修改);加密(客户端和服务器的对话是私密的,无需担心被窃听);效率(一个运行的足够快的算法,以便低端的客户端和服务器使用);普适性(基本上所有的客户端和服务器都支持这些协议);管理的可扩展性(在任何地方的任何人都可以立即进行安全通信);适应性(能够支持当前最知名的安全方法);在社会上的可行性(满足社会的政治文化需要)

  HTTPS是最流行的HTTP安全形式。它是由网景公司首创的,所有主要的浏览器和服务器都支持此协议。HTTPS方案的URL以https://,而不是http://开头,据此就可以分辨某个Web页面是通过HTTPS而不是HTTP访问的

  使用HTTPS时,所有的HTTP请求和响应数据在发送到网络之前,都要进行加密。HTTPS在HTTP下面提供了一个传输级的密码安全层—可以使用SSL,也可以使用其后继者——传输层安全(Transport Layer Security,TLS)。由于SSL和TLS非常类似,所以一般地,不太严格地用术语SSL来表示SSL和TLS

https1

  大部分困难的编码及解码工作都是在SSL库中完成的,所以Web客户端和服务器在使用安全HTTP时无需过多地修改其协议处理逻辑。在大多数情况下,只需要用SSL的输入/输出调用取代TCP的调用,再增加其他几个调用来配置和管理安全信息就行了

 

数字加密

  在详细探讨HTTPS之前,先介绍一些SSL和HTTPS用到的加密编码技术的背景知识。主要包括密码——对文本进行编码,使偷窥者无法识别的算法;密钥——改变密码行为的数字化参数;对称密钥加密系统——编/解码使用相同密钥的算法;不对称密钥加密系统——编/解码使用不间密钥的算法;公开密钥加密系统——一种能够使数百万计算机便捷地发送机密报文的系统;数字签名——用来验证报文未被伪造或篡改的校验和;数字证书——由一个可信的组织验证和签发的识别信息

  密码学是对报文进行编/解码的机制与技巧。人们用加密的方式来发送秘密信息已经有数千年了。但密码学所能做的还不仅仅是加密报文以防止好事者的读取,我们还可以用它来防止对报文的篡改,甚至还可以用密码学来证明某条报文或某个事务确实出自你手,就像支票的手写签名或信封上的压纹封蜡一样

【密码】

  密码学基于一种名为密码(cipher)的秘密代码。密码是一套编码方案——一种特殊的报文编码方式和一种稍后使用的相应解码方式的结合体。加密之前的原始报文通常被称为明文(plaintext或cleartext)。使用了密码之后的编码报文通常被称作密文(ciphertext)

https2

  用密码来生成保密信息已经有数千年了。传说尤利乌斯•凯撒(Julius Caesar)曾使用过一种三字符旋转密码,报文中的每个字符都由字母表中三个位置之后的字符来取代。在现代的字母表中,“A”就应该由“D”来取代,“B”就应该由“E”来取代,以此类推

https3

  用rot3(旋转3字符)密码将报文“meet me at the pier at midnight”编码为密文“phhwphdwwkhslhudwplgqljkw”。通过解码,在字母表中旋转移位3个字符,可以将密文解密回原来的明文报文

  最初,人们需要自己进行编码和解码,所以起初密码是相当简单的算法。因为密码很简单,所以人们通过纸笔和密码书就可以进行编解码了,但聪明人也可以相当容易地“破解”这些密码

  随着技术的进步,人们开始制造一些机器,这些机器可以用复杂得多的密码来快速、精确地对报文进行编解码。这些密码机不仅能做一些简单的旋转,它们还可以替换字符、改变字符顺序,将报文切片切块,使代码的破解更加困难

  编码算法和编码机都可能会落入坏人的手中,所以大部分机器上都有一些号盘,可以将其设置为大量不同的值以改变密码的工作方式。即使机器被盗,没有正确的号盘设置(密钥值),解码器也无法工作

  这些密码参数被称为密钥(key)。要在密码机中输入正确的密钥,解密过程才能正确进行。密码密钥会让一个密码机看起来好像是多个虚拟密码机一样,每个密码机都有不同的密钥值,因此其行为都会有所不同

  下图显示了使用密钥的密码实例。加密算法就是普通的“循环移位N字符”密码。N的值由密钥控制。将同一条输入报文“meet me at the pier at midnight”通过同一台编码机进行传输,会随密钥值的不同产生不同的输出。现在,基本上所有的加密算法都会使用密钥

https4

【数字密码】

  随着数字计算的出现,出现了以下两个主要的进展:从机械设备的速度和功能限制中解放出来,使复杂的编/解码算法成为可能;支持超大密钥成为可能,这样就可以从一个加密算法中产生出数万亿的虚拟加密算法,由不同的密钥值来区分不同的算法。密钥越长,编码组合就越多,通过随机猜测密钥来破解代码就越困难

  与金属钥匙或机械设备中的号盘设置相比,数字密钥只是一些数字。这些数字密钥值是编/解码算法的输入。编码算法就是一些函数,这些函数会读取一块数据,并根据算法和密钥值对其进行编/解码

https5

  给定一段明文报文P、一个编码函数E和一个数字编码密钥e,就可以生成一段经过编码的密文C。通过解码函数D和解码密钥d,可以将密文C解码为原始的明文P。当然,编/解码函数都是互为反函数的,对P的编码进行解码就会回到原始报文P上去

 

对称密钥

  很多数字加密算法都被称为对称密钥(symmetric-key)加密技术,这是因为它们在编码时使用的密钥值和解码时一样(e=d)。我们就将其统称为密钥k

  在对称密钥加密技术中,发送端和接收端要共享相同的密钥k才能进行通信。发送端用共享的密钥来加密报文,并将得到的密文发送给接收端。接收端收到密文,并对其应用解密函数和相同的共享密钥,恢复出原始的明文

https6

  流行的对称密钥加密算法包括:DES、Triple-DES、RC2和RC4

  保持密钥的机密状态是很重要的。在很多情况下,编/解码算法都是众所周知的,因此密钥就是唯一保密的东西了

  好的加密算法会迫使攻击者试遍每一个可能的密钥,才能破解代码。用暴力去尝试所有的密钥值称为枚举攻击(emmieration attack)。如果只有几种可能的密钥值,居心不良的人通过暴力遍历所有值,就能最终破解代码了。但如果有大量可能的密钥值,他可能就要花费数天、数年,甚至无限长的时间来遍历所有的密钥,去査找能够破解密码的那一个

  可用密钥值的数量取决于密钥中的位数,以及可能的密钥中有多少是有效的。就对称密钥加密技术来说,通常所有的密钥值都是有效的。8位的密钥只有256个可能的密钥值,40位的密钥可以有2的40次个可能的密钥值(大约是一万亿个密钥)

  在传统的对称密钥加密技术中,对小型的、不太重要的事务来说,40位的密钥就足够安全了。但现在的高速工作站就可以将其破解,这些工作站每秒可以进行数十亿次计算

  相比之下,对于对称密钥加密技术,128位的密钥被认为是非常强大的。实际上,长密钥对密码安全有着非常重要的影响,美国政府甚至对使用长密钥的加密软件实施了出口控制,以防止潜在的敌对组织创建出美国国家安全局(National Security Agency, NSA)自己都无法破解的秘密代码

  对称密钥加密技术的缺点之一就是发送者和接收者在互相对话之前,一定要有一个共享的保密密钥

  如果想要与Joe进行保密的对话,可能是在看了公共电视台的家装节目之后,想要订构一些木工工具,那么在安全地订购任何东西之前,要先在你和www.joes-hardware.com之间建立一个私有的保密密钥。你需要一种产生保密密钥并将其记住的方式。你和Joe的五金商店,以及因特网上所有其他人,都要产生并记住数千个密钥

  比如Alice(A)、Bob(B)和Chris(C)都想与Joe的五金商店(J)对话。A、B和C都要建立自己与J之间的保密密钥。A可能需要密钥Ka,B可能需要密钥Kb,C可能需要密钥Kc。每对通信实体都需要自己的私有密钥。如果有N个节点,每个节点都要和其他所有个节点进行安全对话,总共大概会有N*N个保密密钥:这将是一个管理噩梦

 

公开密钥

  公开密钥加密技术没有为每对主机使用单独的加密/解密密钥,而是使用了两个非对称密钥:一个用来对主机报文编码,另一个用来对主机报文解码。编码密钥是众所周知的(这也是公开密钥加密这个名字的由来),但只有主机才知道私有的解密密钥。这样,每个人都能找到某个特定主机的公开密钥,密钥的建立变得更加简单。但解码密钥是保密的,因此只有接收端才能对发送给它的报文进行解码

  [注意]大部分公开密钥査找工作实际上都是通过数字证书来实现

https7

  下图中,节点X可以将其加密密钥ex公之于众。现在,任何想向节点X发送报文的人都可以使用相同的公开密钥了,因为每台主机都分配了一个所有人均可使用的编码密钥,所以公开密钥加密技术避免了对称密钥加密技术中成对密钥数目的N*N扩展问题

https8

  尽管每个人都可以用同一个密钥对发给X的报文进行编码,但除了X,其他人都无法对报文进行解码,因为只有X才有解码的私有密钥dx。将密钥分隔开来可以让所有人都能够对报文进行编码,但只有其所有者才能对报文进行解码。这样,各节点向服务器安全地发送报文就更加容易,因为它们只要査找到服务器的公开密钥就行了

  通过公开密钥加密技术,全球所有的计算机用户就都可以使用安全协议了。制定标准化的公开密钥技术包是非常重要的,因此,大规模的公开密钥架构(Public-Key Infrastructure, PKI)标准创建工作已经开展许多年了

【RSA】

  所有公开密钥非对称加密系统所面临的共同挑战是,要确保即便有人拥有了下面所有的线索,也无法计算出保密的私有密钥:公开密钥(是公有的,所有人都可以获得);一小片拦截下来的密文(可通过对网络的嗅探获取);一条报文及与之相关的密文(对任意一段文本运行加密器就可以得到)

  RSA算法就是一个满足了所有这些条件的流行的公开密钥加密系统,它是在MIT发明的,后来由RSA数据安全公司将其商业化。即使有了公共密钥、任意一段明文、用公共密钥对明文编码之后得到的相关密文、RSA算法自身,甚至RSA实现的源代码,破解代码找到相应的私有密钥的难度仍相当于对一个极大的数进行质因数分解的困难程度,这种计算被认为是所有计算机科学中最难的问题之一。因此,如果发现了一种能够快速地将一个极大的数字分解为质因数的方法,就不仅能够入侵瑞士银行的账户系统,而且还可以获得图灵奖了

  RSA加密技术的细节中包括很多繁琐的数学问题。你不需要拥有数论方面的博士学位,有大量的库可以用来执行RSA算法

  任何人只要知道了其公开密钥,就可以向一台公共服务器发送安全报文,所以非对称的公开密钥加密系统是很好用的。两个节点无须为了进行安全的通信而先交换私有密钥

  但公开密钥加密箅法的计算可能会很慢。实际上它混合使用了对称和非对称策略。比如,比较常见的做法是在两节点间通过便捷的公开密钥加密技术建立起安全通信,然后再用那条安全的通道产生并发送临时的随机对称密钥,通过更快的对称加密技术对其余的数据进行加密

 

数字签名

  到目前为止,我们已经讨论了各种使用对称和非对称密钥加/解密保密报文的密钥加密技术

  除了加/解密报文之外,还可以用加密系统对报文进行签名(sign),以说明是谁编写的报文,同时证明报文未被篡改过。这种技术被称为数字签名(digital signing)

  数字签名是附加在报文上的特殊加密校验码。使用数字签名有以下两个好处:1、签名可以证明是作者编写了这条报文。只有作者才会有最机密的私有密钥,因此,只有作者才能计算出这些校验和。校验和就像来自作者的个人“签名” 一样;2、签名可以防止报文被篡改。如果有恶意攻击者在报文传输过程中对其进行了修改,校验和就不再匹配了。由于校验和只有作者保密的私有密钥才能产生,所以攻击者无法为篡改了的报文伪造出正确的校验码

  数字签名通常是用非对称公开密钥技术产生的。因为只有所有者才知道其私有密钥,所以可以将作者私有密钥当作一种“指纹”使用

  下图说明了节点A是如何向节点B发送一条报文,并对其进行签名的

https9

  首先,节点A将变长报文提取为定长的摘要;然后,节点A对摘要应用了一个“签名”函数,这个函数会将用户的私有密钥作为参数。因为只有用户才知道私有密钥,所以正确的签名函数会说明签名者就是其所有者;一旦计算出签名,节点A就将其附加在报文的末尾,并将报文和签名都发送给B;在接收端,如果节点B需要确定报文确实是节点A写的,而且没有被篡改过,节点B就可以对签名进行检査。节点B接收经私有密钥扰码的签名,并应用了使用公开密钥的反函数。如果拆包后的摘要与节点B自己的摘要版本不匹配,要么就是报文在传输过程中被篡改了,要么就是发送端没有节点A的私有密钥,也就是说它不是节点A

 

数字证书

  数字证书被称为因特网上的“ID卡”,它包含一组信息,所有这些信息都是由一个官方的“证书颁发机构”以数字方式签发的。基本的数字证书中通常包含一些纸质ID中常见的内容,比如:对象的名称(人、服务器、组织等);过期时间;证书发布者(由谁为证书担保);来自证书发布者的数字签名

  数字证书通常还包括对象的公开密钥,以及对象和所用签名算法的描述性信息。任何人都可以创建一个数字证书,但并不是所有人都能够获得受人尊敬的签发权,从而为证书信息担保,并用其私有密钥签发证书。典型的证书结构如下图所示

https10

【X.509V3证书】

  数字证书没有单一的全球标准。就像不是所有印刷版ID卡都在同样的位置包含了同样的信息一样,数字证书也有很多略有不同的形式。现在使用的大多数证书都以一种标准格式——X.509V3,来存储它们的信息。X.509V3证书提供了一种标准的方式,将证书信息规范至一些可解析字段中。不同类型的证书有不同的字段值,但大部分都遵循X.509 v3结构。下表中介绍了X.509证书中的字段信息

https11

https12

  基于X.509证书的签名有好几种,其中包括Web服务器证书、客户端电子邮件证书、软件代码签名证书和证书颁发机构证书

【认证】

  通过HTTPS建立了一个安全Web事务之后,现代的浏览器都会自动获取所连接服务器的数字证书。如果服务器没有证书,安全连接就会失败。服务器证书中包含很多字段,其中包括:Web站点的名称和主机名;Web站点的公开密钥;签名颁发机构的名称;来自签名颁发机构的签名

  浏览器收到证书时会对签名颁发机构进行检査。如果这个机构是个很有权威的公共签名机构,浏览器可能已经知道其公开密钥了, 因为浏览器会预先安装很多签名颁发机构的证书。下图中说明了如何通过其数字签名来验证证书的完整性

https13

  如果对签名颁发机构一无所知,浏览器就无法确定是否应该信任这个签名颁发机构,它通常会向用户显示一个对话框,看看他是否相信这个签名发布者。签名发布者可能是本地的IT部门或软件厂商

 

HTTPS细节

  HTTPS是最常见的HTTP安全版本。它得到了很广泛的应用,所有主要的商业浏览器和服务器上都提供HTTPS。HTTPS将HTTP协议与一组强大的对称、非对称和基于证书的加密技术结合在一起,使得HTTPS不仅很安全,而且很灵活,很容易在处于无序状态的、分散的全球互联网上进行管理

  HTTPS加速了因特网应用程序的成长,已经成为基于Web的电子商务快速成长的主要推动力。在广域网中对分布式Web应用程序的安全管理方面,HTTPS也是非常重要的

  HTTPS就是在安全的传输层上发送的HTTP。HTTPS没有将未加密的HTTP报文发送给TCP,并通过世界范围内的因特网进行传输,而是在将HTTP报文发送给TCP之前,先将其发送给了一个安全层,对其进行加密

https14

  HTTP安全层是通过SSL及其现代替代协议TLS来实现的。我们遵循常见的用法,用术语SSL来表示SSL或者TLS

  安全HTTP是可选的。因此,对Web服务器发起请求时,我们需要有一种方式来告知Web服务器去执行HTTP的安全协议版本,这是在URL的方案中实现的。 通常情况下,非安全HTTP的URL方案前缀为http,如下所示:

http://www.joes-hardware.com/index.html

  在安全HTTPS协议中,URL的方案前缀为https,如下所示:

https://cajun-shop.securesites.com/Merchant2/merchant.mv?Store_Code=AGCGS

  请求一个客户端(比如Web浏览器)对某Web资源执行某事务时,它会去检査 URL的方案:如果URL的方案为http,客户端就会打开一条到服务器端口80(默认情况下)的连接,并向其发送老的HTTP命令;如果URL的方案为https,客户端就会打开一条到服务器端口443(默认情况下)的连接,然后与服务器“握手”,以二进制格式与服务器交换一些SSL安全参数,附上加密的HTTP命令

https15

  SSL是个二进制协议,与HTTP完全不同,其流量是承载在另一个端口上的(SSL通常是由端口443承载的)。如果SSL和HTTP流量都从端口80到达,大部分Web服务器会将二进制SSL流量理解为错误的HTTP并关闭连接。将安全服务进一步整合到HTTP层中去就无需使用多个目的端口了,在实际中这样不会引发严重的问题

  下面来详细介绍下SSL是如何与安全服务器建立连接的

  在未加密HTTP中,客户端会打开一条到Web服务器端口80的TCP连接,发送一条请求报文,接收一条响应报文,关闭连接

  由于SSL安全层的存在,HTTPS中这个过程会略微复杂一些。在HTTPS中,客户端首先打开一条到Web服务器端口443(安全HTTP的默认端口)的连接。一且建立了TCP连接,客户端和服务器就会初始化SSL层,对加密参数进行沟通,并交换密钥。握手完成之后,SSL初始化就完成了,客户端就可以将请求报文发送给安全层了。在将这些报文发送给TCP之前,要先对其进行加密

https16

  在发送已加密的HTTP报文之前,客户端和服务器要进行一次SSL握手,在这个握手过程中,它们要完成以下工作:交换协议版本号;选择一个两端都了解的密码;对两端的身份进行认证;生成临时的会话密钥,以便加密信道

  在通过网络传输任何已加密的HTTP数据之前,SSL已经发送了一组握手数据来建立通信连接了

  下图是SSL握手的简化版本。根据SSL的使用方式,握手过程可能会复杂一些,但总的思想就是这样

https17

【服务器证书】

  SSL支持双向认证,将服务器证书承载回客户端,再将客户端的证书回送给服务器。而现在,浏览时并不经常使用客户端证书,大部分用户甚至都没有自己的客户端证书。服务器可以要求使用客户端证书,但实际中很少出现这种情况

  另一方面,安全HTTPS事务总是要求使用服务器证书的。在一个Web服务器上执行安全事务,比如提交信用卡信息时,你总是希望是在与你所认为的那个组织对话。由知名权威机构签发的服务器证书可以帮助你在发送信用卡或私人信息之前评估你对服务器的信任度

  服务器证书是一个显示了组织的名称、地址、服务器DNS域名以及其他信息的X.509 v3派生证书。你和你所用的客户端软件可以检査证书,以确保所有的信息都是可信的

https18

  SSL自身不要求用户检査Web服务器证书,但大部分现代浏览器都会对证书进行简单的完整性检査,并为用户提供进行进一步彻査的手段。网景公司提出的一种Web服务器证书有效性算法是大部分浏览器有效性验证技术的基础。验证步骤如下所述:

  1、日期检测

  首先,浏览器检査证书的起始日期和结束日期,以确保证书仍然有效。如果证书过期了,或者还未被激活,则证书有效性验证失败,浏览器显示一条错误信息

  2、签名颁发者可信度检测

  每个证书都是由某些证书颁发机构(CA)签发的,它们负责为服务器担保。证书有不同的等级,每种证书都要求不同级别的背景验证。比如,如果申请某个电子商务服务器证书,通常需要提供一个营业的合法证明

  任何人都可以生成证书,但有些CA是非常著名的组织,它们通过非常清晰的流程来验证证书申请人的身份及商业行为的合法性。因此,浏览器会附带一个签名颁发机构的受信列表。如果浏览器收到了某未知(可能是恶意的)颁发机构签发的证书,那它通常会显示一条警告信息。有些证书会携带到受信CA的有效签名路径,浏览器可能会选择接受所有此类证书。换句话说,如果某受信CA为“Sam的签名商店”签发了一个证书,而Sam的签名商店也签发了一个站点证书,浏览器可能会将其作为从有效CA路径导出的证书接受

  3、签名检测

  一旦判定签名授权是可信的,浏览器就要对签名使用签名颁发机构的公开密钥,并将其与校验码进行比较,以査看证书的完整性

  4、站点身份检测

  为防止服务器复制其他人的证书,或拦截其他人的流量,大部分浏览器都会试着去验证证书中的域名与它们所对话的服务器的域名是否匹配。服务器证书中通常都包含一个域名,但有些CA会为一组或一群服务器创建一些包含了服务器名称列表或通配域名的证书。如果主机名与证书中的标识符不匹配,面向用户的客户端要么就去通知用户,要么就以表示证书不正确的差错报文来终止连接

  SSL是个复杂的二进制协议。除非你是密码专家,否则就不应该直接发送原始的SSL流量。幸运的是,借助一些商业或开源的库,编写SSL客户端和服务器并不十分困难

  OpenSSL是SSL和TLS最常见的开源实现。OpenSSL项目由一些志愿者合作开发,目标是开发一个强壮的、具有完备功能的商业级工具集,以实现SSL和TLS协议以及一个全功能的通用加密库

【代理】

  客户端通常会用Web代理服务器代表它们来访问Web服务器。比如,很多公司都会在公司网络和公共因特网的安全边界上放置一个代理。代理是防火墙路由器唯一允许进行HTTP流量交换的设备,它可能会进行病毒检测或其他的内容控制工作

https19

  但只要客户端开始用服务器的公开密钥对发往服务器的数据进行加密,代理就再也不能读取HTTP首部了,从而代理就无法知道应该将请求转向何处了

https20

  为了使HTTPS与代理配合工作,要进行几处修改以告知代理连接到何处。一种常用的技术就是HTTPS SSL隧道协议。使用HTTPS隧道协议,客户端首先要告知代理,它想要连接的安全主机和端口。这是在开始加密之前,以明文形式告知的,所以代理可以理解这条信息

  HTTP通过新的名为CONNECT的扩展方法来发送明文形式的端点信息。CONNECT方法会告诉代理,打开一条到所期望主机和端口号的连接。这项工作完成之后,直接在客户端和服务器之间以隧道形式传输数据。CONNECT方法就是一条单行的文本命令,它提供了由冒号分隔的安全原始服务器的主机名和端口号。host:port后面跟着一个空格和HTTP版本字符串,再后面是CRLF。接下来是零个或多个HTTP请求首部行,后面跟着一个空行。空行之后,如果建立连接的握手过程成功完成,就可以开始传输SSL数据了

CONNECT home.netscape.com:443 HTTP/1.0 
User-agent: Mozilla/1.IN

<raw SSL-encrypted data would follow here...>

  在请求中的空行之后,客户端会等待来自代理的响应。代理会对请求进行评估,确保它是有效的,而且用户有权请求这样一条连接。如果一切正常,代理会建立一条到目标服务器的连接。如果成功,就向客户端发送一条200 Connection Established响应

HTTP/1.0 200 Connection established
Proxy-agent: Netscape-Proxy/1.1

results matching ""

    No results matching ""