HTTP系列(五):安全

如果, 你伴随HTTP系列一路成长, 那么现在的你, 已经可以乘船开启(学习)HTTP安全之旅。我保证,你会很享受这个学习过程。🙂

许多公司都是安全漏洞的受害者, 一些知名的公司, 比如Dropbox(支持文件同步、备份、共享云存储的软件)、Linkedin(领英, 全球最大职业社交网站)、MySpace(社交网站)、Adobe、索尼和福布斯等许多公司遭受过恶意攻击。除此之外, 许多用户也被(恶意攻击公司的事件)危及过, 你, 很可能曾是受害者之一。🙂

你可以在Have I Been Pwned查询你是否是受害者之一。

我的电子邮件地址出现在4个不同的存在安全漏洞的网站上。

Web应用安全涉及面广, 因此, 只有一篇文章难以涵盖所有的知识。不过, 本文可作为(Web应用安全的)启蒙。首先, 让我们来了解一下, HTTP协议如何安全地通信。

本文是HTTP系列的第五部分。

文章概要:

文章涵盖了大量的内容, 不管怎样, 我们从头学起吧!

你真的需要HTTPS吗?

你可能认为, 不是所有的网站都需要安全防护的。如果一个网站不提供敏感信息, 也不能以任何形式上传数据, 仅仅是为了在URL栏上可以显示绿色的标志(网站安全的标志), 就去购买证书和降低访问网站的速度, 这看起来像是想要杀猪却用了宰牛刀。

如果有一个属于你的网站, 你就会知道加载速度——越快越好——对网站来说是至关重要的, 因此, 你不会想给你的网站增加负担。

从HTTP迁移到HTTPS是一个痛苦的过程, 何必为了维护原本就不用被保护的网站遭受这样的痛苦呢?你要考虑的第一件事, 就是出钱买网站的安全——因为购买证书对于一般的小公司或个人来说是一笔很大的费用。

注: 听说已经有机构开始颁发免费证书, 详情可以从十大免费SSL证书:网站免费添加HTTPS加密 中了解

现在就让我们一起来看看, (为安全)惹上这个麻烦到底值不值得。

HTTPS给报文加密并防止中间人攻击

在之前的HTTP系列文章中, 我们谈论了不同的HTTP认证机制及其安全缺陷。然而, 不管是basic认证还是digest认证, 都不能阻止中间人攻击。中间人攻击——中间人, 就是恶意介入用户与网站通信的一批人——的目的是拦截双方通信的原始报文(如果有需要, 还可以修改报文), 通过转发拦截的报文, (这个攻击行为)就能不被发现。

原本参与通信的人可能不能察觉到他们的通信正在被监听。

HTTPS通过加密通信(报文)防止中间人攻击, 但是这并不意味着(双方的)通信不会再被监听, 它仅仅阻止了那些查看和拦截(双方通信)报文的人看报文内容罢了。如果中间人想要将报文解密, 那么他必须要有解密的密钥。之后, 你就会知道, 这个密钥是如何起作用的。

让我们继续往下走。

HTTPS——影响搜索排名的因素

不久之前(2014年), Google让HTTPS成为一个影响(搜索算法)排名的因素

这意味着什么呢?

这意味着, 如果你是一个关心Google搜索排名的网络管理员, 你的网站一定要用HTTPS协议。尽管HTTPS没有内容质量、反向链接这些因素影响力大, 但它还是很重要的。

Google通过这项举措鼓励网站管理员尽快(从HTTP)过渡到HTTPS, 从而提高互联网整体的安全性。

(证书)完全免费

要架设HTTPS网站, 就需要一个证书来解决证书认证的问题。从前, 证书费用高昂, 并且年年需要更新。

如今, 感谢Let’s encrypt的小伙伴们, 让我们付得起买证书的钱(只要0元!)。那些证书是真的完全免费的!

Let’s encrypt的证书安装起来很简单, 这离不开其背后大公司的支持和超棒的社区人员(的努力)。看看Let’s encrypt的赞助商和支持(Let’s encrypt)的公司的名单, 其中可能有你认识的公司。

Let’s encrypt只提供域名认证(DV)证书, 不提供组织认证(OV)证书和扩展验证(EV)证书。DV证书只能用90天, 不过更新起来很容易。

和其他(好的)技术相同, Let’s encrypt证书也有不好的一面。正因为现在证书获取起来很容易, 钓鱼网站也开始利用起这些证书了

关于(传输)速度

一提到HTTPS, 许多人就会认为它给速度带来了了额外的开销。简单地用httpvshttps.com来测试一下。

下面是测试结果:

所以这里发生了什么?为什么HTTPS比HTTP快那么多?这怎么可能!

HTTPS是使用HTTP 2.0协议的必要条件。

如果我们查看网络选项卡, 我们可以看到在使用HTTPS时,图像是通过HTTP 2.0协议加载的, 除此之外, 流量看起来也很不一样。

HTTP 2.0是继HTTP/1.1之后成功的HTTP协议。

在HTTP/1.1的基础之上, HTTP 2.0增加了许多优点:

  • 用二进制(传输)取代文本(传输)
  • 可通过单个TCP连接并行发送多个请求
  • 采用HPACK压缩报文首部减少(流量)开销
  • 使用新的应用层协议协商(Application-Layer Protocol Negotiation,简称ALPN)扩展更快地加密通信。
  • 可降低RTT(round trip times, 往返时延), 让网站加载的速度更快。
  • 还有许多其他的优点

不使用HTTPS?浏览器表示拒绝。

如果你现在还不相信——使用HTTPS是必要的——那么你应该知道, 现在一些浏览器已经开始举起的大旗, 反对(传输)那些未经加密内容。在16年9月份的时候, Google发布博文明确地说明了Chrome浏览器会怎么对待那些不安全的网站。

现在, 让我们来看一下Chrome 56的前后变化吧!

下面是最终的效果。

你现在相信了吧!😀

迁移到HTTPS的过程很复杂

(迁移过程复杂)这是过去遗留下来的问题。因为历史悠久的网站之前用HTTP协议上传了大量的资源, 这让迁移至HTTPS对这些网站来说就更加困难了, 不过托管的平台通常都会让这个过程变得简单一点。

大部分托管平台都提供了自动往HTTPS迁移的方法, 点击选项栏的一个按钮就可以轻松迁移。

如果你打算搭建一个基于HTTPS的网站, 你需要先去确认一下, 托管平台是否有提供往HTTPS迁移的方法。或者, 有一个脚本, 可以通过Let’s encrypt和设置服务器配置让你自己解决迁移的问题。

以上, 都是要迁移到HTTPS的原因。有什么理由不去做呢?

文章写到这里, 希望我已经说服你, 用HTTPS是有必要的, 并且这可以让你有动力将网站往迁移HTTPS迁移, 知道HTTPS是如何工作的。

HTTPS的基本概念

HTTPS(Hypertext Transfer Protocol Secure, 超文本传输安全协议)准确地说, 是客户端和服务器在用HTTP进行通信的基础上, 增加使用安全的SSL/TLS的协议。

在之前的HTTP系列文章中, 我们已经了解过HTTP的工作原理了, 不过SSL/TLS却没有提到。SSL和TLS到底是什么?为什么要使用他们?

下面, 就让我们来了解一下。

SSL vc TLS

SSL(Secure Socket Layer, 安全套接层)和TLS(Transport Layer Security, 传输安全协议)可互换使用, 但实际上, 现在提到SSL可能就是在说TLS。

SSL一开始是由网景(Netscape)公司提出的, 不过它的1.0版本早已停用。SSL的2.0版本和3.0版本在1995、1996年相继现世,这两个版本在出现之后, 即便是之后TLS已经开始推行了, 也沿用了很长一段时间(至少对IT行业来说, 时间挺长的)。直到2014年Poodle攻击发生之后才有所改变。当时服务器支持从TLS降级到SSL,这是Poodle攻击如此成功的主要原因。

SSL一开始是由网景(Netscape)公司提出的, 不过它的1.0版本早已停用。SSL的2.0版本和3.0版本在1995、1996年相继现世, 即便是之后TLS已经开始推行了, 也沿用了很长一段时间(至少对IT行业来说, 时间挺长的)。2014年, 当时服务器支持从TLS降级到SSL, 这是Poodle攻击发生之后如此成功的主要原因。

从此之后, 服务器降级到SSL的功能已经完全消失了

如果你用Qualys SSL Labs tool检查你或其他人的网站, 你可能会看到这个:

你可以看到, 根本不支持SSL 2和3, 而TLS 1.3还未启用。

