1. HTTP基础
http是HyperText Transfer Protocol 的缩写,中文称为超文本传输协议,是基于TCP/IP的一种应用层通信协议。
一次http通信过程,可以理解为“一问一答”的过程。客户端主动发起http请求,服务端给出应答,这样就完成了一次通信。http协议本身是无状态的。举例来说,客户端C对服务端S说:“你好,我是C!”,服务端S对客户端C说,“你好C!”。这样就完成了一次http通信,下一次,C对S说:“你好,今天心情怎么样?”,S对C说:“你好,请问你是谁?”。之所以如此,是因为S并没有因为上次和C向他做过自我介绍,而记住C。即从http协议的角度来说,两次通信之间没有任何关联性,是完全独立的。但是有的人可能会说,“这不对啊,我在网页上登陆网上商城,只登陆一次就可以了,我在各个页面间跳转都知道我是谁。”这是因为当用户登陆时,服务端和客户端建立了session,或者颁发了token作为用户的身份凭据,接下来的每次http请求,都会携带sessionid或者token来告诉服务端,我是谁。这是在http协议之上约定的相互识别方法,http协议本身是无状态的。
1.1 URL
url,统一资源定位符。http的URL格式例子:
http://www.example.com:80/dir/index.html?uid=1#hash2
● http,协议类型。
● www.example.com,服务器地址,也可以使用IP地址,必选项
● 80,端口号,不输入时,默认端口号,可选项
● dir/index.html,带层次的文件路径,指定服务器上的文件路径来定位特指的资源
● ?uid=1,查询字符串,可选
● #hash2 -> :片段标识符,hash值,可做锚点,可做前段路由,可选。在http请求时,它并不会发送到服务端。
1.2 Http报文
Http报文是由多行数据构成的字符串文本(ascii码,所以http叫Hyper Text Transfer Protocol)。Http报文大致可分为报文首部和报文主体两块,由空行(CR+LF)来划分。报文主体可以没有。
请求报文结构:
- 报文首部
1.1) 请求行:包含用户请求的方法,请求URI和HTTP版本。例如,GET /index.html HTTP/1.1
1.2) 请求首部字段
1.3) 通用首部字段
1.4) 实体首部字段
1.5) 其他 - 空行(CR + LF)
- 报文主体(HttpBody)
响应报文结构: - 报文首部
1.1) 状态行:包含表明响应结果的状态码,原因短语和HTTP版本
1.2) 响应首部字段
1.3) 通用首部字段:
1.4) 实体首部字段
1.5) 其他 - 空行(CR + LF)
- 报文主体
1.3 请求方法
动词 | REST用法 | 说明 |
---|---|---|
GET | 获取资源 | GET方法的请求不支持HttpBody传参,参数可以在URL中或者HEAD中。 |
POST | 新建或更新资源,以及具有较多数据需要发送到服务器,而含义更准确的动词却因无Http Body或其它原因受限的情况 | POST方法的请求支持HttpBody传参。参数在URL,HEAD,HttpBody中均可。使用HttpBody时要注意设置请求的Content-Type。 |
PUT | 更新资源,客户端提供了完整的对象信息。 | |
DELETE | 删除资源 | DELETE请求方法是有HttpBody的,但主流框架和阿里云API网关都不支持DELETE方法的HttpBody传参。所以当通过指定多个id批量删除时,应该用POST来代替,同时URL的path中应该包含"delete"这个词(一般放在URL Path的最后)来表示这是要删除对象。这一点是不符合RESTful风格的。 |
HEAD (使用较少) | 存在性判定,返回布尔型的情况。 | 这种方式的请求的应答是没有Http Body的,主要用它的状态码。200表示对象存在,404表示对象不存在。 |
OPTIONS (使用很少) | 1、获取服务器支持的HTTP请求方法;2、用来检查服务器的性能。 (微服务中未见使用) | |
PATCH | 更新资源,客户端提供了部分发生改变的属性。 | |
CONNECT(使用很少) | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。要求使用隧道协议连接代理(微服务中未见使用) | |
TRACE(使用很少) | 回显服务器收到的请求,主要用于测试或诊断。追踪路径。(微服务中未见使用) |
1.4
Http响应状态码是服务端对客户端请求响应的回应,它从大体上告知客户端,此次响应的类型(例如是正常、客户端请求错误,服务端出错,还是要求重定向等),以指导客户端该做出何种动作。状态码是允许自定义的,只要客户端能识别并按服务端期望进行处理即可。
类别 | 信息 | 原因短语 |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作已完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务端错误状态码) | 服务器处理请求出错 |
1.4.1 1XX继续
状态码 | 信息 | 表示 |
---|---|---|
100 | Continue | 客户端应当继续发送请求 |
101 | Switching Protocols | 服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求 |
102 | Processing | 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行 |
1.4.2 2XX成功
状态码 | 信息 | 表示 |
---|---|---|
200 | OK | 从客户端发来的请求在服务端正常处理 |
204 | No Content | 服务端接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分,另外也不允许返回任何实体的主体 |
206 | Partial Content | 客户端进行了范围请求, 而服务器成功执行了这部分请求,响应报文中包含由Content—Range 指定的范围的实体内容 |
1.4.3 3XX 重定向
状态码 | 信息 | 表示 |
---|---|---|
301 | Moved Permanently | 永久性重定向 |
302 | Found | 临时性重定向 |
303 | See Other | 由于对应的资源存在着另一个uri,应使用GET方法定向获取请求的资源 |
304 | Not Modified | 客户端发送附带条件的请求,也就是资源找到了,但是没有符合条件请求 |
307 | Tempoeary Redirect | 临时重定向,和302很相似,但是会根据游览器不同导致出现不同问题 |
注意:当301、302、303响应状态码返回时,几乎所有的游览器都会把POST改为GET,并删除请求报文内的主体,之后请求会自动再次发送。301、302标准是禁止将POST改为GET方法,但实际使用时都会改变
1.4.4 4XX 客户端错误
4XX 的响应结果表明发生了错误,其原因来自于客户端。
状态码 | 信息 | 表示 |
---|---|---|
400 | Bad Request | 请求报文中存在语法错误:游览器会像200 OK 一样对待改状态码 |
401 | Unauthorized | 发送的请求需要通过http认证(BASIC认证、DIGEST认证)的认证信息,如果前面已经请求过一次,证明认证失败 |
403 | Forbidden | 请求资源的访问被服务器拒绝 |
404 | Not Found | 服务器无法找到请求的资源 |
1.4.5 5XX 服务器错误
状态码 | 信息 | 表示 |
---|---|---|
500 | Internal Server Error | 服务器端知悉请求时发生错误 |
503 | Service Unavailable | 服务器暂时处于超负载或正在进行停机维护,现在无法处理请求 |
1.5 常用首部字段
- Cache-Control,通用首部字段,主要用来告知客户端、服务端或者缓存服务器,该采用何种缓存策略。
- Date,通用首部字段,创建HTTP报文的日期时间。
- Accept,请求首部字段,期望服务端返回的MediaType
- Content-Length,实体首部字段,说明HttpBody的大小(字节数)。
- Content-Type,实体首部字段,说明HttyBody的MediaType。
- Content-MD5,实体首部字段,是HttpBody的MD5计算值。不总是设置。在http协议下,单纯依靠Content-MD5并不能防止恶意篡改HttpBody的内容,因为Content-MD5也可能被篡改。在后面的文档中,我们会提到在数字签名摘要计算过程中,把Content-MD5也加入到计算过程,可以检测出Content-MD5是否被恶意篡改,从而可以通过对HttpBody计算MD5,检测HttpBody是否被篡改。
1.6 Cookie
前面我们说过,http协议是无状态的。服务端为了识别当前是“谁”在访问,并可取得和“谁”相关的一个上下文,会话(session)机制就是方法之一。通常情况下,我们将会话的标识存储在cookie里面。Cookie虽然没有在http协议的RFC标准中定义,但它却是web客户端和服务端事实上都实现的机制。
在浏览器每次发起http请求时,总是会在cookie的硬盘存储文件和浏览器进程的内存中寻找此网站域名相关的cookie,如果有且没有过期,将在Http请求的header中的Cookie字段上设置上此cookie。服务端如果支持会话和cookie,当发现客户端没有Cookie信息或者Cookie信息过期时,可以在http响应报文中附加上新的Cookie,浏览器会更新此域名相关的Cookie。
Web服务端在收到带Cookie的http请求时,会从Cookie中提取session标识,然后从Session管理器中寻找此Cookie相关的会话,从而取得会话上下文。取得会话之后会做一些安全校验,通常是校验User-Agent字段。这也是通常窃取别人的会话信息要同时窃取Cookie和User-Agent信息的原因。这一点也说明在浏览器上,用http协议访问Web服务器是不安全的,因为http是明文传输的,在局域网内抓包,很容易就窃取到Cookie和User-Agent信息,可以伪造请求。
1.7 跨域
跨域指的是从一个域下的资源上引用或跳转到另一个域下的资源。跨域在网页中是很常见的现象,比如在百度中搜索内容,然后点击某一个搜索结果,打开一个非百度的页面,这就有跨域。跨域有很多种情形,并不是每种跨域都会受到限制,主要受限的情形是iframe和ajax跨
域。跨域限制是浏览器端和Web服务端在一个公共约定基础上的自主选择。
服务端可以进行跨域限制,以简陋地防止页面被某些网站引用。Http有头部字段Origin和Referer字段来表明是从那个域和页面跳转过来的。在浏览器上进行超链接跳转或ajax请求,浏览器都会自动设置上。但如果Web客户端不设置上这些头信息,服务端就无法得知是否发生了跨域。
服务端可以在应答的头部添加一些头信息,以告诉浏览器端应该采取哪些安全限制。下面介绍以下与跨域相关的一些头信息。
1.7.1 Referer
它是一个请求字段,用它来表名当前请求是从哪里发起的。
1.7.2 Referrer-Policy
它是一个请求字段。规定了Referer的发送策略。可选值有以下几种:
- 空字符串("")。若设为空串则默认按照浏览器的机制设置referrer的内容,默认情况下是和no-referrer-when-downgrade设置得一样。
- no-referrer。任何情况下都不发送Referrer信息
- no-referrer-when-downgrade。这是默认值。当从https网站跳转到http网站或者请求其资源时(安全降级HTTPS→HTTP),不显示referrer的信息,其他情况(安全同级HTTPS→HTTPS,或者HTTP→HTTP)则在referrer中显示完整的源网站的URL信息。
- same-origin。表示浏览器只会显示referrer信息给同源网站,并且是完整的URL信息。所谓同源网站,是协议、域名、端口都相同的网站。
- origin。表示浏览器在referrer字段中只显示源网站的源地址(即协议、域名、端口),而不包括完整的路径。
- strict-origin。该策略更为安全些,和origin策略相似,只是不允许referrer信息显示在从https网站到http网站的请求中(安全降级)。
- origin-when-cross-origin。当发请求给同源网站时,浏览器会在referrer中显示完整的URL信息,发个非同源网站时,则只显示源地址(协议、域名、端口)
- strict-origin-when-cross-origin。和origin-when-cross-origin相似,只是不允许referrer信息显示在从https网站到http网站的请求中(安全降级)。
- unsafe-url。浏览器总是会将完整的URL信息显示在referrer字段中,无论请求发给任何网站。
Referrer Policy设置方法:
- 在Http的header中直接设置;
- 通过<meta>元素改变Referrer Policy,直接修改名为referrer的内容。
<meta name="referer" content="origin">
- 给 <a>, <area>, <img>, <iframe>, 或者<link>元素设置referrerpolicy属性。
<a href="http://www.baidu.com" referrerpolicy="origin">
- 如需设置不显示referrer信息时,也可以给 <a>, <area>, <link>元素设置rel的链接关系。
<a href="http://www.baidu.com" rel="noreferrer">
1.7.3 X-Frame-Options
它是响应字段,用来给浏览器指示允许一个页面可否在<frame>, <iframe>, <embed> 或者 <object>中展现的标记。站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。可取值:
- deny。表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
- sameorigin。表示该页面可以在相同域名页面的 frame 中展示。
- allow-from uri。表示该页面可以在指定来源的 frame 中展示。例如:
X-Frame-Options: allow-from https://example.com/
1.7.4 X-XSS-Protection
这是响应字段,当检测到跨站脚本攻击 (XSS (en-US))时,浏览器将停止加载页面。可取值:
- 0。禁止XSS过滤。
- 1。启用XSS过滤(通常浏览器是默认的)。 如果检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。
- 1;mode=block。启用XSS过滤。如果检测到攻击,浏览器不会清除页面,而是阻止页面加载。
- 1; report=<reporting-URI> (Chromium only)。启用XSS过滤。 如果检测到跨站脚本攻击,浏览器将清除页面并使用CSP report-uri (en-US)指令的功能发送违规报告。
1.7.5 Set-Cookie
Set-Cookie是应答首部字段,其中含有先前由服务器通过Set-Cookie响应首部字段投放存储到客户端的Cookie信息。它的语法格式:
SetCookie: name=value; name2=value2; name3
其中有一个有与跨域相关的特定键值。
- HttpOnly。如果在cookie中设置了HttpOnly附加标志,那么js脚本将无法读取到cookie信息,这样能有效防止XSS攻击。
- Expires。Cookie的有效期。如果没有设置,那么就是内存cookie,浏览器关闭,cookie就会丢失;如果设置了Expires,那么就是硬盘cookie,cookie被存储到磁盘,下次启动浏览器,还可以读取到。
- Domain。cookie的适用域。它是使用endWith匹配方法的。
- Path。在domain基础上,进一步限定适用的路径。
- Secure。一个带有安全属性的 cookie 只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器。然而,保密或敏感信息永远不要在 HTTP cookie 中存储或传输,因为整个机制从本质上来说都是不安全的,比如前述协议并不意味着所有的信息都是经过加密的。非安全站点(http:)已经不能再在 cookie 中设置 secure 指令了(在Chrome 52+ and Firefox 52+ 中新引入的限制)。
- SameSite。客户端传送cookie的规则限制。可取值:空字符串("")、Lax()、None、Strict。https下得设置成SameSite=None ; Secure才能在iframe、ajax跨域请求时携带cookie。
1.7.8 Access-Control-Allow-Origin
这是一个响应头部字段。用来告诉浏览器,在没有访问凭据时可以访问的域。在CORS时,需要搭配Vary头信息一起使用。如下:
Access-Control-Allow-Origin: https://developer.mozilla.org
Vary: Origin
2.跨域
对于跨域带来的问题,这里的解决思路不是让用户去修改浏览器设置,让浏览器不去进行相关的安全检查。而是在浏览器缺省的设置条件下,如何解决跨域问题。主要是这样的解决思路:
- 将服务端告诉浏览器端的跨域限制条件放开,以允许当前情形下跨域;
- 让浏览器端不认为这是在跨域,主要是使用代理技术;
- 针对cookie在某些情形下,因不满足浏览器的安全要求,局部被禁用的情形,则是从设法满足浏览器的安全要求,和自定义url参数或header,来实现cookie的功能。
网友评论