URI
URI 是一种更通用的资源标识符,URL 实际是它的子集。URI 是一个通用的概念,由两个主要的子集 URL 和 URN 构成 ,URL 是通过描述资源的位置来标识自愿的,而 URN 则是通过名称来识别资源,与它们所处的位置无关
URN 的好处是显而易见的,但是将 URL 转换成 URN 需要进行大量的工作,在可预见的未来,英特网资源仍然会以 URL 来命名。
URL 格式
1 | <scheme>://<user>:<password>@<host><port><path>;<params>?<query>#<frag> |
除了通用的 URL 格式外,我们生活中还用了很多常用的 Web 链接方案,它们与标准的 URL 或多或少有一些异同,下面就来介绍一下
http 超文本传输协议,除了没有用户名和密码之外,与通用的 URL 格式相同,如果没有指定端口,那么它的默认端口为 80
1
2
3
4
5
6
7
8
9格式:http://<host>:<port>/<path>?<query>#<frag>
范例:https://www.jianshu.com/recommendations/notes?category_id=56#9835
scheme:https
host: www.jianshu.com
port: 80
path: recommendations/notes
query:category_id=56
flag: 9835https 方案与 http 的唯一区别在于 https 方案使用了网景的 SSL,为 http 提供了端到端的加密机制。默认端口 443
1
格式:http://<host>:<port>/<path>?<query>#<frag>
ftp 文件传输协议 URL 可以用来从 FTP 服务器上下载或其上载文件,并获取 FTP 服务器上的目录结构内容的列表。
1
2
3
4
5
6
7
8
9
10格式: ftp://<user>:<password>@<host>:<port>/<path>;<params>
范例: ftp://anonymous:joe%40joes-hardware.com@prep.ai.mit.edu:21/pub/gnu/
scheme: ftp
username: anonymous
password:joe@joes-hardware.com
host: prep.ai.mit.edu
prot: 21
path: pub/gnu/rtsp,rtspu 是可以通过实时流传输协议解析的音视频媒体资源的标识符。方案中的
u
表示它是用来使用 UDP 协议获取资源的。1
2
3
4格式: rtsp://<user>:<password>@<host>:<prot>/<path>
rtspu://<user>:<password>@<host>:<prot>/<path>
范例: rtsp://www.joes-hardware.com:554/interview/cto_videofile 方案 file 表示一台指定主机上可以直接访问的文件。各字段都遵循通用格式。如果省略了主机名,就默认为正在使用的基本格式。
1
2格式: file://<host>/<path>
范例: file://OFFICE-FS/policies/casual-firdays.doc
HTTP 报文
报文的请求格式 :
1 | <method> <request-RUL> <version> |
报文的响应格式 :
1 | <version><status><reason_phrase> |
解释:
方法(method)
客户端对服务器资源执行的动作。由一个单独的单词表示。方法 描述 主体部分 GET 从服务器获取一份文档 否 HEAD 只从服务器获得文档的首部 否 POST 向服务器发送需要处理的数据 是 PUT 将请求的主题部分存储在服务器上 是 TRACE 对可能经过代理服务器的报文进行追踪 否 OPTIONS 决定可以在服务器上执行哪些方法 否 DELETE 从服务器删除一份文档 否
请求 URL(request-URL)
所请求资源,或者 URL 路径组件的完整 URL版本(version)
报文所使用的 HTTP 版本 ,格式规定是这样的:1
HTTP/<major>.<minor>
major 表示主要版本号而 minor 表示次要版本号,它们都是整数
状态码(status-code)
这三位数码描述了请求过程中发生的情况。每个状态码的第一位数都用语描述状态的一般类别(成功、出错等)整体范围 已定义范围 分类 100 ~ 199 100 ~101 信息提示 200 ~ 299 200 ~ 206 成功 300 ~ 399 300 ~ 305 重定向 400 ~ 499 400 ~ 415 客户端错误 500 ~ 599 500 ~ 505 服务器错误
原因短语(reason-phrase)
数字状态码的可读版本,只对人类解读有意义首部(header)
可以有零个或多个首部,每个首部都包含一个名字,后面跟着一个冒号:
然后是一个可选的空格,接着是一个值,最后是一个 CRLF。实体的主体部分(entity-body)
包含一个任意数组组成的数据块。但并不是所有的报文都包含实体的主体部分。
连接管理
TCP
HTTP 和 TCP 的关联
HTTP 连接实际上就是 TCP 连接及其使用规则。 TCP 为 HTTP 提供了一条可靠的比特传输管道。从 TCP 连接一端填入的字节会从另一端以原有的顺序正确的传送出来。
TCP 的数据通过名为 IP 分组(或 IP 数据报)的小数据块来发送的,按 HTTP over TCP over IP 这样的协议栈来传递。其安全版本 HTTPS 就是在 HTTP 和 TCP 之间插入了一个(称为 TSL 或 SSL)密码加密层
HTTP 请求流程
- 客户端首先根据 URL 确定 服务器 IP 地址和端口号。如果最近没有对 URI 中的主机名进行访问,则需要通过 DNS 解析系统将 URI 中的主机名转换成一个 IP 地址。
- 客户端向服务器发送一条 TCP 连接请求,并等待服务器回送一个请求接受应答,从而建立起一个 TCP 连接。
像我们常用的 okHttp 这种网络框架除了常用 的 DNS 缓存外还建立了 socket 连接池用作 TCP 连接的复用。 - 建立连接后客户端会通过新建立的 TCP 管道来发送 HTTP 请求,服务器收到请求并对请求经行处理。
- 服务器回送 HTTP 响应
TCP 网络的时延取决于应减速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。以及 TCP 协议的技术复杂性也会对时延产生有巨大的影响。
TCP 具体是如何传递的
当 HTTP 要发送一条报文时,会以流的形式将报文数据的内容通过一条打开的 TCP 连接按序传输。TCP 收到数据流之后,会将数据流砍成被称作段的小数据块。并将段封装在 IP 分组中,通过因特网进行传输。所有的这些工作都是由 TCP/IP 软件来处理的,HTTP 程序员什么都看不到。
每个 TCP 段都是有 IP 分组承载,从一个 IP 地址发送到另一个 IP 地址的每个分组中都包含:
- 一个 IP 分组首部(通常为 20 字节)
包含了源和目的地的 IP 地址、长度和其他一些标记 - 一个 TCP 段首部(通常为 20 字节)
包含了 TCP 端口号、TCP 控制标记,以及用于数据排序和完整性检查的一些数字值 - 一个 TCP 数据块(0 或多个字节)
被分割封装的数据块
TCP 性能优化
TCP 性能聚焦区域
TCP 连接建立握手
提起 TCP 握手就很容易想到 TCP 的 三次握手和四次挥手这种耳熟能详的话语,这句话也同时证明了 TCP 的建立是多么的复杂。现在就是详细说说 TCP 是怎么三次握手和四次挥手的- 请求新的连接时,客户端要向服务器发送一个小的 TCP 分组。分组设置了一个特殊的标记 SYN,说明这是一个连接请求
- 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个 TCP 分组。分组中 SYN 和 ACK(确认有效标记)都被置位,说明连接已经被接受
- 客户端收到服务器的消息后,再回送一条标记 ACK 的分组消息,发送完毕后客户端和服务器端进入连接状态,三次握手完毕
- 客户端进程发出释放报文,并停止发送数据。报文首部标记 FIN ,说明这个请求是断开连接请求
- 服务器收到释放报文,发出 ACK 确认报文。此时服务已经进入了半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端仍要接收。
- 服务器将所有数据发送完毕后,就向客户端发送 FIN 和 ACK 标记的报文确认释放
- 客户端收到释放报文后立刻发出 ACK 确认报文,此时四次挥手结束。但是客户端只是进入了 TIME_WAIT 状态,还需要经过 2MSL 的最长报文时间后才真正的结束释放 TCP 链接
小的 HTTP 事务可能会在 TCP 建立连接上花费 50% 甚至更多的时间。
TCP 慢启动拥塞控制
数据聚集的 Nagle 算法
用于捎带确认的 TCP 延迟确认算法
TIME_WAIT 时延和端口耗尽
Tips:
什么是 2MSL?
MSL 是 Maximum Segment Lifetime 英文的缩写,中文可以译为 “报文最大生存时间”,他是任何报文在网络上存在的长时间,超过这个时间报文将被丢弃。RFC793 中规定 MSL 为 2 分钟,实际应用常用的是 30 秒、1 分钟和 2 分钟。2MSL 即 2 倍的 MSL 时间
持久连接
客户端经常会打开到同一个站点的连接。一个页面上的大部分资源、请求相当一部分通常都指向同一个站点。因此从,初始化了对某服务器 HTTP 请求的应用程序可能在不久的将来还会对那台服务器发起更多请求。这种性质被称作为“站点局部性”
因此 HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许 HTTP 设备在事务处理结束之后将 TCP 任然保持在打开在状态,以便未来的 HTTP 请求重用现存的链接。重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便快速进行数据的传输。
持久连接分为两种类型
1. HTTP/1.0 + keep-alive 连接
1996 年开始 ,很多 HTTP/1.0 浏览器和服务器都经行了扩展,以支持一种被称为 keep-alive 连接的早期实验性持久连接。这些早期的持久连接受到了一些互操作性设计方面的困扰,与之后的 HTTP/1.1 版本得到的了修正,但是至今任然还有很多实用早期的 keep-alive 连接
实现 keep-alive 连接的客户端可以通过包含 connection:keep-alive 的首部请求来请求一条将要保持状态的连接,服务器如果愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有,客户端则认为服务器不不支持 keep-alive
需要注意的是 Keep-alive 只是请求将连接保持在活跃状态。发出 keep-alive 请求后客户端和服务端不一定会同意,它们可以随时关闭该连接。
2. HTTP/1.1 持久连接
HTTP/1.1 逐渐停止了对 keep-alive 的支持,用一种名为持久连接(persistent connection)的改进型设计取代了它。
与 keep-alive 不同,持久连接在默认情况下是激活的。除非特别指明,否则 HTTP/1.1 假定所有连接都是持久的。要在事务处理结束之后通过在报文中显式的在报文中添加一个 connection:close 首部将连接关闭。虽然设计上是这样,但是客户端和服务端仍然可以随时关闭。同时不发送 Connection:close 也不意味着服务器承诺永远将连接保持在打开状态
管道化连接
HTTP/1.1 还允许在持久化连接上可选地使用请求管道。这是相对于 keep-alive 连接的又一性能优化。在响应达到之前,可以将多条请求放入队列。当地一条请求通过网络流向服务器时,第二条第三条也可以开始发送。在高延时网络条件下,这样可以降低网络的环回时间,提高性能。
客户端识别与 cookie 机制
HTTP 最初是一个匿名、无状态的请求/响应协议。服务器处理来自客户端的请求,然后向
现代的 Web 站点希望能够提供个性化的接触。但是 HTTP 本身并不具有识别用户的能力,于是早期的 Web 站点设计者们自己创造了一些用户数识别技术。每种技术都有其优劣,大致可以分为以下几种:
- 承载用户身份信息的 HTTP 首部
- 客户端 IP 地址跟踪,通过用的 IP 地址对其进行识别
- 用户登录,用认证方式来识别用户
- 胖 URL,一种在 URL 中嵌入识别信息的技术
- cookie 一种功能强大且高效持久身份识别技术
常用的 HTTP 首部
首部请求 | 首部类型 | 描述 |
---|---|---|
From | 请求 | E-mail 地址 |
User-Agent | 请求 | 用户的浏览器软件 |
Referer | 请求 | 用户是从这个页面上转发过来的 |
Authorization | 请求 | 用户名和密码 |
Client-IP | 扩展(请求) | 客户端的 IP 地址 |
X-Forwarded-For | 扩展(请求) | 客户端端的 IP 地址 |
Cookie | 扩展(请求) | 服务器产生的 ID 标签 |
From 首部包含了用户的 E-mail 地址。每个用户都有不同的 E-mail 地址就,所以在理想情况下,可以将这个地址作为可行的源端来识别用户。但是担心 E-mail 被恶意收集和利用,所有很少有使用和发送 From 首部
User-Agent 首部可以将用户所有的浏览器的相关信息告知服务器,包括程序的名称和版本,通常还包含操作系统的相关信息。
Referer 首部提供了用户来源的 URL 。Referer 首部虽然不能完全标示用户,但它却是说明了用户之前访问过哪个页面,通过它可以更好的理解用户的浏览器行为。
From、User-Agent、Referer 三者虽然都不足以对用户进行可靠的识别,但对也对用户的行为定位起到了一定的作用,User-Agent 可以判断用户的设备,让 Web 页面做出不同的行为。Referer 可以识别用户来源来做数据分析等等。
客户端 IP 地址
早期的 Web 先锋还尝试将客户端 IP 地址作为一种标识形式使用。如果每个用户都有不同的 IP 地址,IP 地址也很少发生变化,那么这种方法确实是可行的。但实际上通过客户端 IP 识别用户存在很多缺点,限制了将其作为用户识别技术的效能。
- IP 地址描述的是客户所使用的机器,而不是用户。如果多个用户使用同一台机器,就无法对其分别了
- 很多因特网服务区会对登录网络的用户动态分配 IP 地址。用户每次登录都会获得一个不同的 IP 地址
- 为了提高安全性,并对稀缺的地址资源进行管理,很多用户都是通过网络地址转换防火墙来浏览网络的。这些 NAT 设备隐藏了防火墙后面时实际客户端的 IP 地址,而将其转换成了客户一个防火墙共享的 IP 地址和不同的端口号来标识
用户登录
Web 服务器无需被动的根据用户的 IP 地址来猜测他的身份,它可以要求用户通过用户名和密码进行认证来显示的询问用户是谁。
而且为了使 Web 站点的登录更加简便,HTTP 中包含了一种内建机制,可以用 WWW-Authenticate 首部和 Authentication 首部向 Web 站点传递用户的相关信息。一旦登录浏览器就可以不断在每条发往这个站点的请你去中发送这个登录信息了。
胖 URL
有些站点还会为每个用户生成特定的 URL 俩追踪用户的身份。通常会对真正的 URl 进行扩展,在 URL 路径的尾部添加一些状态信息来表明用户相关信息,这种修改过的 URl 就被称之为 胖 URL( fat URL)
这种方式存在几个很严重的问题
- 用户相关信息直接暴露在外边
- 破坏缓存,为每个用户生成 URL 就意味着不再有可供访问的公共缓存了。
- 无法共享 , URl 的信息附带了当前用户的个人信息,发送给其他人就意味着信息被暴露了
- 逃逸口,用户必须严格按照流程才能正常使用,如果用户逃离此链接可能会丢失进展(信息都在 URL 中)
Cookie
cookie 是当前识别用户,实现持久会话的最好方式。前面各种技术存在的很多问题对它都没什么影响,但是通常也会将它们与那些技术公用,以实现额外的价值。
cookie 定义了一些新的 HTTP 首部,同时 cookie 还影响了缓存,大多数缓存和浏览器都不会对任何 cookie 的内容进行缓存。
可以将 cookie 笼统的分为两类:会话 cookie 和持久 cookie 。会话 cookie 是一种临时 cookie ,它记录了用户访问站点时设置的偏好和爱好。用户退出浏览器时,会话 cookie 就被删除了。持久 cookie 生存的时间会更长一些;它们存储在硬盘上,即使浏览器退出、计算机重启它们也仍然存在。通常会用持久 cookie 维护某个用户的周期性访问的站点和配置文件或登录名。它们唯一区别就是它们的过期时间。如果 cookie 设置了 Discard 参数或者没有设置 Expires 和 Max-Age 参数来说明扩展的过期时间,这个 coookie 就是一个会话 cookie
cookie 是如何工作的
Cookie 是一种非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。cookie 由服务器生成,发送给浏览器,浏览器把 cookie 以键值对(key-value)的形式保存在目录下的文本内,下一次请求同一网站时会把该 cookie 发送给服务器,服务器就能获得发送请求的用户的状态信息。
cookie 的传递
浏览器内部的 cookie 罐中可以有成百上千个 cookie,但浏览器不会将每个 cookie 都发送给所有的站点。产生 cookie 的服务器可以想 Set-Cookie 响应首部添加一个 Domain 属性来控制哪些站点可以看到那个 cookie。比如。例如:
1 | Set-Cooke:user="mary";domian="airtravelbargains.com" |
之后如果用户访问的是 .airtravelbargains.com 结尾的站点,那么 user="mary"
的这个 cookie 信息都会被发布出去
同时 cookie 规范甚至允许用户将 cookie 与部分 web 站点关联起来。可以通过 path
属性来实现这一功能,在这个属性下列出的 URL 路径前缀下的 cookie 都是有效的
例如,某个 web 服务器可能是由两个组织共享的,每个组织都有独立的 cookie。站点 www.airtravelbargains.com 可能会将部分的 web 站点用于汽车租凭——比如 http://www.airtravelbargains.om/autos/ 用一个独立的 cookie 来记录用户喜欢的汽车样式。可能会生成一个特俗汽车租凭 cookie:
1 | Set-cookie:pref=compact;domian="airtravelbargains.com";path="/autos/" |
如果用户访问 http://www.airtravelbargains.om/specials.html 它就只会获得这个 cookie :
1 | Cookie:user=“mary” |
但如果访问 http://www.airtravelbargains.om/autos/index.html 则就会获得两个 cookie:
1 | Cookie:user="mary" |
因此,cookie 就是服务器贴到客户端上由客户端维护的状态片段,只会回送给合适的站点。
认证机制
现在有数以亿计的人在用 web 进行私人事务处理,访问私有的数据。我们保证能方便访问的同时,且只有特定的人能够看到我们的敏感信息并且能够执行我们的特权事务。并不是所有信息都是能够公开的。
因此服务器需要通过某种方式来了解用户身份。一旦服务器知道了用户身份证,就以判断用户可以访问的事务和资源了。
常见的 HTTP 认证分为基本认证(base authentication)和摘要认真(digest authentication)接下来就介绍两种认证机制
基本认证机制
HTTP 提供了一个原生的质询/响应(challenge/response)框架,简化了用户的认证过程。
Web 应用程序收到一条 HTTP 请求报文时,服务器没有按照请求执行动画,而是以一个“认真质询“进行响应,要去用户提供一些保密信息来说明他是谁,从而对其进行质询。
当用户再次发起请求时要附上保密证书(用户名和密码)。如果政府不匹配则产生一条错误信息。如果匹配,则可以正常请求了
认证协议与首部
HTTP 通过一组可定制的控制首部,为不同的认证协议提供了一个可扩展框架。通过人们可以随意设计一些使用 HTTP 质询/响应的新协议。
HTTP 官方定义了两个协议:基本认证和摘要认证。
基本认证
基本认证是最流行的 HTTP 认证协议。几乎每个主要的客户端和服务器都实现了基本认证机制。基本认证最初是在 HTTP/1.0 规范提出的,但此后被移到了 RFC 2617 中
在基本认证中, Web 服务器可以拒绝一个事务,质询客户端,请用户提供有效的用户和密码。服务器此时会返回 401 状态码来提示客户端需要认证,客户端收到质询后,请求用户输入用户名和密码,然后将用户米和密码稍加扰码再用 Authentication 首部将信息传递给服务器验证。具体的流程如下表格:
步骤 | 首部 | 描述 | 方法/状态 |
---|---|---|---|
请求 | 第一条请求没有认证信息 | GET | |
质询 | WWW-Authentication | 服务器用 401 状态拒绝了请求,说明需要用户提供用户名和密码 | 401 Unauthorized |
授权 | Authentication | 客户端重新发送请求,这次会携带 Authentication 首部,内容部分用来说明算法、用户和密码 | GET |
成功 | Authentication-Info | 如果授权正确,服务器将会把文档返回 | 200 OK |
基本认证存简单便捷,但与此同时也存在很多的安全缺陷,只能用它防止非恶意用户无意间进行的访问,或将其配合 SSL 技术来使用。
基本认证存在下列缺点:
- 用户信息容易被盗取 基本认证会通过网络发送用户名和密码,这些用户和密码只是用 Base-64 编码一下,攻击者可以很容易的获取到用户的明文用户名和密码
- 无法抵御重放攻击 即使用户名和密码进行了更难解码的方式加密,第三方用户任然可以捕获修改过的用户和密码,并将修改过的用户名和密码一次次的重放给服务器
- 没有提供针对代理和中间节点的防护措施 代理和中间节点可以不修改首部的认证信息而去修改报文的其余部分,这样就严重的改变了事务的本质
- 无法识别恶意服务器 基本认证只能服务器对客户端发起质询,客户端无法辨别服务器的真伪
摘要认证
摘要认证是另一种 HTTP 认证协议,它试图修复基本认证的严重缺陷。具体来说,它进行了如下改进:
- 永远不会以明文的方式在网络上发送密码
- 可以防止恶意用户捕获并重放认证的握手过程
- 可以有选择地防止对报文内容的篡改
- 防范其他几种常见的攻击方式
摘要认证是一种升级版的认证方式,所用首部和基本认证类似。它的简要握手机制如下:
- 服务器计算出一个随机数,并将随机数放在 WWW-Authentication 质询报文中,与之一同发送客户端的还有服务器所支持的算法列表
- 客户端选择一个算法,计算出密码和其他数据的摘要,其后将摘要信息放置到 Authentication 报文中发回给服务器。如果客户端还要对服务器进行认证,可以发送客户端随机数。
- 服务端收到摘要、选中的算法以及支持数据,计算出客户端相同的摘要。然后服务器将本地生成摘要与传送过来的数据进行比较,认证其是否匹配。如果客户端反过来通过客户端随机数对服务器机型质询,就会创建客户端摘要。
摘要认证的核心就是对公共信息、保密信息和有时限的随即值这个组合进行信息摘要计算。来防止可能发生的密码窃听、认证重放以及报文内容篡改等。通常我们使用 MD5 等信息摘要算法
摘要认证拥有较为完善的安全机制,但它也并不是万无一失的。它并没有对内容的安全提供任何保证——真正安全的事务只有通过 SSL 才能提供。
HTTPS
随着互联网的高速发展,越来越多的人在网络上处理一些很重要的事情。但如果没有强有力的安全保证,人们就会无法安心地进处理业务。基本认证和摘要认证已经无法满足人们,人们迫切的需要一种易于管理,不但能够适应不断变化的情况还应该能满足社会和政府的各项要求。我们需要一种能够提供下列功能的 HTTP 安全技术:
- 服务器认证
- 客户端认证
- 完整性(客户端和服务端的数据不会被篡改)
- 加密(客户端和服务端的对话是私密的,不会被窃听)
- 效率(一个运行足够快的算法,以便低端的客户端和服务端能够使用)
- 普适性(大多数客户端和服务端都是直接使用)
- 管理的可扩展性(任何地方任何人可以立刻进行安全通信)
- 适应性(能够支持当前最知名的安全方法)
- 在社会上的可行性(满足社会的政治需要)
HTTPS 就应运而生了。它是由网景公司首创的,所有主要的浏览器和服务器都支持此协议。
使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送到网络之前,都要进行加密。HTTPS 在 HTTP 下面提供了一个传输级的密码安全层——可以使用 SSL 也可以使用其后继者——传输安全层(TLS),大部分困难的编码及解码工作都是又 SSL 库中完成的,所以 Web 客户端和服务器在使用 安全 HTTP 时无需过多地修改其协议处理逻辑。
数字加密
详细了解 HTTPS 之前,我们先介绍一些 SSL 和 HTTPS 用到的加密编程技术的背景知识。
最初,人们需要自己进行编码和解码,所以起初密码是相当简单的算法。后来机械技术的发展,使得人们开始制造一些机器,这些机器可以用复杂得多的密码来传递快递、精确地对报文进行编解码,但也仍然被机械设备的速度和功能限制着,之后计算机的发展打破了这些限制,使得超大密钥成为可能。超大密钥可以从一个加密算法中产生数万亿的虚拟加密算法,由不同的密钥值来区分不同的算法。密钥越长,编码组合就越多,通过随即猜测密钥来破解代码就越困难。
对称加密技术
很多数字加密算法都被称为对称密钥(symmetric-key)加密技术,这是应为它们在编码和解码时使用的密钥一样。
在对称密钥加密技术中,发送端和接收端要共享相同的密钥 K 才能进行通信。发送端用共享的密钥来加密报文,并将得到的密文发送给接收端。接收端收到密文,并对其应用解密函数和相同的共享密钥,恢复出原始的明文。
常用的对称密钥算法包括:DES、Triple-DES、RC2 和 RC4
对称密钥加密技术的缺点之一就是发送者和接收者在互相对话之前,一定要有一个共享保密密钥。如果网络上的所有节点都要使用这种技术,那将是一个管理噩梦。
公开密钥加密技术
公开密钥加密技术没有为每对主机使用单独的加密/解密密钥,而是使用了非对称密钥:一个用来对主机报文编码,另一个用来对主机报文解码。编码密钥众所周知的,但只有主机才知道私有的解密密钥。这样,每个人都能找到某个特定主机的公开密钥,密钥的建立变得更简单。但解码密钥是保密,只有接收端才能对发送给它的报文进行解码。
RSA
RSA 算法就是一个满足了所有这些条件的流行的公开密钥加密系统,它是在 MIT 发明的,后来由 RSA 数据安全公司将其商业化。即使有了公共密钥、任意一段明文、用公共密钥对明文编码之后得到的相关密文、RSA 算法自身、甚至源代码,破解代码找到相应的私有密钥的难度仍相当于对一个极大的树进行质因数分解的困难程度。
数字签名
到目前为止,我们已经讨论了各种使用对称和非对称密钥加/解密保密报文的密钥加密技术。除了加/解密报文之外,还可以用加密系统对报文进行签名(sign),以说明是谁编写的报文,同时证明报文未被篡改过。这种技术叫做数字签名(digital signing)对下一节要讨论的英特网安全证书系统来说非常重要。
数字签名是附加在报文上的特殊加密校验码。它有以下好处:
- 签名可以证明作者编写了这条报文。只有作者才会有最机密私有密钥。因此只有作者才能计算出这些校验和。校验和就像来自作者的个人”签名”一样。
- 签名可以防止报文被篡改。如果有恶意在报文传输过程中对其进行了修改,校验和就不再匹配了。由于校验和只有作者保密的私有密钥才能产生,所以攻击者无法为篡改了的报文伪造出正确的校验码
数字签名的认证过程
节点 A 向节点 B 发送与一条报文,它们的交互过程如下
- 节点 A 将变长报文提取为定长摘要
- 节点 A 对摘要应用了一个“签名”函数,这个函数会将用户的私有密钥作为参数。因为只有用户才知道私有密钥,所以正确的签名函数会说明签名者就是其所有者。
- 一旦计算出签名,节点 A 将其附加在报文的末尾,并将报文和签名都发送给 B
- 在接收端,如果节点 B 需要确定报文确实是节点 A 写的,而且没有篡改过,节点 B 就可以对签名进行检查。节点 B 接收经私有密钥扰码的签名,并应用了使用公开密钥的反函数。如果拆包后的摘要与节点 B 自己的摘要版本不匹配,要么就是报文在传输过程中被篡改了,要么就是发送端没有节点 A 的私有密钥(也就是说它不是节点 A)
数字证书
数字证书与现实生活的 ID 卡一样,证书内的内容都是由某些受信任组织以数字方式签发的。证书一般包含证书的对象、发布者、数字签名以及公开密钥等等。任何人都能创建一个数字证书,但并不是所有人都能够获得受人尊敬的签发权,从而为证书信息担保,并用其私有密钥签发证书。
通过 HTTPS 建立一个 Web 事务后,现代的浏览器都会自动获取所连接服务器的数字证书。如果没有证书,安全连接就会失败。服务器中包含很多字段,其中包括:
- Web 站点的名称和主机名
- Web 站点的公开密钥
- 签名颁发机构的签名
- 来自签名颁发机构的签名
浏览器收到证书时会对颁发机构进行验证检查。如果这个机构是很有权威的公共签名机构,浏览器可能已经知道其公开密钥了(浏览器会预先安装很多签名颁发机构的证书),然后浏览器和服务器就开始通过数字签名来进行认证。
如果它对颁发机构一无所知,浏览器就无法确定是否应该信任这个签名颁发机构,它通常提示用户,看看它是否信任这个签名发布者(例如 Chrome 中浏览不受信任的 HTTPS 链接时候,会有 X 和 HTTPS:// 的提示来显示这是一个不安全的访问)
HTTPS 概述
HTTPS 就是在安全的传输层上发送的 HTTP 。HTTPS 没有将未加密的 HTTP 报文发送给 TCP ,并通过世界范围内的因特网进行传输。而在在将 HTTP 报文发送给 TCP 之前,先将其发送给了一个安全层,对其进行加密。目前,HTTP 安全层是通过 SSL 及其现代代替协议 TLS 来实现的。
HTTPS 方案
通常情况下,非安全的 HTTP 的 URL 方案前缀为 http :
1 | http://www.baidu.com/ |
在安全 HTTPS 协议中,URL 的方案前缀为 HTTPS 如下所示:
1 | https://www.baidu.com/ |
当一个客户端对某 Web 资源执行事务时,它会去检查 URL 的方案。
- 如果方案为 HTTP ,客户端就会打开一条到服务器端口 80 (默认情况下) 的连接,并向其发送老的 HTTP 命令
- 如果方案为 HTTPS,客户端就会打开一条服务器到端口 443(默认情况下)的连接,然后与服务器“握手”。这个握手过程中它们要完成以下工作
- 交换协议版本号
- 选择一个两端都了解的密码
- 对两端的身份进行认证
- 生成临时的会话密钥,以便加密信道
服务器证书
SSL 支持双向认证,将服务器证书承载回客户端,再将客户端的证书回送给服务器。
一方面浏览器并不经常使用客户端证书。大部分用户甚至没有客户端证书。服务器可以要求使用客户端证书,但实际上很少这样做。
另一方面,安全 HTTPS 事务总是要求使用服务器证书的。服务器是一个显示了组织的名称、地址、服务器 DNS 域名以及其他信息的派生证书。你和你所用的客户端软件可以检查证书,以确保所有的信息都是可信的。
虽然 SSL 自身并不要求用户检查 Web 服务器证书,但是大部分现代浏览器都会对证书进行简单的完整性检查,并为用户提供进行进一步彻查的手段。网景公司提出的一种 Web 服务器证书有效性算法是大部分浏览器有效性验证的基础。验证步骤如下:
日期检测
首先,检查证书的起始日期和结束日期,以确保证书仍然有效签名颁发者可信度
每个证书都是由某些证书颁发机构(CA)签发的,它们负责为服务器担保。证书有不同的等级,每种证书都要求不同级别的背景验证。任何人都可以生成证书,但有些 CA 是非常著名的组织,它们通过非常清晰的流程来验证证书申请人的身份及商业行为的合法性。因此,浏览器会附带一个签名颁发机构的授信列表。如果浏览器收到了未知颁发机构签发的证书,那它通常会显示一条警告信息。签名检测
一单判定签名授权是可信的,浏览器就要对签名使用签名颁发机构的公开密钥,并将其与校验码进行比较,已查看证书的完整性。站点身份检测
为防止服务复制其他人的证书,或拦截其他人的流量,大部分浏览器都会试着去验证书中的域名与它们所对话的服务器的域名是否匹配。
OpenSSL
OpenSSL 是 SSL 和 TLS 最常见的开元实现,由一些志愿者合作开发,目前是开发一个强壮的,具有完备功能的商业级工具集,以实现 SSL 和 TLS 协议及一个全功能的通用加密库。可以从 https://www.openssl.org 上获取完整的相关信息。
实体和编码
每天都有数以亿计的各种媒体对象经由 HTTP 传送,如图像、文本、影片以及软件程序等。只要你能叫出名字,HTTP 就可以传送,不经如此,它还需要保障它的报文被正确传送、识别、提取以及适当处理。做到这一步它要确保它所承载的“货物”满足以下条件
- 可以被正确识别(通过 Content-Type 首部说明媒体格式,Content-Language 说明媒体语言),以便被客户端正确识别处理
- 可以被正确解包(Content-Length 首部来说报文长度和 Content-Encoding 说明报文转码压缩方式)
- 是最新的(Expires、Last-modified 来说明实效时间和最后修改时间,Cache-Control 来说明缓存)
- 符合用户的需要(基于 Accept 系列的内容协商用户所需内容)
- 在网络上可以快速有效的传输(通过范围请求、差异编码以及其他数据压缩方法)
- 完整到达、未被篡改(通过 Content-Range 和 Content -MD5 来验证)
可以看到 HTTP 为传输报文定制了一系列相关的报文首部,这些首部平常在日常也相当常见,我们这次就来详细了解它们的功能以及如何运作的。
实体结构
1 | HTTP/1.0 200 OK |
上文可以看出,描述 HTTP 协议之后的内容就是实体部分,实体部分又分为两部分:实体首部和实体主体,他们之间由一个空白的 CRLF 行结束。
回车符(CR)和换行符(LF) 是文本文件用于标记换行的控制字符或字节码。
- CR = Carriage Return,回车符号(
\r
,十六进制 ascii 码为0x0D
,十进制 ascii 码为13
),用于将鼠标移动至行首,并不前前进至下一行。 - LF = Line Feed, 换行符号(
\n
,十六进制 ascii 码为0X0A
,十六进制码为10
)。
紧邻的 CR 和 LF (组成 CRLF ,\r\n
,或十六进制 0X0D0A
)将鼠标移动到下一行行首。(Windows 操作系统默认的文本换行符为 CRLF ;Linux 以及 macOS 系统默认使用 LF)
Content-Length
content-length 首部指示出报文中实体主体打字节大小。这个大小是包含了所有内容编码的,比如,对文文件进行了 gzip 压缩的话,content-Lenght 首部就是压缩后打大小,而不是原始大小。
除了使用了分块编码,否者 Content-Length 就是带有实体主体的报文必须使用的。使用 Content-Length 首部是为了能够检测出服务器崩溃而导致的报文截尾,并对共享持久连接的多个报文进行正确分段。
实体摘要
尽管 HTTP 通常都是在 TCP/IP 这样可靠传输协议之上实现的,但仍有很多因素导致报文的一部分在传输过程中被修改,比如不兼容的转码、代理有误等待呢个。为了检测主体的数据的完整性,发送方可以在生成初始的主体对主体运行 MD5 算法生成一个数据的校验和,通过 Content-MD5 首部发送给接收方。
除了检测完整性之外,MD5 还可以当做散列表的关键字,用来快速定位文档并消除不必要的重复内容存储。
媒体类型和字符集
Content-Type 首部字段说明了实体主体的 MIME 类型。 MIME 类型是标准化的名字,用以说明作为货物运载实体的基本媒体类型。客户端应用程序使用 MIME 类型来解释和处理其中内容。如果实体主体部分经过内容编码的话,Content-Type 首部说明的仍然是编码之前的实体主体部分
MIME 类型由一个住媒体类型(比如 text、image 或 audio 等)后面跟一条斜线以及一个子类型组成,子类型用于进一步描素媒体类型。下表中罗列一些常用的媒体类型
媒体类型 | 描述 |
---|---|
text/html | HTML 文档 |
text/plain | 纯文本文档 |
image/gif | GIF 图像 |
image/jpeg | JPEG 图像 |
audio/x-wav | WAV 格式声音数据 |
model/vrml | 三维 VRML 模型 |
application/vnd.ms-powerpoint | PPT 文档 |
multipart/byteranges | 实体主体有若干部分,每部分都包含了完整文档的不同的字节范围 |
message/http | 实体主体包含了完整的 HTTP 报文 |
多部分媒体类型
MIME 中的 multipart 电子邮件报文中包含多个报文,它们合在一起作为单一的复杂报文发送。每一部分都是独立的,有各自的描述其内容的集;不同部分之间用分界字符连接在一起。
HTTP 也支持多部分主体。不过,通常只用在下列两种情形之一:提交填写的表单,或是作为承载若干文档片段的范围响应。
下边我们就举例介绍它们的传输格式
多部分表格提交
当提交填写的 HTTP 表格时,边长的文本字段和上传的对象都作为多部分主体里面独立的部分发送,这样表格中就可以填写各种不同类型和长度的值。
HTTP 使用 Content-Type :multipart/from-data 或 Content-Type:multipart/mixed 这样的首部以及多部分主体来发送这种请求,举例如下:
1 | Content-Type:multipart/from-data ;boundary=AaB03x |
其中 boundary 参数说明了分割主体中不同部分所用的字符串
下面例子向我们展示了多表格提交的编码,假如我们有如下表格。
1 | <form |
我们在文本中输入 “Joke” 并选择了一个文本文件“essayfile.txt” ,那么它的主体构造可能是这个样子
1 | Content-Type : mutltipart/form-data;boundary=AaB03x |
如果我们还选了了另一个图像文件 “imagefile.gif”,那么构造的主体可能就这个样子了
1 | Content-Type:multipart/form-data;bundary=AaB03x |
多部分范围提交
HTTP 对范围请求的响应也可以使多部分的。这样的响应中有 Content-Type:mutipart/byteranges 首部和带有不同范围的多部分主体。