不过, 因为SSL流行了很长一段时间, 它已经成为了大部分人熟悉的术语, 现在被用于许多方面。 因此, 当你听到有人用SSL代替TLS, 这是个历史遗留问题, 别想少了, 他们真正想说的不是SSL。

因为我们要摆脱那种表达方式, 所以从现在开始, 我会用TLS, 毕竟这个会更加准确。

那么, 客户端和服务器到底是怎样建立安全连接的?

TLS握手

客户端和服务器在进行真正的、加密过的通信之前会有一个”TLS握手”的过程。

下面是TLS的工作原理(非常简单, 后面有附加连接)。

在建立完连接之后, 会开始加密通信。

真的TLS握手机制会比这个图更加复杂, 不过, 只是为了实现HTTPS协议, 你不必了解所有的真实的细节。

如果你想确切了解 TLS 握手的工作原理, 可以在RFC 2246中找找看。

TLS握手的过程中, 发送的证书扮演着什么样的角色?又是怎样颁发的呢?让我们来一起看看吧!

证书(Certificate)及证书认证(CAs, Certification Authorities)

证书是通过 HTTPS 进行安全通信的关键部分, 它由受信任的证书颁发机构颁发。

数字证书让网站的用户在使用网站时可以以安全的方式进行通信。

比如, 在浏览我的文章时, 证书是这个样子的:

如果你用的是Chrome浏览器, 你可以按F12打开开发者工具安全栏查看证书。

我想要点明两件事。在上图的第一个红色框框里, 你可以看到证书的真实目的。它是用来确保你访问的是正确的网站的如果有些网站伪造成你真正想进行通信的网站, 你的浏览器一定会提醒你的。

第二, 浏览器左边最上面的那个绿色的”安全”标志, 只能说明你是在和有证书的网站在进行通信, 并不能证明你是在与原本想通信的网站进行通信。因此, 要小心了。如果恶意攻击者有一个合法的域名及证书的话, 那么他们就能窃取你的身份凭证了。🙂

注: 有些钓鱼网站, 通过用与大型网站相似的域名申请域名证书(比如用Web1.com替代原本的Webone.com), 瞒过了浏览器的法眼, 骗取用户的身份凭证或其他重要信息。

扩展验证证书(EV, Extended validation certificates), 换句话说, 就是用来证明网站是由合法实体(域名持有者或公司)持有的证书。不过, 现在EV证书的可用性还存在着争议, 因为可能不适用于互联网上的特殊用户(比如为经过浏览器安全功能训练的用户)。你可以在URL栏左方看到EV证书持有者的名字。例如, 当你访问twitter.com的时候, 你可以看到:

这意味着, 网站用EV证书来证明其背后的持有者(或公司)。

证书链(certificate chain)

任何一个服务器都可以(向客户端)发送数字证书, 并以此声称它们就是你想要访问的那个服务器。所以, 到底是什么让你使用的浏览器信任服务器返回的证书呢?

这里, 就要引入根证书了。通常, 证书都是链接起来的,
(链接最顶层的)根证书是你的电脑全心全意地信任的一个证书。

我博客的证书链是这个样子的:

最底层的是我的域名证书, 这是由它的上一级的证书签署的, 它的上一级证书是上上级证书签署的…这样循环往复直到最顶层的根证书。

不过, 到底是谁签署的根证书呢?

好吧, 告诉你, 是它自己给自己签署的!😀


颁发给: 域根证书; 颁发者: 域根证书。

你的电脑和浏览器会有一个信任的根证书名单, 这些根证书依靠确认你浏览的域名是通过验证的(来保证服务器端证书是值得信任的)。若证书链由于某些原因断裂了——当时我在我博客上开启了CDN[内容分发网络], 就出现了这样的问题——你的电脑会显示你的网站是”不安全”的。

在键盘上按win+R输入” certmgr.msc”后运行证书管理器, 就可以在windows系统上查询信任的根证书的名单。然后, 你可以在受信任的根证书颁发机构文件夹中找到电脑信任的证书。这个名单适用于Windows系统、IE浏览器、Chrome浏览器和Firefox浏览器, 换言之, 他们自己在管理自己(信任的证书)的名单。

通过交换证书, 客户端和服务器能确认与对方进行通信的人是正确的, 并且可以开始加密传输报文了。

HTTPS的不足

HTTPS有时会让人误以为当前站点是绝对安全的,实际上启用HTTPS机制只是提高了网站对某些认证攻击的防御能力,比如中间人攻击。但实际上,如果网站后端代码有问题,还是会存在一些其它漏洞,除了中间人攻击之外,还有很多种窃取用户信息的方式。

而且,如果HTTPS本身的设置有问题的话,依然可能发生中间人攻击。网站管理员可能会误以为,只要使用了HTTPS,就不存在中间人攻击了。

如果网站登录页面的后端代码存在漏洞,即使使用了HTTPS机制,也还是会导致用户账号密码泄露。所以,根本方法,要解决整个网站的安全问题,而不仅仅是企图依赖于HTTPS。

总结

以上就是HTTP系列的全部了希望你可以从中受益, 或者(通过文章)你理解了你曾经不能理解的概念。

在写文章的过程中, 我获得了喜悦和大量的新知识。希望我的文章也能给你带来快乐(或者至少于都过程是享受的)。🙂

HTTP系列(四):HTTP的基础概念概述

在上一篇文章中, 我们讨论了网站用多种方法去标识浏览网站的用户(身份)。

标识本身仅仅表示一个声明, (用户通过声明)表明自己的身份, 然而除此之外, 并没有其他证据可以证明用户的身份。

认证, 用另一种方式来说, 就是用户证明自身身份的证据, 展示个人ID和输入用户密码是两种证明自身身份常见的方法。

通常情况下,只有通过认证, 网站才能为用户提供敏感资源。

HTTP自身拥有认证机制, 该机制允许服务器(向用户)提出质询并获取其所需的认证信息。你将会在文章中了解到什么是认证和认证是如何进行的。文章中还会涵盖每一种认证方式的优缺点, 并且分析它们是否具有独当一面的能力。(偷偷剧透一下, 它们才没这个能力呢!)

这篇文章是HTTP系列的第四部分。

文章概要:

  • HTTP认证的工作方式
  • Basic认证(基本认证)
  • Digest认证(摘要认证)

在进一步探索什么是HTTP认证机制之前, 我们一起来看看什么是HTTP认证吧!

HTTP认证的工作方式

认证是Web服务器标识用户的一种方式。用户需要摆出证据, 证明其有获得所请求资源的权限。通常, 证明的过程需要一组用户名和密码, 并且, 输入的用户名和密码必须经服务器(验证后)认同是有效的, 然后才由服务器判断该用户是否有获取该资源的权限。

HTTP提供了两种认证协议:

  • Basic认证(基本认证)
  • Digest认证(摘要认证)

在深入了解上述两个协议之前, 我们来大概过一遍基础概念。

质询(challenge)/响应(response)认证框架

质询/响应认证是什么意思?

就是说, 在某个客户端发送请求的时候, 服务器不会立即响应, 而是(向客户端)返回认证要求。该认证要求要求用户通过输入私密信息(用户名及密码)来提供标识身份的证明。

之后, 客户端会重复发送请求来提供(身份)凭证, 如果该身份凭证被服务器认证是有效的, 那么用户可以得到所请求的响应资源。如果(身份)凭证被认为无效, 那么服务器会重新发出质询, 或直接发送(认证信息)错误报文。

注:加入用户名密码错误的话, 服务器会返回401

与认证相关的请求/响应首部字段

WWW-Authenticate响应首部字段, 是服务器用来(向客户端)发出质询的, 该首包含了认证协议和认证域这两部分内容。

在用户输入(身份)凭证之后, (客户端)会重新发送请求报文。然后, 客户端会发送附有Authorization首部字段的请求报文, 该首部字段包含了认证方式(Basic)和一组用户密码。

如果(身份)凭证通过验证, 服务器会返回响应, 并在响应中选用Authentication-Info首部字段附上附加信息——该首部字段是非必需的, 所以可有可无。

安全认证域(Security Realms)

认证域(又称防护域, protection space)在服务器上提供了不同访问权限关联不同资源组的方法

实际上, 认证域的存在就意味着, 用户访问(不同的)资源需要输入不同的身份凭证。

服务器拥有多个认证域。

/admin/statistics/financials.txt -> Realm=”Admin Statistics”
比如, 上面的示例就是存放网站统计信息的认证域——只允许网站管理员访问(其中的资源)。

/images/img1.jpg -> Realm = “Images”
还有存放网站图片的认证域——即使不是管理员, 也能访问和上传图片。

