HTTP权威指南阅读笔记 第三部分识别,认证与安全(第十一章到第十四章)
目录:
11 客户端识别与Cookie机制
11.1 个性化接触
最开始HTTP协议是一个匿名,无状态的请求/响应协议,Server没有信息可以判断这个请求是那个Client发出,也无法记录来访Client的请求序列
- 个性化问候 对用户专门生成欢迎语和页面内容,使购物体验更个性
- 有的放矢的推荐 通过了解客户的兴趣,推荐认为客户感兴趣的商品,在客户临近生日或一些其他的日子的时候提供生日特定商品
- 管理信息的存档 不需要繁琐的填写地址等细节
- 记录会话 在交互的过程中构建增量状态,例如添加购物车,Web站点需要区分不同Client的HTTP事务
用户识别机制
- 承载用户身份信息的HTTP首部
- Client IP跟踪,通过用户的IP地址进行识别
- 用户登录,用认证的方式识别用户
- 胖URL,在URL中嵌入识别技术
- Cookie,一种功能强大且高效的持久身份识别技术
11.2 HTTP首部
可以承载用户相关信息的HTTP请求首部
- FROM 请求 用户的E-mail
- User-Agent 请求 用户的浏览器软件
- Referer 请求 用户是从这个页面的链接跳转过来的
- Authorization 请求用户名和密码
- Client-IP 扩展(请求) Client的IP地址
- X-Forwarded-For 扩展(请求) Client的IP地址
Cookie 扩展(请求) Server产生的ID标签
对于FORM首部,因为担心不讲道德的Server搜集这些E-mail地址,用于垃圾邮件散发,一般浏览器都不会进行FORM首部,一般爬虫会有FORM首部,方便网关进行投诉
- 对于User-Agent首部还可以将浏览器的相关信息告知Server,包括程序和版本,还可以包含操作系统相关信息,Server可以实现定制内容和特定的浏览器及其属性间的良好互操作性,这个首部是非常有用的
- 对于Referer首部,确实可以提供用来源页的URL,但是只能说明用户之前访问过那个页面
11.3 客户端IP地址
早期Server尝试将Client IP作为一种标识,不过在HTTP首部通常不提供Client IP,但是Server可以通过承载HTTP请求的TCP连接另一端的IP地址
例如Unix系统就可以通过getpeername来返回Client IP地址
status = getpeername(tcp_connection_socket, ...);
使用Client IP地址来识别用户有很多缺点
- Client IP描述的所用机器的IP地址,而不是用户,如果多个用户共用一个IP就不好分辨了
- 服务商可能在用户登录的时候动态分配IP地址
- 对于稀缺地址资源,很多用户都是通过网络地址转换(NAT)防火墙来浏览,NAT设备隐藏了实际的ClientIP,将实际的ClientIP地址转换为了一个共享的IP地址和不同的端口号
- 通过HTTP代理和网关连接到Server,Server获取的代理或网关IP,而不是实际的ClientIP,有些代理会添加特殊的Client-IP或X-Forwarded-For扩展首部来保存原始的ClientIP,但是并不是所有代理都支持这种行为
在因特网上IP地址可以伪造,或者拦截代理也会有破坏
11.4 用户登录
Server无需被动的根据用户IP地址来猜测,而是要求Client通过用户名和密码来进行显示的访问
HTTP包含内建机制,可以用WWW-Authenticate首部和Authorization首部向Server传送用户相关信息。
如果Server希望为用户提供对站点的访问之前,先进行登录,可以向浏览器发送一条HTTP相应为401 Login Required,然后浏览器会显示一个登录对话框,并用Authorization首部在对下一条对Server的请求提供这些信息
只要输入了用户名和密码,浏览器会重复原来的请求,并添加Authorization首部说明用户名和密码,对用户名和密码进行加密,方式被外泄。在请求要使用用户名和密码的时候,浏览器会将储存的值发送出去,在整个会话期间都可以维持这个身份。
不过对于登录多个站点是很繁琐的
11.5 胖URL
server会为每个用户生成特定版本的URL来追踪用户身份。
通常在URL路径开始或者结束的地方添加一些状态信息,用户浏览的时候会动态生成一些超链,继续维护URL中的一些状态信息,这种URL就是胖URL
Amazon.com中对胖URL的实例,标识码用于用于用户浏览商品时对内容进行跟踪。
胖URL的问题
- URL丑陋
- 无法共享URL
- 破坏公有缓存
- 逃逸口 在跳转过程中可能会丢失胖URL会话
- 会话是非持久的 用户退出登录后,信息丢失
11.6 cookie
cookie是识别当前用户,实现持久会话的最好方式。是由网景公司开发的。
cookie定义了一些新的HTTP首部。
11.6.1 cookie的类型
cookie分为两类,会话cookie和持久cookie。
- 会话cookie是一种临时cookie,记录用户偏好设置等,退出浏览器后就被删除了
- 持久cookie生存的时间较长,存储在硬盘上,计算机重启后仍然存在,用于维护配合和登录等,一般设置了Discard参数,或者没有设置Expires或Max-age参数来扩展过期时间
11.6.2 cookie是如何工作
当用户第一次访问cookie的时候,Server对cookie一无所知,但是Server希望在用户再次访问的时候可以识别出来这个用户。
cookies包含key: value
的形式,提供Set-Cookie或Set-Cookie2的HTTP扩展首部将其附加到用户上。
cookie可以包含任意值,通常包含一个Server为了跟踪而产生的独特的识别码,Server通过这个识别码查找用户的累积数据。
浏览器会记住从Server返回的cookie内容,并将cookie保存在浏览器cookie数据库中,当用户请求对应站点的时候会将对应cookie加入到首部中
11.6.3 cookie罐:客户端的状态
浏览器负责存储cookie,这个系统被称为客户端状态
不同的浏览器会以不同的方式存储cookie
示例网景的Navigator会将cookie存储在一个名为cookies.txt的文件中
- domain cookie的域
- allh 是域中所有主机都获取cookie还是只有指定了名字的主机获取
- path 域中与cookie相关的路径前缀
- secure 是否只有在SSL连接的时候发送这个cookie
- expiration cookie的过期秒数(从格林尼治标准时间1970年1月1日 0:00:00开始)
- name cookie的名称
- value cookie的值
微软的Internet Explore的cookie存储在高速缓存
11.6.4 不同站点使用不同的cookie
浏览器内部存储的cookie可能有成百上千个,浏览器不会将每个cookie都发送给所有站点,而是通常只会发送几个cookie
- cookie中包含Server特有名称的键值对,对大部分站点都是无效数据
- 将所有cookie发送会引发潜在的隐私问题
- 传输大量得cookie会严重降低性能,造成传输的cookie比主体还要大
浏览器只会向Server发送由该Server产生的cookie。
很多Server与第三方达成协议,由其来管理广告,因为cookie的原因,用户访问另一个Server站点,由于有该cookie,也可以获取到用户相关浏览信息,进行个性化的广告推荐等
11.6.4.1 cookie的域属性
server产生cookie时可以默认为Set-cookie响应首部添加一个Domain属性来控制那些站点可以看到该cookie
Set-cookie: user="mary17"; domain="whysdomain.com"
在请求任意whysdomain.com结尾的域名的时候,都会添加首部Cookie: user="mary17"
11.6.4.2 cookie路径属性
cookie规范甚至允许用户将cookie与部分Server站点关联,通过Path属性来实现,这样URL前缀下所有cookie都是有效的
Set-cookie: user="mary17"; domain="whysdomain.com"
Set-cookie: pref=compact; domain="whysdomain.com"; path=/autos/
对于访问whysdomain.com
的其他url的时候,还是Cookie: user="mary17"
,对于访问whysdomain.com/autos/
的url的时候,就会获得两个cookie
11.6.5 cookie成分
cookie的版本有版本0和版本1,但是都不是HTTP/1.1规范的一部分提供的
11.6.6 cookies版本0
由网景公司定义,版本0的cookie定义了Set-cookie响应首部和cookie请求首部,以及用户控制cookie的字段
Set-cookie首部与一个强制的cookie名和值,还有一些可选部分,中间由";"
分隔
Set-cookie属性 | 描述 | 实例 |
---|---|---|
NAME=VALUE | 强制,NAME和VALUE都是字符串,除非包含在双引号内,否则不包括分号,逗号,等号和空格,Server可任意创建 | Set-cookie: name=why |
Expires | 可选,这个属性定义一个日期字符串,用来定义cookie的实际生存周期,超过这个过期时间,就不再存储或发布这个cookie了。日期格式为Weekday, DD-MM-YY HH:MM:SS GMT ,唯一合法时区为GMT,如果没有指定Expires,则会在用户会话结束时过期 |
Set-cookie: name=why; expires=Wednesday, 09-Nov-99 23:12:40 GMT |
Domain | 可选,浏览器只向指定的域中的域名发送cookie,如果没有指定域,就默认为产生Set-cookie响应的域名 | Set-cookie: name=why; domain=blog.whysdomian.com |
Path | 可选,可以为Server特定的接口分配cookie,如果是一个URL前缀,就可以附加一个cookie,如果没有设置为产生Set-cookie响应的URL路径 | Set-cookie: name=why; path=/blog |
Secure | 可选,如果包含此属性,只有在HTTPS连接才会发送 | Set-cookie: name=why; secure |
在浏览器发送请求的时候,会把所有与域,路径,安全过滤器相匹配的未过期Cookie都发送给这个Server,所有cookie被合并在一个Cookie首部
Cookie: name=why; host=whysdomain
11.6.7 cookies版本1
这个版本引入了Set-cookie2首部和Cookie2首部,也能与cookies版本0进行互相操作
Set-cookie2属性 | 描述 | 实例 |
---|---|---|
NAME=VALUE | 强制,"$" 是保留字符,NAME不能以其开头 |
- |
Version | 强制,是一个整数,为RFC2965为1 | Set-cookie1: name=why; Version="1" |
Comment | 可选,说明Server准备如何使用这个cookie,用户可以通过检查策略来确定是否允许使用带有这个cookie的会话,这个值必须采用utf-8编码 | - |
CommentURL | 可选,提供了一个URL指针,指向描述cookie目的和策略的文档 | - |
Discard | 可选,如果提供了这个属性,就会在Client程序终止时,销毁这个cookie | - |
Domain | 可选,和版本0一样,有一些附加限制,看考书上 | - |
Max-Age | 可选,这个值是一个整数,用秒为单位的cookie生存期,浏览器根据规则计算cookie的使用期,如果大于Max-Age,则丢弃 | - |
Path | 可选,和版本0一样 | |
Port | 可选,可以应用cookie的端口列表,如果有端口列表,就只能向与列表中端口匹配的Server提供cookie,如果单独提供关键字而没有值,就只能向当前Server的端口提供cookie | Set-cookie1: name=why; Port="80, 81, 8080" |
Secure | 可选,和版本0一样 |
对于响应
Set-cookie2: ID="29046"; Domain=".whysdomain.com"; Version="1";
Set-cookie2: color="blue"; Version="1";
Set-cookie2: Conpon="hammer027"; Path="/tools"; Version="1";
Set-cookie2: Conpon="handvac103"; Path="/tools/cordless"; Version="1";
发送的Cookie为
Cookie: $Version="1";
ID="29046"; $Domain=".whysdomain.com";
color="blue";
Conpon="hammer027"; $Path="/tools";
Conpon="handvac103"; $Path="/tools/cordless";
都是由保留关键字传输的Set-cookie2关键字
版本1的Cookie2首部和版本协商
Cookie2请求首部能够理解不同cookie规范版本的Client和Server之间互操作性的协商,Cookie2通过Cookie2: $Version="1"
来声明版本
如果Server理解新形式的cookie就能识别出Cookie2首部,并在响应首部中发送Set-Cookie2首部而不是Set-Cookie,如果Client在响应中获得了Set-Cookie和Set-Cookie2首部,就会忽略Set-Cookie首部
Client如果即支持版本0和1的cookie,但是从Server获取到的是版本0的首部,就会带着Cookie首部去请求,但是还是会带着Cookie2首部来声明版本,告诉Server可以升级
11.6.8 cookie与会话追踪
cookie可以用于Server进行事务处理的时候对用户进行跟踪
11.6.9 cookie与缓存
缓存那些与cookie事务有关的文档时,可能不希望用户私人的数据被其他人看到
- 如果无法缓存文档,要将其标识出来 如果除Set-Cookie首部之外文档是可以缓存,就使用
Cache-Control: no-cache="Set-Cookie"
,另一种更通用的做法是为可缓存文档使用Cache-Control: public
,可以节省些带宽 - 缓存Set-Cookie首部时要小心 如果有Set-Cookie首部,就可以对主体进行缓存,但是如果发送多个Cookie,而破坏用户定位。如果Cache在响应之前将Set-Cookie首部删除,对于正常的请求会获取到Cookie的,而Cache没有提供Cookie,导致Client没有cookie。强制缓存与Server重新验证每条请求,并将返回所有Set-Cookie首部合并到响应中,就可以改善这种情况,Server可以提供以下首部
Cache-Control: must-revalidate, max-age=0
- 小心处理带有Cookie首部的请求 带有Cookie首部请求到达的时候,就代表结果可能是私有的,一定要私有内容的内容标识为不可缓存。有些响应文档对应携带Cookie首部的请求,保存的缓存可能会选择不去缓存这些响应文档,同样有些缓存带有cookie首部的图片,将过期时间设置为0,强制每次都进行验证
其实最好是对于这些cookie的不走缓存,只对静态文件进行缓存
11.6.10 cookie,安全性和隐私
cookie是可以禁止的,而且可以通过日志分析或其他的方式来实现大部分来跟踪记录。
可以通过匿名cookie作为键值,而在Server端存储对应用户的数据,降低Client到Server过程中敏感数据传送
12 基本认证机制
最常见的HTTP认证就是基础认证,而功能性更强的就是摘要认证(digest authentication)
12.1 HTTP的质询/响应认证框架
12.1.1 认证
HTTP提供了原生的质询/响应(challenge/response)框架,简化了对用户认证的流程
Server在接收到请求的时候,没有指定对应的操作,而是一个认证质询进行响应,要求用户提供一些保密信息来说明他是谁,而进行质询
用户再次发起请求的时候,需要附上保密整数(用户名和密码),如果证书不匹配,Server再度质询Client,或产生错误信息,如果证书匹配,就完成正常的请求
12.1.2 认证协议与首部
HTTP通过可定制的控制首部,为不同的认证协议提供了一个可扩展框架,认证协议也是通过首部来指定的
HTTP定义了两种官方的认证协议: 基本认证和摘要认证
认证的四个步骤
- 请求 第一条请求没有认证信息 GET请求
- 质询 首部WWW-Authenticate Server用401状态拒绝了请求,说明需要用户名和密码,Server上可以分为不同区域,可能有不同的密码,首部也定义指定认证算法,
401 Unauthorized
- 授权 Authorization Client重新发起请求,但是这一次会附加一个Authorization首部,用来说明认证算法,用户名和密码 GET请求
- 成功 Authentication-Info 如果授权证书是正确的,则将文档返回,有些授权算法可能会在可选的Authentication-Info首部返回一些与授权相关的附加信息
示例质询
HTTP/1.1 401 Authorization Required
WWW-Authenticate: Basic realm="Family"
示例授权
GET /family/jeff.jpg HTTP/1.0
Authorization: Basic YnJpYW4tdG90dHk6T3ch
12.1.3 安全域
示例质询中有一个realm指令,Server将受保护文档组织成了一个安全域,每个安全域可以有不同的授权用户级
安全域更多是帮助用户使用那个用户名和密码
12.2 基本认证
12.2.1 基本认证实例
和上边的流程一样,在发送用户名和密码的过程中,浏览器通过冒号的形式将其连接,编码成“经过扰码的”Base-64表现形式,示例授权的YnJpYW4tdG90dHk6T3ch
就是编码后的用户名:密码
,然后放在Authorization首部中回送,而Server则是解密获取用户名和密码
12.2.2 Base-64用户名/密码编码
Base-64编码会将一个8位字节序列划分为一些6位的块,用每个6位的块在一个特殊的由64个字符组成的字母表中选择一个字符,这个字母表包含了大部分字母和数字。
有些用户名和密码会包含国际字符或其他在HTTP首部中非法字符,Base-64编码就可以解决这个问题,Base-64编码可以接受二进制字符串,文本,国际字符等数据。
12.2.3 代理认证
代理也可以认证功能,但是首部和状态码不同而已
Server | Proxy |
---|---|
Unauthorized status code: 401 | Unauthorized status code: 407 |
WWW-Authenticate | Proxy-Authenticate |
Authorization | Proxy-Authorization |
Authentication-Info | Proxy-Authentication-Info |
12.3 基本认证的安全缺陷
基本认证简单,便捷,但是并不安全,只能放置非恶意用户无意间访问,或将其与SSL这样的加密技术配合使用
- 用户名和密码虽然经过加密,但是都可以通过Base-64解码获得,甚至可以手工解码
- 即使编码不易解开,第三方可以直接使用被编码的用户名和密码进行请求
- 这些密码被解很有可能造成其他网站密码泄露
- 基本认证没有提供任何针对代理作为中间人的中间节点的防护措施,没有修改认证,但是修改了报文的其他部分
- 假冒服务器
13 摘要认证
13.1 摘要认证的改进
- 永远不会以明文方式在网络上发送密码
- 可以防止恶意用户捕捉并重放认证的握手过程
- 可以选择性的防止对报文内容的篡改
- 防范其他几种常见的攻击方式
摘要认证并不是最安全的协议,摘要认证并没有被广泛的使用
13.1.1 用摘要保护密码
Client发送的不是密码,而是一个密码摘要,Server可以验证提供的摘要是否与密码匹配
说实话没懂
13.1.2 单向摘要
摘要是对信息主体的浓缩,是一种单向函数,主要用于将无限的输入值转换为有限的浓缩输出值,常见摘要有MD5,会将任意字节长度的字节序列转化为一个128位的摘要
MD5输出的128位摘要通常会被写成32个16进制的字符,每个字符代表4位
单向摘要也被称为加密的校验和,单向散列函数,指纹函数
13.1.3 用随机数防止重放攻击
使用单向摘要就可以无需发送明文密码了,只需要发送密码的摘要。
但是截获到摘要,并发送给Server和密码一样好用,为了防止这种情况,Server还会给Client发送一个随机数,这种数会经常变化,Client计算摘要之前,先将这个随机数附到密码上。
随机数是通过WWW-Authenticate质询中从Server发送给Client。
示例首部
HTTP/1.1 401 Unanthorized
WWW-Authenticate: Disget realm="Shopping Cart" qop="auth, auth-int" nonce="66C4EF58DA7CB956BD04233FBB64EOA4"
13.1.4 摘要认证的握手机制
- Server计算出随机数
- 将这个随机数放到WWW-Authenticate质询报文中,与Server支持算法一并发送给Server
- Client选择一个自己支持的算法,计算出密码和其他数据的摘要
- 将摘要放在一条Authorization报文返回给服务器,如果Client要对Server进行认证,可以发送Client随机数
- Server收到摘要,选中的算法以及支撑数据,计算出与Client相同的摘要,然后Server将与本地生成的摘要和网络传输过来的进行对比,验证是否匹配,如果Client要求了用Client随机数进行质询,会创建Client摘要
13.2 摘要的计算
13.2.1 摘要算法的输入数据
- 由单向散列函数H(d)和摘要KD(s,d)组成的一对函数,其中s为密码,d表示数据
- 一个包含安全信息的数据块,包括密码,称为A1
- 一个包含请求报文中非保密属性的数据块,称为A2
H和KD处理数据块A1和A2,产生摘要
13.2.2 算法H(d)和KD(s,d)
摘要认证支持各种摘要算法,RFC 2617中建议使用MD5和MD5-sess,如果没有指定默认使用MD5
H(<data>) = MD5(<data>)
KD(<secret>, <data>) = H(concatenate(<secret>:<data>))
13.2.3 与数据安全性相关的数据(A1)
A1数据块是密码和受保护信息的产物,包含用户名,密码,保护域和随机数等内容,并且A1会与H,KD和A2一同用于摘要计算
- MD5 为每条请求运行单向散列函数,A1是由冒号连接起来的用户名,域和密码三元组
A1=<user>:<realm>:<password>
- MD5-sess 只在第一次WWW-Authenticate握手时运行一次散列函数,对用户名,域和密码进行一次CPU密集型散列,并将其放在当前随机数和Client随机数之前
A1=MD5(<user>:<realm>:<password>):<nonce>:<cnonce>
13.2.4 与报文有关的数据(A2)
A2数据块是与报文自身有关的信息,比如URL,请求方法和报文实体的主体部分,A2有助于防止方法,资源或报文被篡改。A2会与H,KD和A1一起用于摘要计算
RFC 2617根据所选择的保护质量(qop),与A2定义两种策略
- 第一种策略只包含HTTP请求方法和URL,当qop="auth"时使用这种策略,这是默认的情况
- 第二种策略添加了报文实体的主体部分,以提供一定程序的报文完整性检测,qop="auth-int"
对于未定义和auth的qop策略,A2=<request-method>:<url-directive-value>
,而qop="auth-int",A2=<request-method>:<url-directive-value>:H(<request-entity-body>)
request-method是请求方法,url-direcative-value是请求的URL,可能是"*",absoluteURL或者abs_path,但是它必须与请求的URL一致。如果请求URL是absoluteURL,他必须是个绝对URL
13.2.5 摘要算法汇总
RFC 2617 定义了两种给定了H,KD,A1和A2之后计算摘要的方式
- 第一种方式要与老规范RFC 2069兼容,在没有qop选项的时候使用,它是用保密信息和随机报文数据的散列值来计算摘要的
- 第二种方式是现在推荐使用的方式——这种方式包含了对随机数计算和对称认证的支持,只要是qop为auth或auth-int就要使用这种方式,他向摘要添加了随机数,需要像摘要加入随机计数,qop和cnonce数据
13.2.6 摘要认证会话
在Client收到一条保护空间的Server发送的WWW-Authenticate质询之前,认证会话会一直持续,Client会记录用户名,密码,随机数,随机数计数等等,用来以后构建Authorization首部的时候使用。当随机数过期的时候,老的Authorization中的首部不再新鲜,Server可以接受旧的数据,也可以返回一个新的随机数的401响应,让Client重试,指定响应首部stale=true,告诉Client用新的随机数重试
13.2.7 预授权
在认证过程可能会出现每次请求都进行请求/质询的循环,如果Client可以提前知道下一个随机数,就可以取消这个循环,在Client请求时直接,直接生成Authorization首部去请求。进而减少了请求的次数和报文数量
三种可选方式
- Server预先在Authentication-Info成功的首部中发送下一个随机数
- Server允许一小段时间内使用上一个随机数
- Client和Server使用同步,可预算的随机数生成算法
预先生成下一个随机数
Server响应首部为
Authentication-Info: nextnonce="<nonce-value>"
但是这样响应一定要在下一次请求之前到达才行,让Client获取到随机数,否则可能会造成更大的损失
受限的随机数重用
Server可以允许这个随机数重用5次或者10次
在这种情况,Client可以随意发布带有Authoration首部请求,但是由于随机数是实现知道的,还可以进行管道化请求。随机数过期的时候,Server直接再度给Client发送401 Unauthorized质询,并设置WWW-Authenticate: stale=true命令
重用随机数,还是会造成重放攻击的
同步生成随机数
可以采用时间同步的随机数生成算法,Client和Server可以根据共享的密钥,进而生成无法预测的相同的随机数序列
13.2.8 随机数的选择
略
13.2.9 对称认证
略
13.3 增强保护质量
13.3.1 报文完整性保护
使用完整性保护就是使用的qop="auth-int"
13.3.2 摘要认证首部
- 质询部分
对于基本认证
WWW-Authenticate: Basic realm="<realm-value>"
对于摘要认证
WWW-Anthenticate: Digest realm="<realm-value>" nonce="<nonce-value>" [domain="<list-of-URIs>"] [opaque="<opaque-token-value>"] [stale=<true-or-false>] [algorithm="<digest-algorithm>"] [qop="<list-of-qop-values>"] [<extension-directive>]
- 响应部分
对于基本认证
Authorization: Basic <base(user:pass)>
对于摘要认证
Authorization: Digest username="<username>" realm="<realm-value>" nonce="<nonce-value>" uri=<request-uri> response="<32-hex-dig-digest>" [algorithm=<digest-algorithm>] [opaque="<opaque-token-value>"] [qop=<qop-value>] [nc=<8-hex-digit-nonce-count>] [<extension-direcative>]
- Info
对于摘要认证
Authentication-Info: nextnonce="<nonce-value>" [qop="<list-of-qop-values>"] [rspauth="hex-digest"] [cnonce="<nonce-value>"] [nc=<8-hex-digit-nonce-count>]
13.4 应该考虑的实际问题
13.4.1 多重质疑
对于Server不了解Client的能力,就会提供既支持基本认证,又支持摘要认证的多重质疑,如果浏览器只支持基础认证,但是这会存在安全问题
13.4.2 差错处理
在摘要认证上,如果指令或其值使用不当,或者缺少某个指令,就应该使用响应400 Bad Request。
如果请求的摘要不匹配,就应该记录一次登录失败,在某Client连续失败多次就证明攻击者在猜测密码
认证Server要确保URI指令的资源和请求行中指定的资源相同,如果不同应该返回400 Bad Request错误,因为这可能是一种攻击
13.4.3 保护空间
域值,与被保护Server的标准根URL结合在一起,定义保护空间。
通过域,可以将Server上受保护资源划分为一组保护空间,每个空间都有自己的认证机制和授权数据库,域值是一个字符串,通过原始服务器分配
对于基本认证,是URL下所有资源,而摘要认证则是domain字段下的
13.4.4 重写URL
代理可以改变URL的语法,而不改变描述实际资源的方式重写URL,所以可能会造成摘要认证失败
13.4.5 缓存
共享的缓存收到包含Authrization首部的请求和转接那条请求产生的响应时,除非响应中列出了以下Cache-Control首部指令之一,否则一定不能作为其他请求的应答。
- 如果原始响应中包含Cache-Crontrol指令must-revalidate,Cache可以在应答后继请求的时候使用那条响应的实体部分,但是Cache需要用新的请求首部与Server进行再验证
- 如果原始响应中包含Cache-Crontrol指令public,在任意后继请求的应答中都可以返回响应的实体
13.5 安全性考虑
13.5.1 首部篡改
略
13.5.2 重放攻击
随机数就是为了防止重放攻击的
13.5.3 多重认证机制
略
13.5.4 词典攻击
更多的是对密码猜测型攻击
13.5.5 恶意代理攻击和中间人攻击
代理更多可以进行对流量中数据包的再编译等等,进行篡改,SSL是一种很有效的方式
13.5.6 选择明文攻击
如果代理可以获取到传递的随机数,就可以用已知的秘钥来计算响应可以简化响应的密码分析过程,被称为选择明文攻击
- 预先计算的词典攻击 预先创建确定的随机数和常见密码进行访问
- 批量暴力型攻击 枚举了指定空间可能存在的密码进行访问
防止这些攻击就可以配置Client可选的cnonce指令,响应就是基于Client的判断产生的,而不是Server提供的随机数,以及一个好的密码过期策略,可以消除选择明文攻击
13.5.7 存储密码
保护好存储摘要认证的文件,防止被入侵获取
14 安全HTTP
14.1 保护HTTP的安全
HTTPS是HTTP和数字加密技术结合,为HTTP的安全版本,有着高效,可移植,利用管理的优点。
- Server认证 Client可以知道是与Server通信而不是伪造的Server
- Client认证 Server可以知道是与Client通信而不是伪造的Client
- 完整性 Client和Server的数据不会被修改
- 加密 Client和Server之间对话是私密的,不会被窃听
- 效率 一个运行的足够快的算法,以便低端的Client和Server使用
- 普适性 基本所有Client和Server都支持这些协议
- 管理的可扩展性 在任何地方的任何人都可以立刻进行安全通信
- 适应性 能够支持当前最知名的安全方法
- 在社会的可行性
HTTPS由网景公司首创,URL以https://
开头。
在使用HTTPS时,所有的HTTP请求和响应数据再发送之前都是需要进行加密的,可以理解为HTTPS在HTTP层下提供了加密层,使用的SSL或TSL协议
14.2 数字加密
14.2.1 密码编制的机制与技巧
略
14.2.2 密码
密码是一种编码方案,一种特殊的报文编码方式和一种稍后使用相应解码方式的结合体,加密之前被称为明文,而加密之后被称为密文
14.2.3 密码机
通过进行编码和解码的机器,比人工更不易破解
14.2.4 使用了密钥的密码
使用不同的秘钥对密码进行编码和解码,防止密码机被盗或者人工泄露编码方式
14.2.5 数字密钥
- 更复杂的编解码算法称为了可能
- 支持超大的秘钥成为了可能
14.3 对称秘钥加密技术
很多的数字秘钥都使用的对称秘钥加密技术,编码和解码的时候使用秘钥是相同的。
在对称加密技术中,发送端和接收端要共享密钥才能进行通信,发送端通过共享的密钥进行加密报文,并将得到的密文发送给接收端,接收端收到密文并用对应的解密函数和共享秘钥解密
流行的对称加密算法,DES、Triple-DES、RC2和RC4
14.3.1 密码长度和枚举攻击
编解码方式是众所周知的,不同的就是密钥。
好的加密算法会迫使攻击者试遍每个密钥,才能破解,用暴力破解所有密钥值称为枚举攻击,如果密钥数量少就可能会被攻破。
密钥的长度取决于密钥中的位数,以及密钥中有多少是有效的,对称密钥加密算法来说,通常所有密钥都是有效的,40位密钥可以有2的40次方,一万亿个密钥。
对于小型或者不太重要的事务,40位足够安全了,128位被认为是非常强大的。
14.3.2 建立共享秘钥
对称加密技术的缺点之一就是发送者和接收者在互相对话之前一定要有一个共享密钥。
但是每个通信实体都需要有自身的秘钥,需要管理的秘钥非常之多为N的平方
14.4 公开密钥加密技术
公开密钥加密技术使用了两个非对称密钥,一个进行主机报文编码,一个用于主机报文解码,编码密钥是众所周知的,每个主机都能获取,密钥的建立变得简单,但是解密的密钥是保密的,因此只有接收端才能进行解码
14.4.1 RSA
所有公开密钥非对称加密系统所面临的共同挑战,要保证有所有的线索也不能计算出私有密钥
- 公有密钥
- 拦截下的密文
- 一条报文和与之相关的密文
RSA算法就是一个满足了这些条件的公开密钥加密算法
14.4.2 混合加密系统和会话密钥
公开密钥的算法计算可能会慢一些,实际上我们是使用了对称和非对称策略,通常做法是在两点间先建立公开密钥加密算法的安全通信,然后用这条通信发送临时的对称密钥对,通过更快的对称加密技术去其余数据进行加密。
14.5 数字签名
除了使用对称加密或者非对称加密对密码进行加密之外,还可以用加密系统对报文进行签名,说明是谁写的报文,同时证明报文未被篡改,被称为数字签名
签名是加密的校验和
- 签名可以证明作者编写了这条报文,只有作者有私钥,因此只有作者能计算出校验和
- 签名可以防止报文被篡改,如果被修改之后校验和就会发生变化
数字签名使用非对称加密产生只有所有者才能知道私
- 节点A将边长报文提取为边长的摘要
- 节点A对摘要采用了一个签名函数,使用私有秘钥作为参数,只有用户有私钥才能进行解开
- 节点A将签名附在报文尾部,将报文和签名都发送给B
- 节点B接收到报文确定确实是A发送并没有被篡改,就可以对签名通过公钥和反函数,解包之后的摘要和B的摘要版本不一致,就认为被篡改了
14.6 数字证书
14.6.1 证书的主要内容
- 对象的名称(人,服务器,机构组织)
- 过期时间
- 证书发布者
- 证书发布者的数字证书
- 对象的公钥
- 对象所使用签名算法的描述信息
14.6.2 X.509 v3证书
现在使用的大多数证书都是X.509 v3证书
字段 | 描述 |
---|---|
版本 | 这个X.509证书版本号,现在通用的是v3 |
序列号 | 证书颁发机构CA生成的唯一整数,CA生成的每一个证书都要有一个唯一的序列号 |
签名算法ID | 签名所使用的加密算法,例如RSA加密的MD2摘要 |
证书颁发者 | 发布和签署这个证书的组织名称,以X.500格式表示 |
有效期 | 由一个起始日期和一个结束日期表示 |
对象名称 | 用于描述实体,以X.500格式表示 |
对象公开的密钥信息 | 证书对象的公开密钥,公开密钥使用的算法,以及所有附加参数 |
发布者唯一ID(可选) | 可以重用相同的发布者名称? |
对象唯一的ID(可选) | 可以重用相同的对象名称? |
扩展 | 在v3或更高的版本使用 |
证书颁发机构签名 | 证书颁发机构用指定签名算法对上述字段进行数字签名 |
对于扩展分关键扩展和非关键扩展,如果证书使用者无法识别出关键扩展字段,就必须拒绝该证书,常用扩展字段包括
- 基本约束 对象和证书颁发机构的关系
- 证书策略 授予证书的策略
- 密钥的使用 对公开密钥的使用策略
基于X.509证书的签名有好多种,包括WebServer证书,Client邮件证书,软件代码签名证书和机构颁发机构证书
14.6.3 用证书对服务器进行认证
通过HTTPS建立起一个安全的web事务之后,浏览器都会自动获取所连接Server的数字证书,如果Server没有证书则连接失败。
证书包括Server的名称,主机名,公开密钥,签名颁发机构,来自签名颁发机构的签名
浏览器收到证书会对签名颁发机构进行检查,如果机构是权威的,浏览器就可以知道其公开秘钥了,也就可以验证签名。如果一无所知,则像用户确定是否信任
14.7 HTTS——细节介绍
14.7.1 HTTPS概述
HTTPS是在安全的传输层上发送的HTTP,HTTPS没有将未加密的HTTP报文发送给TCP,并通过因特网传输,而是在HTTP发送之前,先将其发送给一个安全层,进行加密。
HTTPS的安全层是通过SSL协议或者其现代替代协议TLS协议实现。
14.7.2 HTTPS方案
安全HTTP是可选的,对于安全HTTP使用的URL前缀为https://
当Client请求一个http Server的时候,默认情况会打开Server的80端口,而请求一个https Server的时候,默认会打开Server的443端口,然后与Server握手,以二进制格式与Server交换一些SSL安全参数,附上加密的HTTP命令
SSL是个二进制协议,与HTTP不同,其流量是承载在另一个端口,SSL通常使用443端口,如果SSL和HTTP都使用80端口,Server会把二进制的SSL流量认为为错误的HTTP而关闭连接,将安全服务进一步整合到HTTP层中就无需使用多个目的端口了。
14.7.3 建立安全传输
在未加密HTTP中建立了TCP连接就可以进行发送报文等操作了。
HTTP在TCP连接建立之后,会再初始化SSL层,对加密参数进行沟通,并交换密钥,握手完成后,SSL初始化完成,Client将请求报文发送给安全层。
14.7.4 SSL握手
SSL握手主要完成的是
- 交换协议版本号
- 选择一个两端都了解的密码
- 对两端身份进行认证
- 生成临时的会话密钥,以便加密通信
14.7.5 服务器证书
SSL支持双向认证,将Server承载回Client,再讲Client证书发送给Server,但是基本没有Client证书发送给Server。
14.7.6 站点证书的有效性
浏览器会对证书做简单的完整性检测,一般包含
- 日期检测
- 签名颁发机构可信度检测
- 签名检测 对签名使用签名机构的公开密钥,并将其与校验码比较
- 站点身份检测 检测证书中的域名和对话Server的域名是否匹配
14.7.7 虚拟主机与证书
对于虚拟主机,处理SSL流量会很棘手,一般会HTTP域名采取重定向到一个配置HTTPS的站点
14.8 HTTPS客户端实例
14.8.1 OpenSSL
OpenSSL是SSL和TLS的开源实现,并且是一个全功能的通用加密库
14.8.2 简单的HTTPS客户端
略,介绍的用C编写的HTTPS Client
14.8.3 执行OpenSSL客户端
略
14.9 通过代理以隧道形式传输安全流量
Client通常会通过代理的方式来访问Web服务器,但是Client使用HTTPS对数据进行加密,代理是无法正常的读取HTTP首部的,也就无法知道请求转发到何处。
所以会启动HTTPS隧道,Client需要告知代理想要连接的主机和端口,这是在加密前用明文的方式告知的,所以代理可以理解。HTTP通过CONNECT的扩展方法发送明文形式的端点信息,CONNECT告知代理打开一条到目的主机和端口的连接,直接在Client和Proxy,Server之间以隧道的形式传输数据
CONNECT www.whysdomain.com:443 HTTP/1.1
User-agent: Mozilla/1.1N
<raw SSL-encrypyed data would follow here...>
请求中的空行之后,代理会对响应进行评估,确保是有效的,且用户有权请求这样一条连接,如果一切正常,会建立到目标服务器的连接,如果连接成功,返回200 Connect Estanblished响应
HTTP/1.1 200 Connect estanblish
Proxy-agent: Netspape-Proxy/1.1
如果建立握手成功完成就能传输SSL数据了,更多参考8.5