当你尝试访问”financials.txt”时, 服务器会向你发出质询, 并返回如下响应:

1
2
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Basic realm="Admin Statistics"

点击https://tools.ietf.org/html/rfc7235#section-2.2可了解更多认证域。

简单的HTTP认证实例

现在, 让我们用简单的HTTP认证实例为这一小节内容画上一个完美的句号吧!(basic认证会在后文继续讲述)

  1. 用户代理 -> 服务器
    用户请求访问服务器上的图片。

    1
    2
    GET /gallery/personal/images/image1.jpg HTTP/1.1
    Host: www.somedomain.com
  2. 服务器 -> 用户代理
    服务器(向客户端)发送认证要求。

    1
    2
    HTTP/1.1 401 Access Denied
    WWW-Authenticate: Basic realm="gallery"
  3. 用户代理 -> 服务器
    用户通过表单输入标识自己。

    1
    2
    GET /gallery/personal/images/image1.jpg HTTP/1.1
    Authorization: Basic Zm9vOmJhcg==
  4. 服务器 -> 用户代理
    服务器验证(身份)凭证成功后, 发送状态码”200 OK”及图片。

    1
    2
    3
    HTTP/1.1 200 OK
    Content-type: image/jpeg
    ...<image data>

看起来不是那么复杂, 对吗?

现在, 我们来进入更深层次的学习, 学习basic认证。

basic认证

basic认证是最常用且支持面最广的认证协议。从HTTP/1.0开始, basic认证就已经广泛应用了, 每一个客户端都可以使用该认证协议认证。

上面的实例描述的是basic认证过程。basic认证实现和用起来都很简单, 不过, 它存在着安全问题。

进入(basic认证)安全问题的学习之前, 不妨先了解一下basic认证是如何处理(认证过程中传输的)用户和密码的。

basic认证将用户名及密码放在一串字符串中, 两者由符号”:”分割。然后, 用base64编码字符串。不过, 无论字符串的表现形式是什么样, 混乱的字符顺序也不能保证(认证)安全, 因为它可以被轻易地解开。

实际上, base64编码不是为了加密, 而是为了保证用户名及密码在通过HTTP协议传输之后具有可移植性。不过,在HTTP首部字段中不能使用通用字符, 才是编码的主要原因。

1
2
GET /gallery/personal/images/image1.jpg HTTP/1.1
Authorization: Basic Zm9vOmJhcg==

例子中的字符串”Zm9vOmJhcg==”解码后只是”foo:bar”(这一小段字符串)。

因此, 每个可以收到请求的(代理/服务器)都能轻易地解码和使用(传输的身份)凭证。

更糟糕的是, 恶意的第三方仍然可以通过发送(编码后的)混乱字符序列达到同样的效果, 因此对用户名及密码进行编码不会有任何帮助。

没有任何可以用来防止代理或其他形式的攻击篡改请求主体(数据), 同时还能保证请求主体完好无损的措施。

因此, 如你所见, basic认证称不上是一个完美的认证机制。

尽管如此, basic认证还是可以用来防止(无权限用户)意外访问受保护的资源, 并(为用户)提供一定程度的个性化内容。

为了让(认证)更加安全实用, 可以用基于SSL的HTTPS协议对basic认证进行扩展, 相关内容我们会在本系列的第五部分详述。

然而, 有些人认为, basic认证的安全与否取决于运输机制(的安全性)

digest(摘要)认证

与简单且不安全的basic认证相比, digest认证是一个更加安全可靠的选择。

那么, 它是如何认证的呢?

digest认证将多个nonceMD5哈希加密算法结合使用, 以此来隐藏密码(使账号)免于遭受多种恶意攻击的侵害。

注: nonce本身是用base64加密的。

这个(认证过程)听起来好像很复杂, 不过在你看完实例之后, 就会觉得清晰多了。

实例

  1. 用户代理 -> 服务器

    1
    2
    GET /dir/index.html HTTP/1.0
    Host: localhost

    客户端发送不含认证(信息)的请求。

  2. 服务器 -> 用户代理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    HTTP/1.0 401 Unauthorized
    WWW-Authenticate: Digest realm="shire@middleearth.com",
    qop="auth,auth-int",
    nonce="cmFuZG9tbHlnZW5lcmF0ZWRub25jZQ",
    opaque="c29tZXJhbmRvbW9wYXF1ZXN0cmluZw"
    Content-Type: text/html
    Content-Length: 153
    <--空行--!>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8" />
    <title>Error</title>
    </head>
    <body>
    <h1>401 Unauthorized.</h1>
    </body>
    </html>

    服务器对客户端发出质询, 要求其用digest认证进行认证, 并向客户端发送(认证时)所需的信息。

  3. 用户代理 -> 服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /dir/index.html HTTP/1.0
    Host: localhost
    Authorization: Digest username="Gandalf",
    realm="shire@middleearth.com",
    nonce="cmFuZG9tbHlnZW5lcmF0ZWRub25jZQ",
    uri="/dir/index.html",
    qop=auth,
    nc=00000001,
    cnonce="0a4f113b",
    response="5a1c3bb349cf6986abf985257d968d86",
    opaque="c29tZXJhbmRvbW9wYXF1ZXN0c

    客户端将计算出响应码与用户名、认证域(real)、URI、nonce(随机数)、opaque、qop、nc和cnonce(客户端的随机数)这些属性一起发送出去, 这信息量真的很大。

  4. 服务器 -> 客户端

    1
    2
    3
    4
    HTTP/1.0 200 OK
    Content-Type: text/html
    Content-Length: 2345
    ... <content data>

    服务器会自己计算(服务器产生的nonce)的哈希值并与从客户端收到的nonce哈希值进行比对。若两者相同, 服务器就会发送(客户端)所请求的数据。

(步骤3的)详细说明

定义:

  • nonce、opaque —— 服务器生成的字符串, 客户端会在返回的内容中附上该信息。
  • qop(保护质量) —— 可设置一个或多个值(“auth” | “auth-in” | token)。这些值影响摘要(信息)的计算。
  • cnonce - 客户端随机数, 若设置了qop的值, 一定要生成客户端随机数。它是用来防止选择明文攻击和保证信息完整性的。
  • nc —— 若qop设置了, 必须(给服务器)发送该属性。服务器可通过nc属性的值, 检测请求是否是重新发送过来的。若(两次请求)nc属性出现了相同值, 那么说明该请求重新发送了一次。

response属性的值的计算方法:

1
2
3
4
5
6
7
8
9
10
11
HA1 = MD5("Gandalf:shire@middleearth.com:Lord Of The Rings")
= 681028410e804a5b60f69e894701d4b4
HA2 = MD5("GET:/dir/index.html")
= 39aff3a2bab6126f332b942af96d3366
Response = MD5( "681028410e804a5b60f69e894701d4b4:
cmFuZG9tbHlnZW5lcmF0ZWRub25jZQ:
00000001:0a4f113b:auth:
39aff3a2bab6126f332b942af96d3366" )
= 5a1c3bb349cf6986abf985257d968d86

如果你对response属性是如何受qop属性影响这件事感兴趣的话, 你可以看看RFC 2617

简短的总结

如你所见, digest认证不管是理解起来还是实现起来都(比basic认证)复杂多了。

尽管, digest认证比basic认证安全得多, 但还是存在遭受中间人攻击的可能。

RFC 2617中提到, digest认证是用于取代basic认证的, 因为它可弥补basic认证的不足, 同时RFC 2617中没有将digest认证仍旧不符合现代密码标准的事实隐藏起来, 不过, 该认证安全与否, 还是取决于实际实施情况。

digest认证的优点:

  • 不会在网络中发送明文密码
  • 防止重放攻击
  • 防止篡改报文

digest认证的缺点:

  • 易遭受中间人攻击
  • 不用设置大量的安全选项, 导致digest认证方法不够安全
  • 存储密码时, 不能使用强密码哈希函数

注: 缺点的最后一点, 是因为无论是密码、用户名、realm、密码的摘要都要求是可恢复的

上述的种种缺点使得digest认证未能大力推广, 反观简单的basic认证, 结合SSL使用之后比(复杂的)digest认证安全得多——因此该认证方法得到大力推广。

总结

本文为HTTP系列的一部分。

文章看到这里, 我们大致过了一遍HTTP默认提供的认证机制, 并且谈论了不同认证机制的优缺点。

希望(对你来说)这些概念不再只是屏幕上的单词, 并且下次你提到这些名词的时候, 能准确地知道它们是什么和怎么运用它们。

你应该察觉到了, 上述认证机制并不能规避(所有)安全风险, 这就是HTTPS、SSL/TLS(Transport Layer Security, 传输层安全性协定)这些概念存在的原因。下一篇文章, 我们将会更多地去讨论安全问题和如何去规避这些问题。

如果你感觉有些概念不是很清晰, 你可以看看HTTP系列第一部分第二部分第三部分

安全小讲堂

在找相关知识的时候, 发现有一个人用了一个很通俗易懂的方式来讲密码学里面的知识, 感觉很有趣。今天来尝试一波。

假设小明和小兰互相不认识, 在老师严抓早恋的背景下, 俩人都学习了写情书的新姿势, 将自己写的文字加密并发出, 然后通过情书培养感情。小草也喜欢小兰, 为了阻(heng)止(dao)他(duo)们(ai), 采取了以下攻击方式。

  1. 选择明文攻击
    小草很了解小兰(但没有学习过上面的新姿势), 知道小兰最爱在信里表示自己很”开心”, 所以信加密后出现最高频率的词, hexo这就是知道”开心”加密之后的密文是啥了。
    ————已知密文。

    如果有一天, 小兰写”开心”的次数变少了, 小草就可以找机会雪(chen)中(xu)送(er)炭(ru)了。

  2. 中间人攻击
    小明和小兰协商好, 在书信里面放入自己的信物表明自己的身份, 为了防止第三者做些乱七八糟的事情, 这个方式也是很明智了。但是未曾想, 小草在一开始协商信物的时候, 就窃取了他们的信物——这个信物就是公钥——并且将自己事先准备好的两个信物与小明和小兰原本的信物进行对调。


    这时候小草甚至还能皮一下, 篡改小明的书信, 离间他们两者之间的感情。

  3. 重放攻击
    小明给小兰送情书之前, 都会在小兰的桌上放一瓶酸奶, 作为自己给小兰写信的信号。小兰回教室看到抽屉里有信, 在思考是谁写的(发出质询), 看到酸奶(收到响应), 小兰就知道是小明给自己写情书了(通过验证)。小草找到了这个规律, 同样给小兰桌上放了一瓶酸奶(重放攻击, 二次利用认证信息), 之后在抽屉里放着离间两人的信, 这就达成了小草想要的效果。

HTTP系列(三):客户端标识

到现在, 你已经了解HTTP的基本概念及其体系结构, 那么, 就可以让进入下一个重要的部分——客户端标识。

你知道为什么客户端识别那么重要吗? 你知道Web服务器是怎样识别你所用的客户端的吗?你知道怎样使用和存储这些身份信息吗?看完这篇文章, 你的这些问题都能迎刃而解。

这篇文章是HTTP系列的第三部分。

文章概要:

首先, 让我们了解一下, 为什么网站要识别客户端?

客户端识别及其重要性

正如你(可以)很清楚地意识到的一样, 每一个网站, 至少是那些关心你(客户端)和你的操作的网站, 都存在以某种形式呈现的内容个性化。

然而, 我想通过网站的这个特性表达什么呢?

所谓的内容个性化, 就是(在网页上添加)建议项, 建议项包括: 商务网站上或社交网站上推荐的视频、像蛔虫一般知晓你的需要的广告、与你有关联的新闻、社交网站上的好友推荐等等。

这个特性看起来像是一把双刃剑。一方面, 这个特性做的非常好的一点是,提供给你个性化定制的内容。而另一方面, 该特性带来的确认偏误会造成刻板印象和偏见。迪尔伯特有一套优秀的漫画涉及到了确认偏误。

注:该漫画的地址http://dilbert.com/strip/2011-07-02

然而, 我们怎么能不知道我们最喜欢的球队昨晚的得分? 怎么能不知道昨晚公众人物们干了些啥?

无论从哪一方面考虑, 个性化的内容都已经成为了我们日常生活中的一部分了, 我们不能,甚至连想不可能想去限制内容个性化。

接下来, 就让我们了解一下Web服务器是如何通过识别你的身份实现内容个性化的。

客户端识别的多种方式

识别客户端具有多种方式:

  • HTTP请求首部
  • IP地址
  • 长URLs
  • Cookies
  • 登陆信息(认证)

下面, 我们将上面的每一种方法大致过一遍吧!HTTP认证会在HTTP系列第四部分进行更加详细的介绍。

用于识别的HTTP请求首部字段

Web服务器可以通过一些方法, 直接从HTTP请求首部字段中提取客户端的相关信息。

这些首部字段是:

  • From - 若客户端有提供, 则含有用户(客户端或代理)的电子邮件地址
  • User-Agent - 含有客户端的信息
  • Referer - 含有用户所请求的原始资源(的URI)
  • Authorization - 含有用户名及密码
  • Client-ip - 含有用户的IP地址
  • X-Forwared-For - 含有用户的IP地址(通过代理服务器时使用)
  • Cookie - 含有服务器端生成的ID

理论上, From首部字段是用户理想的唯一标识, 但实际上, 由于会造成收集电子邮件地址这种安全性问题, 因此该首部字段很少用到。

注: 国外有专门形容收集电子邮件地址的词组-email collection, 词组原意应该是“收集电子邮件数据”, 但是因为From这个首部字段含有的是电子邮件的地址, 因此翻译为”收集电子邮件地址”。

User-agent首部字段含有浏览器版本、操作系统的信息。尽管这些信息对自定义内容来说很重要, 但它还是不能(帮服务器)用更恰当的方式去识别用户。

Referer首部字段告知服务器用户的请求是从哪个Web页面发起的。该信息有助于(服务器)分析用户行为, 但较少识别该首部字段内容。

注: 我大概查了一下为什么作者会说很少识别该首部字段内容。看完《图解HTTP》,我是这样理解的, 当直接在浏览器的地址栏输入URI, 或其他安全性考虑时, 客户端可以不发送该首部字段, 因为URI的查询字符串中可能含有ID和密码等保密信息, 若写进去有可能导致信息泄露。所以服务器就很少识别该首部字段的内容。如果想更深入了解Referer首部字段, 可以看一下https://www.sojson.com/blog/58.html

虽然这些首部字段(给服务器)提供了有用的客户端信息, 不过, 如果想有效地实现内容个性化, 这是远远不够的。

剩余的首部字段(为服务器)提供了更准确的识别机制。

IP地址

过去, IP地址不易被伪造或交换, 因此IP地址可用于识别客户端。但是(检查)IP地址可以被当作一个额外的安全检查, 毕竟只检查IP地址, 还是不够可靠。

下面列出的, 是其不可靠的原因:

  • IP地址描述机器, 而不是用户
  • NAT防火墙 - 许多ISP(Internet service provider, 互联网服务供应商)用NAT防火墙提高安全性和弥补IP地址的不足
  • 动态IP地址 - 用户通常都是从ISP获取动态IP地址
  • HTTP代理和网关 - 代理和网关可用于隐藏真实IP地址。某些代理通过Client-ip请求首部字段或X-Forwarded-For请求首部字段来保留真实IP地址。

长(胖)URL

在用户浏览页面时, 网站会在URL中添加更多的信息, 直至URL看起来很复杂且难以读懂。

下面举浏览亚马逊商店的例子能很好地告诉你, 胖URL长得啥样。

1
https://www.amazon.com/gp/product/1942788002/ref=s9u_psimh_gw_i2?i

在使用胖URL的时候, 有以下缺点:

  • 看起来很丑
  • 不可共享
  • 破坏缓存
  • 会话(session)受限
  • 给服务器增加负载

注: 这里大概解释一下这些缺点。首先, “不可共享”是因为共享的话可能会造成个人隐私泄露等安全问题。”破坏缓存”则是因为, 胖URL实际上是为每个用户生成特定版本的URL, 这就会造成没有可供公共访问的缓存了。之后, “会话受限”, 我们应该知道会话内容会在关闭浏览器之后消失, 除非用户收藏了特定的胖URL, 否则一旦用户退出登录, 所有会话期间的内容都会消失。而”增加负载”则是因为将URL重写会生成负荷。若想更进一步了解胖URL, 可参考:http://gcidea.info/2016/05/11/fat-url/

Cookies

Cookie是现在除了认证(authentication)之外最好的标识客户端方法, 它是由网景公司提出的, 现如今, 每一个浏览器都支持cookie。

Cookie分为两类: 会话Cookie和持久性Cookie。会话Cookie在(用户)关闭浏览器时会被删除, 而持久性cookie则会(比会话Cookie)更长久地保存在硬盘中。若想将会话Cookie作为持久性Cookie来使用, 仅需设置Max-Age首部字段或Expiries属性。

现代浏览器(例如Chrome、Firefox)在用户关闭浏览器后, 可以让后台进程一直运行, 以便用户从(进程)中断处恢复进程。这可能会导致(浏览器)会保留绘画cookie, 因此要小心一点。

那cookie到底是怎么工作的呢?

Cookie含有一个键值对列表, 该列表是服务器通过Set-Cookie或Set-Cookie2两种响应首部字段设置的。通常, Cookie存储的信息会是某种客户ID, 但是有些网站也会(在其中)存储其他信息。

浏览器将Cookie信息储存在Cookie数据库中, 并在用户下次访问该页面或网站时返回Cookie。浏览器可以处理成千上万不同的cookie, 并且知道每一个cookie什么时候提供(给用户)。

下面为(使用Cookie的)流程的示例。

  1. 用户代理(User Agent) -> 服务器

    1
    2
    POST /acme/login HTTP/1.1
    [form data]

    用户(代理)通过表单输入的内容识别用户。

  2. 服务器 -> 用户代理

    1
    2
    HTTP/1.1 200 OK
    Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"

    服务器向用户代理(浏览器)发送Set-Cookie2响应首部字段, 指示用户代理在cookie中写入用户相关信息。

  3. 用户代理 -> 服务器

    1
    2
    3
    POST /acme/pickitem HTTP/1.1
    Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
    [form data]

    用户将选择的物品添加到购物车中。

  4. 服务器 -> 用户代理

    1
    2
    HTTP/1.1 200 OK
    Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"

    购物车中添加了所选出物品。

  5. 用户代理 -> 服务器

    1
    2
    3
    4
    POST /acme/shipping HTTP/1.1
    Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme";
    Part_Number="Rocket_Launcher_0001";
    [form data]

    用户选择送货方式。

  6. 服务器 -> 用户代理

    1
    2
    HTTP/1.1 200 OK
    Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"

    产生新的Coookie中映射出送货方式。

  7. 用户代理 -> 服务器

    1
    2
    3
    4
    5
    6
    POST /acme/process HTTP/1.1
    Cookie: $Version="1";
    Customer="WILE_E_COYOTE"; $Path="/acme";
    Part_Number="Rocket_Launcher_0001"; $Path="/acme";
    Shipping="FedEx"; $Path="/acme"
    [form data]

这就是完整的流程。

有一点我必须要提醒你, Cookie并不是完美的。除开安全考虑, 还有一个问题, Cookie的使用会与REST架构风格发生冲突。

注:发生冲突的原因可见文末的附加内容。若对英文版的完整解释有兴趣的话, 可以点击打开。

你可以在RFC 2965了解更多关于cookie的信息。

总结

本文为HTTP系列的一部份。

由本文, 你已经了解到了内容个性化的优缺点, 也知道了服务器标识客户端的多种方法。接下来, 在HTTP系列的第四部分中, 我们将会探讨到最重要的一类客户端标识: 认证(authentication)。

如果你发现本文讲述的某些概念不是很清楚, 你可以参考[HTTP系列][]]的第一部分第二部分

附加内容

  1. 漫画内容翻译
    A:让我们开始会议吧, 但是你要知道, 我会(在会议过程中)记录下你所有的欺凌行为。
    B :emmmm, 我根本不是一个欺凌者, 但是你现在的确认偏误搞得像我在欺凌你一样, 这让我很难受。
    A:可以请你重复一下你暗示我我是个妄想的女巫之后的内容吗?

  2. Cookie与REST架构产生冲突的原因

    One of the key ideas of REST is statelessness – not in the sense that a server can not store any data: it’s fine if there is resource state, or client state.

REST的主要思想是无状态, 无状态并不意味着服务器不能存储任何数据, 而是它只能用来存储资源状态、客户端状态

The most typical use of cookies is to store a key that links to some server-side data structure that is kept in memory. This means that the cookie, which the browser passes along with each request, is used to establish conversational, or session, state.

通常, cookie都被用来存储一个由服务器端发来的值(即存储了服务器端的内容), 该值最后存储于用户硬盘中。这就意味着, 浏览器随每个请求一起发送的cookie, 都是用于设定通信的状态或传送会话的状态。

If a cookie is used to store some information, such as an authentication token, that the server can validate without reliance on session state, cookies are perfectly RESTful

假如cookie中存储了认证信息(例如会话令牌, 即session token), 那么服务器不用验证会话状态就可直接登陆(账户), 此时使用cookie就会完全符合REST原则——此时, cookie存储的session是客户端的。

HTTP系列(二):HTTP的结构体系

在本系列的第一篇文章中, 我们探讨了HTTP的基本概念。有了(前面的)基础, 我们就可以开始HTTP结构体系的学习。 HTTP协议不仅仅只是用来发送和接收数据的。

实际上, HTTP协议本身是不能作为应用程序协议工作的, 但是因为它的基础结构是由硬件和软件组成, 软硬件协同工作提供了不同的服务, 这才能在万维网上实现有效的通信。

本文是HTTP系列文章的第二部分。

文章概要:

这些都是我们网络生活中不可分割的一部分, 接下来, 你可以(通过本文)全面地了解它们的用途及其工作形式。这些知识可以帮你把第一篇文章中的(知识)点串接起来, 使你更加熟悉HTTP通信过程。

那么, 让我们现在就开始学习吧!

Web服务器

正如第一篇文章所提到的, Web服务器的主要功能是存储资源并在接收请求后(按照请求)处理资源。通过Web客户端(又称为Web浏览器)用户可访问Web服务器, 并返回获取请求的资源或更改(资源的)现有的状态。访问Web服务器的操作, 通过网络爬虫, 也可以实现自动化, 相关内容我们会在文章后面进行深入的了解。

你所知道的最流行的服务器, 不外乎是Apache的HTTP 服务器、Nginx、
IIS、Glassfish…

Web服务器可分为(使用)单种简单易用的软件或(使用)多种复杂的软件的服务器。 现代的Web服务器能够执行许多不同的任务, 其基本功能为:

  • 建立连接 —— 接受或关闭客户端连接
  • 接收请求 —— 读取HTTP请求报文
  • 处理请求 —— 解析请求报文并执行(相应请求)
  • 访问资源 —— 访问请求报文中(请求的)资源
  • 构造响应 —— 创建HTTP响应报文
  • 发送响应 —— 向客户端返回响应报文
  • 事务日志 —— 在日志文件中记录已完成的事务

我将Web服务器基本(工作)流程分为不同的阶段, 所列出的阶段是Web服务器(工作)流程的一个极其简化的版本。

阶段 1:建立连接

当Web客户端要访问Web服务器时, 客户端必须要重新打开一个TCP连接。与此同时, Web服务器在另一边向客户端确认IP地址。然后, 由服务器决定是否与该客户端建立TCP连接。

如果服务器接受连接, 它会将客户端的IP地址添加到现有连接的列表中, 并监视该连接上的数据。

如果客户端未被授权或是黑名单中的(视为恶意的), 服务器可以关闭TCP连接。

服务器还可以使用”反向 DNS(Domain Name System, 域名服务器)”来识别客户端的主机名。客户端的主机名有利于事务日志的记录, 但主机名的查找需要时间, 所以会造成记录事务的速度减慢。

阶段 2:接收/处理请求

分析接收的请求时, Web服务器会分析报文的请求行、首部字段和主体(如果有提供的话) 中的信息。需要注意的一点是, TCP连接可以随时暂停, 因此在这种情况下, 服务器必须将(已传送)数据临时存储起来, 直到服务器接收到剩余(未传输的)数据。

高端的Web服务器能够同时建立大量连接——包括同时与同一客户端有多个连接。因此, 典型Web页面可以向高端服务器请求大量不同的资源。

阶段 3:访问资源

Web 服务器通过多种方法映射和操作资源, (为Web客户端)提供资源。

映射资源最简单的方法, 就是根据请求(报文)中的URI在Web服务器的文件系统中查找所需的文件。通常, 资源都存放在服务器上一个被称为文件根目录(docroot)的特殊文件夹中。例如, Windows 服务器上的文件根目录就位于“F:\WebResources \”。假设有一个GET请求想要访问”/image/codemazeblog”上的文件, 服务器会将其转换为”F:\WebResources\images\codemazeblog.txt”, 并在响应报文中返回该文件。当一个Web 服务器承载多个网站时, 每个站点都会有其独立的的文件根目录。

如果 Web 服务器接收到的是对(文件)路径的请求, 而不是对文件的请求, 服务器可以用以下几种方式处理请求:返回错误消息; 替代返回路径或遍历路径, 返回默认索引文件;返回带有(所请求)内容的HTML文件。

服务器还可以将请求(报文)中的URI映射到动态资源上, 这些动态资源是由应用程序生成的。有一类服务器叫做应用程序服务器, 其功能就是管理动态资源, 它还能给Web服务器提供复杂的软件解决方案。

阶段 4:生成及发送响应

一旦服务器确定了需要所请求的资源, 它就会生成响应报文, 报文中包含状态码、响应首部字段, 若请求(报文)中需要的话还应有响应主体。

如果存在响应主体, 那么该响应报文通常包含Content-Length和Content-Length两个首部字段——Content-Length描述主体大小, Content-Length描述返回资源的MIME类型。

生成响应(报文)后, 服务器选择需要发送响应的客户端。对于非持久性连接(长期连接), 服务器需要在发送整个响应消息后关闭连接。

阶段 5:记录事务

服务器在事务完成后将记录文件中的所有事务信息, 大部分的服务器都采用自定义的方式记录日志。

代理服务器

代理服务器 (代理) 是的中间服务器, 通常位于Web服务器和Web客户端之间, 因为这个性质, 代理服务器需要具有与Web客户端和Web服务器两者相同的行为(功能)。

不过, 为什么要用代理服务器呢?我们为什么不直接用Web客户端和Web服务器进行通信?这难道不是更加简单快速吗?

当然, (直接利用Web客户端和Web服务器)是操作起来是很简单, 不过说到速度快, 那就未必了。接下来, 我们会解释原因的。

在解释什么是代理服务器之前, 我们先要解决一个问题, 那就是什么是反向代理服务器(reverse proxy), 或者说(探究) 正向代理(forward proxy)反向代理( reverse proxy)有什么区别。

正向代理负责向Web服务器请求资源, 并将资源返回到客户端。除此之外, 正向代理还能通过防火墙过滤请求或隐藏客户端信息来维护客户端的安全。而反向代理的工作方式(与正向代理)完全相反, 它通常位于防火墙之后来保护 Web 服务器。所有客户端已知其通信对象是实际的Web服务器, 但却了解反向代理背后的(Web服务器所处的)网络。

代理服务器

反向代理服务器


代理(服务器)非常有用并且应用十分广泛。接下来, 让我们来了解一下代理服务器的功能。

  • 压缩(Compression) —— 就是简单地利用压缩内容的方式加快通信速度。
  • 监控(Monitoring)和过滤(filtering) —— 用代理服务器(进行监控和过滤)不失为一个阻止小学生访问成人网站好方法。🙂
  • 安全(Security) —— 代理(服务器)可以作为整个网络的一个独立的入口点, 它们能够检测恶意软件和对应用层协议设限。
  • 匿名性(Anonymity) —— 代理可以修改请求(报文)以提高匿名性, 它可以从请求中剥离敏感信息, 只留下重要的东西。尽管向服务器发送较少的信息会降低用户体验,
    但是有时侯, 匿名性(比用户体验)更重要。
  • 访问控制(Access control) —— 若要实现对多个服务器的访问控制, 集中管理单个代理服务器即可。
  • 缓存(Caching) —— 使用代理服务器缓存常用的内容, 会大大降低加载速度。注:这里的加载速度按照我自己的理解, 应该是如果存在缓存的话,
    服务器需要先去加载缓存中的内容, 这会降低加载的速度
  • 负载均衡(Load balancing) —— 如果服务达到性能的临界点, 你可以使用负载均衡器——负载均衡器是一种可均衡分配路由通信量的代理, 可避免在服务性能达到临界点时导致单个服务器超负荷——分配在资源或Web服务器中的负载。
  • 转码(Transcoding) —— 代理服务器有修改报文主体的功能。

综上所述, 代理(服务器)有功能多样且可灵活使用的特点。

缓存

Web 缓存(系统)是一种可以自动复制请求报文数据并将其保存到本地存储中的设备。

Web缓存的优点:

  • 减少通信量
  • 消除网络瓶颈
  • 防止服务器草符合
  • 降低长距离造成的响应延迟

因此, 可以直白地说, Web缓存改善了用户体验和服务器性能。当然, 还有可能省了很多钱。

命中率的范围由0至1, 其中0表示缓冲提供所请求的0%资源 ,而1表示缓冲提供所请求的100%资源。命中率最理想的状态当然是达到100%, 但是, 实际命中率通常只能接近40%。

下面是基本的Web缓存的工作流程:

网关、隧道、中继代理

随着HTTP(越来越)成熟, 不同的HTTP协议使用方法不断涌现出来。这就促使, HTTP——连接不同应用程序和协议的框架——变得非常有用。

接下来, 就让我们来看看HTTP是怎么做到如此有用的。

网关

网关是一种(网络)硬件,它能够通过抽象方法使 HTTP协议与不同协议和不同应用程序进行通信, 从而获得资源。网关也被称为协议转换器, 由于(具有)多个协议的应用, 所以它比路由器或交换机复杂得多。

下面是网关利用转换协议工作的实例:使用网关发送HTTP请求可以接收到FTP(File Transfer Protocol, 文件传输协议)文件; 使用客户端加速安全网关可以接收到SSL(Secure Sockets Layer, 安全套接层)加密后转换成HTTP协议的报文; 使用服务器端安全网关可以将HTTP报文转换成更安全的HTTPs报文(Hypertext Transfer Protocol Secure, 超文本传输安全协议);

隧道

隧道(协议)是利用CONNECT请求方法实现的, 该协议能够通过HTTP协议发送非HTTP的数据。CONNECT是一个(客户端)要求代理连接目标服务器的请求方法, 并且在客户端和服务器之间中转数据。

注:上面这句话简单理解就是, 隧道协议不用HTTP解析数据, 对数据不做任何处理, 只将数据直接中转给目标服务器。

CONNECT请求:

1
2
3
CONNECT api.github.com:443 HTTP/1.0
User-Agent: Chrome/58.0.3029.110
Accept: text/html,application/xhtml+xml,application/xml

CONNECT响应:

1
2
HTTP/1.0 200 Connection Established
Proxy-agent: Netscape-Proxy/1.1

与常规的HTTP响应(报文)不同, CONNECT响应不需要特别说明Content-Type字段。

一旦(隧道)建立了连接,客户端和服务器就可以直接进行通信。

中继代理

中继(服务器)在HTTP世界中充当着坏人的角色, 他们不受HTTP的律法束缚。实际上, 中继(服务器)是代理的简化版本, 只要它们能够建立连接——仅需请求报文中极少信息量——那么就可以中转所有接收到的信息。

中继存在的唯一的理由就是, (在某些情况下)需要一个尽可能少惹麻烦的代理。不过, 使用中继也可能带来麻烦。因此中继是应用在特定场景下, 所以在添加中继(服务器)之前, 一定要先考虑添加之后的损益比。

网络爬虫


当然, 通常都称呼它为网络蜘蛛(spiders), 它是一种网络机器人, 用于自动浏览万维网和编纂网络索引。因此,对于搜索引擎和大部分来说, 网络爬虫是必不可少的工具。

网络爬虫是一个全自动化的软件, 不需要人机交互。网络爬虫的复杂度差异会很大, 一些网络爬虫就非常复杂(比如搜索引擎用的爬虫)。

网络爬虫会消耗所访问的网站的资源。因此, 公开的网站有一个机制去告知爬虫:网站哪些内容可被抓取, 或禁止爬虫抓取任何数据。你可以通过robots.txtRobots Exclusion Protocol, 漫游器排除协议)去实现这个机制。

毫无疑问, robots.txt不能阻止那些不请自来的网络爬虫抓取网站, 因为它只是一个标准。也就是说, robots.txt只能防君子不能防小人。

下面是robots.txt书写方式的举例:

1
2
User-agent: *
Disallow: /

含义:禁止所有爬虫访问网站的任何部分。

1
2
3
4
User-agent: *
Disallow: /somefolder/
Disallow: /notinterestingstuff/
Disallow: /directory/file.html

含义:仅禁止访问两个特定的目录和文件。

1
2
User-agent: Googlebot
Disallow: /private/

含义:在特定情况下, 禁止指定的爬虫访问。

不过, 鉴于万维网的庞大, 即使是有史以来最强大的爬虫, 也无法抓取整个网络的资源并编纂整个网络的索引。这就是爬虫使用选择策略(selection policy)抓取关联性最大的部分的原因。此外, 万维网经常发生动态变化, 因此爬虫必须用重新访问策略(freshness policy )去计算它们是否能再次访问网站。并且, 爬虫轻松覆盖整个服务器的资源时, 会采取发送过于快速和繁多的请求的举措, 因此爬虫会采取平衡礼貌策略(politeness policy)避免造成服务器不能正常使用。已知的大部分爬虫, 以低至20秒高至3-4分钟的时间间隔对服务器进行轮询, 避免使服务器上超载。

注:重新访问策略其实就是为了避免资源过期, 爬虫进行确认的一个机制。

你可能听说过神秘又邪恶的深网或暗网。但是, 深网(暗网)只是网络的一部分, 搜索引擎为了将(其中的)信息藏起来故意不把它们编入索引。

总结

这个链接是HTTP系列的目录及简介。相信你现在脑海中对HTTP工作流程已经有了更加清晰的概念, 学到了比(原本的)请求、响应、状态码更多的知识正是不同的软硬件结合的整体架构, 才让作为应用层协议的HTTP能够发挥出它的潜能。

我在这篇文章谈到的每个概念都很庞大, 大到足以用一篇文章甚至一本书来细细讲述。不过, 我只是想大概介绍一下概念, 这样你们就知道这些概念是如何搭配使用的, 方便你们之后查找相关内容。

如果在阅读的过程中, 你发现有些内容解释得过于简短和不清晰并且没有看过我之前的文章, 你可以阅读这篇文章的第一部分HTTP指南, 在这两篇文章中都有关于HTTP基本概念的概述。

感谢你阅读本文, 敬请关注HTTP系列的第三部分, 我在这篇文章中讲述了服务器是如何识别客户端的。

HTTP系列(一):HTTP的基础概念概述

前言

在网上看到这篇很棒的文章, 所以接下来, 我会边学习边翻译HTTP系列这篇文章。
如果想阅读原文, 你可以访问这个网站: https://code-maze.com/

原作者是一个很nice的人, 也花了挺多时间写这篇文章还有维护这个网站的, 相信大家能在网站里找到更多自己想要的东西。

正文

本文会对HTTP的基础概念进行介绍。

为什么要学HTTP?

你可能会问:为什么要了解HTTP协议?

如果你是软件开发人员, 学习HTTP协议通信过程, 会让你知道怎么样才能写出更好的应用程序。
如果你是系统架构师或者是网络管理员, 学习HTTP通信过程, 会让你进一步学习复杂的网络体系结构设计。

表现层状态转换(REST, Representational State Transfer)是现在一种很重要的架构风格, 使用这个架构时我们需要先了解HTTP协议的特性, 因此, 学习HTTP协议就变得尤为重要了。如果你想做好RESTful架构的产品, 你就必须先学习HTTP协议。

我必须提醒你, REST架构并不是只能使用HTTP协议的, 它也能用其他的协议来实现。但是, 由于HTTP应用的广泛性, 它在那场战役(选择实现REST架构的协议的那场战争)中获得了胜利, 因此给予找不到用其他协议来实现的REST架构。

难道, 你想错过这个了解、学习万维网和网络通信的基本概念的机会吗?

我希望你不会想这样的🙂

本文会尽可能简单地介绍HTTP重要的部分。我写这篇文章主要是为了将HTTP有用的信息集中到一起, 省去你们在书籍和RFC文档上寻找你们需要信息花费的时间。

这篇文章是HTTP系列的第一篇文章, 它会简要地介绍HTTP的基础概念。

文章概要:

  • HTTP的定义
  • 资源
  • 如何在WEB客户端和服务器之间交换信息?
  • 报文实例
  • MIME类型
  • 请求方法
  • 报文首部
  • 状态码

闲话少说, 直接进入下面的学习吧!

HTTP的定义

万维网的发明者Tim Berners-Lee发明了HTTP协议。纵观HTTP的发展史, Roy Fielding(REST架构风格的发明者)同样是一个重要人物。

超文本传输协议(The Hypertext Transerfer Protocol)是应用程序之间的通信协议。本质上, HTTP协议是负责委派互联网中客户端和服务器端之间的媒体文件(包括HTML、图片、文本文件、电影等客户端和服务器端之间传输的所有文件), 使用HTTP协议能使媒体文件快速、可靠地传输。因为HTTP被用于应用层通信, 所以HTTP协议是应用层协议而不是传输层协议。下图为网络栈的构成, 放在这里是为了让你们回忆起(相关知识)。

在上图中,你可以清晰地看到HTTP是应用层协议,而(不像)TCP是传输层协议。

资源


因特网上所有的内容都是资源(Resource), 而HTTP正是处理这些资源的协议——包括文件、(数据)流、 服务和其他所有的东西。HTML页面、youtube上的视频、WEB应用程序上日常任务电子表格…这些都是资源。(相信通过上面的举例)你已经知道了什么是资源了。

接下来就要思考另一个问题了, 如何区分不同的资源?

通过给资源标识URLs(Uniform resource locators, 统一资源定位符)——这里URL和URI(统一资源标识符)在大部分时候都可以混用。

URL指向唯一的(资源存放)位置。

如何在Web客户端和服务器端之间交换信息?

所有的内容、资源都是存放在一些WEB服务器(HTTP服务器)上的。想要获取资源, 那么需要你发送HTTP请求给这些(HTTP)服务器。

但是, 怎么样才能从WEB服务器请求(得到)资源?

当然, 你必须先有一个支持HTTP协议的客户端🙂。

你现在阅读文章时所使用的Web浏览器, 就是HTTP客户端。

Web浏览器与HTTP服务器进行通信,之后, 将从服务器上获取的资源传送到你的电脑中。

(市面上)最流行的客户端有:Google的Chrome浏览器、Mozilla的火狐浏览器、Apple的Safari浏览器、(居然还有)臭名昭著的Internet Explorer浏览器。
注:作者原本写的是unfortunately,我感觉这种“哀其不幸, 怒其不争”的语气去吐槽还有人用着Internet Explorer这件事, 用“居然”会稍微合适一点

报文实例

那, HTTP报文到底长什么样?

多说无益, 下面列出HTTP报文的示例:

GET请求

1
2
3
4
5
GET /repos/CodeMazeBlog/ConsumeRestfulApisExamples HTTP/1.1
Host: api.github.com
Content-Type: application/json
Authorization: Basic dGhhbmtzIEhhcmFsZCBSb21iYXV0LCBtdWNoIGFwcHJlY2lhdGVk
Cache-Control: no-cache

POST请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks?access_token=5643f4128a9cf974517346b2158d04c8aa7ad45f HTTP/1.1
Host: api.github.com
Content-Type: application/json
Cache-Control: no-cache
{
"url": "http://www.example.com/example",
"events": [
"push"
],
"name": "web",
"active": true,
"config": {
"url": "http://www.example.com/example",
"content_type": "json"
}
}

通过上面的例子, 我们来快速地比较一下GET请求报文和POST请求报文。

请求报文的第一行是请求行。请求行包括请求方法、请求URI和HTTP版本。

接下来几行是请求首部字段。请求首部字段提供请求报文的附加信息, 如请求响应的内容类型、授权信息等。

对于GET请求来说, HTTP报文到这里结束了。而POST请求还可以传输(实体)主体, 并且可以以报文主体的形式携带附加信息。在创建GitHub webhook(网络回调)时, 会产生有附加信息的JSON数据——关于如何为有URI标识的特定项目创建webhook——这个数据在创建webhook时是必不可少的, 通过POST请求向GitHub API 提供该数据。

注:《图解HTTP》一书中提到实体主体和报文实体的区别, 一般来说实体主体和报文主体是相同的, 只有在编码时会改变实体主体,也就是说如果我分别用deflate(zlib)和identity(不进行编码)对内容进行编码, 只会影响实体主体而不会影响报文实体。

HTTP报文的请求行和各种首部字段——即报文首部——后面必须有 (回车符\r和换行符\n), 并且报文首部与的报文主体之间存在一个空行, 该空行仅包含 CRLF。

HTTP 请求协议相关内容参照: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html

之后, 服务器端会如何响应这些请求呢?

响应报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
HTTP/1.1 200 OK
Server: GitHub.com
Date: Sun, 18 Jun 2017 13:10:41 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Status: 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4996
X-RateLimit-Reset: 1497792723
Cache-Control: private, max-age=60, s-maxage=60
[
{
"type": "Repository",
"id": 14437404,
"name": "web",
"active": true,
"events": [
"push"
],
"config": {
"content_type": "json",
"insecure_ssl": "0",
"url": "http://www.example.com/example"
},
"updated_at": "2017-06-18T12:17:15Z",
"created_at": "2017-06-18T12:03:15Z",
"url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404",
"test_url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404/test",
"ping_url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404/pings",
"last_response": {
"code": 422,
"status": "misconfigured",
"message": "Invalid HTTP Response: 404"
}
},
]

除了第一行(状态行), 响应报文与请求报文的结构几乎相同, 惊奇地发现响应报文的状态行(实际上)是响应状态的相关信息。

在状态行之后, 是响应报文的各种首部字段和响应报文主体。

注:响应状态包括相应结果的状态码, 原因短语和HTTP版本

HTTP响应报文参照: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html

MIME类型

MIME类型是互联网中用于描述文件类型的一种标准化方法。 (我们使用的)浏览器上就有一个MIME类型的列表,同样的, WEB服务器端也有一个MIME类型的列表, 通过这种方法, 我们就可以在不同的操作系统间传输常见类型的文件。

有趣的是, 实际上MIME是多用途因特网邮件扩展的意思,它们一开始是邮件用来处理不同类型的数据的。但是从那之后, MIME(就)被改造成为HTTP协议和其他协议使用的机制了。

MIME类型由类型(type)、子类型(subtype)、可选参数(optional parameters)构成, 格式为:类型/子类型;可选参数。

例子如下:

1
2
3
Content-Type: application/json
Content-Type: text/xml; charset=utf-8
Accept: image/gif

你可以在HTTP指南中查看常见的MIME类型和子类型列表。

请求方法

HTTP请求方法(也被称为HTTP动词)定义了将在资源上执行的操作。HTTP中有多种请求方法, 最常用的是GET方法和POST方法。

请求方法有幂等性和非幂等性之分。“幂等性”与“非幂等性”只是花里胡哨的术语, (实际上)它是用来反映某一请求方法多次调用同一资源的操作安全与否的。这意味着, 为了安全, GET方法—用于获取资源的方法—默认情况下应该是幂等性的方法。在用GET方法调用同一资源时, 不应该产生不同的响应结果。与之相对, POST方法就不应该是幂等性方法。

注:关于幂等性, 可以看看理解HTTP幂等性这篇文章, 里面的解释还是比较容易理解的。

HTTP/1.1版本之前,只有GET、POST、HEAD三种请求方法。在HTTP/1.1规范中, 引进了许多新的方法:OPTIONS、PUT、DELETE、TRACE、CONNECT。

如果想要进一步了解请求方法, 可以参考HTTP指南

报文首部

报文首部域是由冒号分割的首部字段名和字段值组成的, 反映了服务器端或客户端请求或响应的内容及属性, 位于请求报文或响应报文第一行之后。

这里有五种报文首部类型:

  • 通用首部字段(General headers):该首部字段被应用于客户端和服务器端双方。举一个常见的例子,日期首部字段(Date headers)就指明了创建报文的日期时间。

  • 请求首部字段(Request headers):该首部字段仅在请求报文中出现, 向服务器端提供附加信息。例如, “Accept:/“告知了服务器客户端可处理任何媒体类型。

  • 响应首部字段(Response headers)
    :该首部字段仅在响应报文中出现,想客户端提供附加信息。例如, “Allow:GET, HEAD, PUT”告知了客户端服务器能接受的请求资源方法。

  • 实体首部字段(Entity headers):该首部字段用于实体主体。例如”Content-Type: text/html”告知应用程序(传输)的数据是HTML文件。

  • 扩展首部字段(Extension headers):这些非标准首部字段可由应用程序开发人员创建, 尽管该首部字段不在HTTP规范中, 但仍可使用。
    你可以在HTTP指南中查看常用请求首部和响应首部字段。

状态码

状态码(Statue code)是一个三位数字, 表示请求的结果。原因短语(Reason phrase)是用自然语言表示的状态代码解释, 跟随在状态码之后。

例子如下:

  • 200 OK —— 表示从客户端发来的请求在服务器端正常处理了
  • 404 Not Found —— 服务器上无法找到请求资源
  • 500 Internal Server Error —— 服务器端在执行请求时发生错误

状态码根据不同的范围分为五种不同的类型。

如果想查看状态码的分类和完整的状态码列表及其含义, 可以看HTTP指南

总结

emmm, 这个信息量挺大的。

学习HTTP协议的基础概念, 虽然不能帮助你直接解决问题, 但是从中学习到的互联网通信的基本原则, 几乎适用于所有比HTTP协议更高级别的问题。不管是 REST、APIs、WEB应用程序开发还是网络方面出现的问题, 现在, 在解决这些问题时, 你至少可以更加自信一点。

不可否认, HTTP讲起来是一个相当大的话题, 之后的内容, 与HTTP协议的基本概念比起来多多了。

HTTP体系结构方面会在HTTP系列(二)中介绍。

名词解释与概述:

REST

REST(Representational State Transfer)——表现层状态转换, 是一种用来描述网络中Client和Server的交互形式的协议。
下面对名词进行解释:

  1. 表现层(Representation), 直白地说就是资源(信息实体)具体呈现出来的形式, 例如:文本表现为TXT格式、HTML格式或者是XML格式。

  2. 状态转换(State Transfer), 这个状态转换实际上是发生在Client访问Server的过程中。为什么会选择无状态HTTP作为实现REST架构的协议? 因为HTTP可发送四种状态:GET、POST、PUT、DELETE。这四个HTTP协议的动词可以用来进行不同的操作:GET用来获取资源, POST用来新建更新资源,PUT用来更新资源,DELETE用来删除资源。在写的时候我自己有点小迷糊, 什么是有状态的? 什么是无状态的呢? 无状态, 其实就是所有的状态都保存在Server。因此, 状态转换, 是在信息由Client传送到Server的中间过程实现的。
    RESTful

libsvm环境配置及使用方法

前言

在刚刚学习机器学习内容的时候,就给了自己很清晰的定位“未想有所成,只想有所用”。因为机缘巧合接触的机器学习,假期想学习的内容里面需要安装libsvm包,在翔翔的远程帮助和及其懈怠的状态下,我按照以下步骤在五分钟之内解决了安装libsvm包的时候出现的问题。(前提是你在Anaconda找不到libsvm的包)

环境、所用到的软件及其版本

win 10
Anaconda3_5.5.0
python 3.6
Visual Studio 2013
(当然这个不在我电脑上=v=,感兴趣地可以按照我之后给的链接在本机运行一下)
PyCharm 2017.2.3

libsvm环境配置

以下文件路径仅仅是我电脑中的,如果存放的位置不一样,请自行调整

  1. 因为电脑装VS之后出现了点问题,所以不能按照林智仁LIBSVM python包的安装与使用这个链接中64位机的解决方法。就直接用了翔翔发给我的libsvm.dll。将相关链接中的“用vs2013运行过的libsvm.dll”下载好直接放在C:\Windows\System32下即可。
    Tips:或者在Anaconda离线包的链接中下载,按照“林智仁LIBSVM python包的安装与使用”进行操作。当时在页面不能直接找到VS的Prompt快捷方式,打开D:\develop\Visual Studio\Common7\Tools\Shortcuts中的VS2013 x64 本机工具命令提示进行nmake操作。
  2. 直接将libsvm文件夹中python下的 svmutil.py和svm.py两个文件复制粘贴到D:\develop\Anaconda\Lib下

  3. 在python环境下测试是否安装成功
    (1) 输入测试代码

    from svmutil import *
    y, x = svm_read_problem('../heart_scale')
    m = svm_train(y[:200], x[:200], '-c 4')
    p_label, p_acc, p_val = svm_predict(y[200:], x[200:], m)
    

    (2) 测试代码结果输出:

    optimization finished, #iter = 257
    nu = 0.351161
    obj = -225.628984, rho = 0.636110
    nSV = 91, nBSV = 49
    Total nSV = 91
    Accuracy = 84.2857% (59/70) (classification)
    

libsvm使用方法-非常简易版本

  • svm_train函数
    这里用的是彭亮视频中的例子
    from svmutil import *
    x = [[1,1], [2,0], [2,3]]
    y = [0, 0, 1]
    clf = svm_train(y, x, '-s 0 -t 0')
    
  • 相关参数解释说明
    x是特征向量
    (在这里,x必须是list或者是tuple,因为svm_train函数第二个参数传入时限定了,如果不是的话,会有报错)
    y是特征向量对应的标记
    clf是分类器
    ‘-s 0 -t 0’是option,详情请见LIBSVM详细介绍和使用方法
  • svm_predict函数
    from svutil import *
    x = [[1,1], [2,0], [2,3]]
    y = [0, 0, 1]
    x1 = [[1,1], [2,0]]
    y1 = [0, 1]
    svm_predict(y1, x1, clf)
    

相关链接


林智仁LIBSVM python包的安装与使用
Anaconda离线包
LIBSVM详细介绍和使用方法
用vs2013运行过的libsvm.dll 密码:aiu7
libsvm-3.22 离线包 密码:gbpl