http cookie session

170 阅读13分钟

HTTP response status codes

developer.mozilla.org/en-US/docs/…

  1. [Informational responses] (100 – 199)
  2. [Successful responses] (200 – 299)
  3. [Redirection messages] (300 – 399)
  4. Client error responses
  5. [Server error responses] (500 – 599)
  • [101 Switching Protocols]

cookie

developer.mozilla.org/zh-CN/docs/… Cookie 使基于[无状态]的 HTTP 协议记录稳定的状态信息成为了可能。

Cookie 主要用于以下三个方面:

  • [会话状态管理]

    如用户登录状态、购物车、游戏分数或其他需要记录的信息

  • [个性化设置]

    如用户自定义设置、主题和其他设置

  • [浏览器行为跟踪]

    如跟踪分析用户行为等

定义 Cookie 的生命周期

Cookie 的生命周期可以通过两种方式定义:

  • 会话期 Cookie 会在当前的会话结束之后删除。浏览器定义了“当前会话”结束的时间,一些浏览器重启时会使用会话恢复。这可能导致会话 cookie 无限延长。
  • 持久性 Cookie 在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除。

[限制访问 Cookie]

有两种方法可以确保 Cookie 被安全发送,并且不会被意外的参与者或脚本访问:Secure 属性和 HttpOnly 属性。

标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。它永远不会使用不安全的 HTTP 发送(本地主机除外)

JavaScript [Document.cookie] API 无法访问带有 HttpOnly 属性的 cookie;此类 Cookie 仅作用于服务器。例如,持久化服务器端会话的 Cookie 不需要对 JavaScript 可用,而应具有 HttpOnly 属性。此预防措施有助于缓解[跨站点脚本(XSS)]攻击。

cookie session

cloud.tencent.com/developer/a…

他们的出现是因为 HTTP 是无状态协议,服务器无法从网络连接上知道客户身份。

Set-Cookie header 会通知客户端保存Cookie(在本地机器上存储一小段文本)。当客户端再向服务端发起请求时,客户端会自动在请求报文中加入Cookie值之后发送出去.

set-Cookie的字段的属性

Set-Cookie: logcookie=3qjj; expires=Wed, 13-Mar-2019 12:08:53 GMT; Max-Age=31536000; path=/;
 domain=fafa.com;secure; HttpOnly;

以上面的set-cookie的例子,说一下set-cookie的属性

1. logcookie=3qjj 赋予Cookie的名称和值,logcookie是名字 ,3qjj是值

2. expires 是设置cookie有效期。当省略expires属性时,Cookie仅在关闭浏览器之前有效。可以通过覆盖已过期的Cookie,设置这个Cookie的过期时间是过去的时间,实现对客户端Cookie 的实质性删除操作。

3. path 表示浏览器要发送该 Cookie 标头时,请求的 URL 中所必须存在的路径。

4. domain 指定 cookie 可以送达的主机。只能将值设置为当前域名或更高级别的域名(除非是公共后缀)。设置域名将会使 cookie 对指定的域名及其所有子域名可用。若缺省,则此属性默认为当前文档 URL 的主机(不包括子域名)。

登陆方式

blog.csdn.net/Kimser/arti…

Cookie + [Session]

image.png

用户访问 a.com/pageA,并输入密码登录。

服务器验证密码无误后,会创建 SessionId,并将它保存起来。

服务器端响应这个 HTTP 请求,并通过 Set-Cookie 头信息,将 SessionId 写入 Cookie 中。

服务器端的 SessionId 可能存放在很多地方,例如:内存、文件、数据库等。

第一次登录完成之后,后续的访问就可以直接使用 Cookie 进行身份验证了:

image.png

  1. 用户访问 a.com/pageB 页面时,会自动带上第一次登录时写入的 Cookie。
  2. 服务器端比对 Cookie 中的 SessionId 和保存在服务器端的 SessionId 是否一致。
  3. 如果一致,则身份验证成功。

Cookie + Session 存在的问题

SessionId 存放在 Cookie 中,有 CSRF 攻击的 问题 (可以通过 CSRF Token (个token和浏览器自动携带的cookie不一样,是需要前端手动带上的) 或者 双重 cookie (在请求中加一个额外的字段,其值和cookie一致) 解决)

Token

Token 是服务端生成的一串字符串,以作为客户端请求的一个令牌。当第一次登录后,服务器会生成一个 Token 并返回给客户端,客户端后续访问时,只需带上这个 Token 即可完成身份认证。

image.png

  1. 用户输入账号密码,并点击登录。
  2. 服务器端验证账号密码无误,创建 Token。
  3. 服务器端将 Token 返回给客户端,由客户端自由保存

后续页面访问时:

image.png

Token 机制的特点

  • 服务器端不需要存放 Token
  • Token 可以存放在前端任何地方,可以不用保存在 Cookie 中(还有local storage)
  • Token 下发之后,只要在生效时间之内,就一直有效,如果服务器端想收回此 Token 的权限,并不容易。

Token 的生成方式

最常见的 Token 生成方式是使用 JWT(Json Web Token)

JWT 算法主要分为 3 个部分:header(头信息),playload(消息体),signature(签名)。

  • header 部分指定了该 JWT 使用的签名算法:
    • header = '{"alg":"HS256","typ":"JWT"}' // HS256 表示使用了 HMAC-SHA256 来生成签名。
  • playload 部分表明了 JWT 的意图:
    • payload = '{"loggedInAs":"admin","iat":1422779638}' //iat 表示令牌生成的时间
  • signature
base64Signature = encodeBase64(HMAC(key, `${base64Header}.${base64Payload}`))  //key 为 服务器私钥
  
token = `${base64Header}.${base64Payload}.${base64Signature}`

服务器在判断 Token 时:


base64Header, base64Payload, base64Signature = token.split('.')
 
signature1 = decodeBase64(base64Signature)
unsignedToken = `${base64Header}.${base64Payload}`
signature2 = HMAC('服务器私钥', unsignedToken)
 
if(signature1 === signature2) {
  return '签名验证成功,token 没有被篡改'
}
 
payload =  decodeBase64(base64Payload)
if(new Date() - payload.iat < 'token 有效期'){
  return 'token 有效'
}

php Session

一个访问者访问你的 web 网站将被分配一个唯一的 id,就是所谓的会话 id。 这个 id 可以存储在用户端的一个 cookie 中,也可以通过 URL 进行传递。

会话支持允许你将请求中的数据保存在超全局数组$_SESSION中。 当一个访问者访问你的网站,PHP 将自动检查(如果 [session.auto_start] 被设置为 1)或者在你要求下检查(明确通过 [session_start()]) 当前会话 id 是否是先前发送的请求创建。如果是这种情况, 那么先前保存的环境将被重建 ??

session_start()  会创建新会话或者重用现有会话。 如果通过 GET 或者 POST 方式,或者使用 cookie 提交了会话 ID, 则会重用现有会话。

当会话自动开始或者通过 session_start()  手动开始的时候, PHP 内部会调用会话处理程序的 open 和 read 回调函数。 会话处理程序可能是 PHP 默认的, 也可能是扩展提供的(SQLite 或者 Memcached 扩展), 也可能是通过 [session_set_save_handler()] 设定的用户自定义会话处理程序。 通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储), PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量。

SESSION 的数据保存在哪里呢?

保存在服务器的文件或数据库中。

默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。

如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path="N;/save_path",N 为分级的级数,save_path 为开始目录。

当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。

secure your PHP sessions in several ways:

  • Regenerate the session ID periodically.
  • Store the session data on the server-side in an encrypted format.
  • Use HTTPS to protect the transmission of session data.

[Cache-control]

  • cache-control: private
    具有“private”指令的响应只能由客户端缓存,不能由中间代理(例如 CDN 或代理)缓存。这些资源通常是包含私密数据的资源,例如显示用户个人信息的网站。

  • cache-control: public
    相反,“public”指令表示资源可以由任何缓存存储。

  • cache-control: no-store
    带有“no-store”指令的响应无法缓存到任何位置,也永不缓存。也就是说,用户每次请求此数据时,都必须将请求发送到源站服务器以获取新副本。此指令通常保留给包含极其敏感数据的资源,例如银行帐户信息。

  • cache-control: max-age
    此指令指定了生存时间,也就是资源在下载后可以缓存多少秒钟。例如,如果将最大期限设置为 1800,则首次从服务器请求资源后的 1800 秒(30 分钟)内,后续请求都会向用户提供该资源的缓存版本。如果 30 分钟后用户再次请求资源,则客户端需要向服务器重新请求该资源。

  • cache-control: no-cache 从网页截图里可以看出,使用的缓存控制指令是cache-control: no-cache。它表示,只有先检查资源没有更新版本后,才可使用所请求资源的缓存版本。那么问题来了,怎么判断资源是否有更新版本呢?这就需要 ETag

[ETag]

Etag 是 Entity tag 的缩写,是服务端的一个资源版本的令牌标识。在 HTTP 响应头中将其传送到客户端。每当资源更新时,此令牌会更新。

比如,浏览器第一次请求资源的时候,服务端返回了这个资源的ETag: "095933fff2323351d3b495f2f879616f1762f752"

当浏览器再次请求这个资源的时候,浏览器会将If-None-Match: "095933fff2323351d3b495f2f879616f1762f752" 传输给服务端,服务端拿到该 ETAG,对比资源是否发生变化。

  • 如果资源未发生改变,则返回 304HTTP 状态码,不返回具体的资源。
  • 否则表示资源已经更新,浏览器需要下载新版本以提供给用户。

此过程可确保用户始终获得资源的最新版本,并且无需进行不必要的下载。

安全相关

[跨站脚本(XSS)]

跨站脚本(XSS)是一种安全漏洞,允许攻击者向网站注入恶意客户端代码。该代码由受害者执行从而让攻击者绕过访问控制并冒充用户。

跨站脚本攻击通常在以下情况中发生:1) 数据通过不受信任的来源(通常是 Web 请求)进入 Web 应用程序,或者 2) 动态内容未经恶意内容验证就发送给 Web 用户。

恶意内容通常包括 [JavaScript],但有时也包括 HTML、Flash 或浏览器可以执行的任何其他代码。基于 XSS 的攻击种类几乎是无限的,但它们通常包括向攻击者传输 Cookie 等隐私数据或其他会话信息、将受害者重定向到攻击者控制的网页,或者以易受攻击的站点为幌子在用户的计算机上执行其他恶意操作。

跨站请求伪造(CSRF)

segmentfault.com/a/119000004… Cross-site request forgery,跨站请求伪造,直白来说就是恶意网站伪装成用户,向被害网站发起操作请求。

发起

  • 自动提交的隐藏表单,只要用户打开页面,就会发起转账请求;
  • 在受害网站的评论区放置一个a标签,点击跳转时发起伪造请求;
  • 在受害网站的评论区发表伪装的图片,实际是一个恶意请求,浏览过评论区的用户都会被黑客攻击;
<img
  src="https://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory" />

现在如果你登录银行账户并且你的 cookie 仍然有效(并且没有其他验证),那么在加载包含此图像的 HTML 后你将立即转账。

预防

检验请求来源

HTTP 请求头中有两个字段会标识请求的来源:originreferer。这两个字段不受前端控制,会诚实地告诉服务器请求来自哪里。

origin是请求来源的域名部分,但是在302重定向、以及IE11上不会显示。

所以还需要referer辅助,它是一个完整的url路径,但可靠性不如origin

SameSite cookie

CSRF攻击能成功,正是所有向受害网站发起的请求,都会被自动带上cookie。

Chrome意识到这个问题后起草了一份协议:向浏览器注入cookie时,开发者可以标注哪些请求才会带上。

被标注为strict的cookie只有本域的请求才能带上。

被标注为lax的cookie在跳转到新页面时可以带上。因为如果全部设置为strict,在百度搜索并打开淘宝,默认是没有登录的,用户体验会很差。

CSRF Token

在本站发起的请求中,加一个攻击者无法获取的token,也可以区别出正常请求和恶意请求。这个token和浏览器自动携带的cookie不一样,是需要前端手动带上的。

双重cookie

在请求中加一个额外的字段,其值和cookie一致。因为上文提到过,攻击者没法获取到cookie,只是在发起请求时会携带。

在服务端收到请求时,如果没有和cookie值一样的额外字段,就可以认为是来自恶意网站。

中间人

第三方拦截了 Web 服务器和客户端(浏览器)之间的流量,并冒充 Web 服务器以捕获数据(例如登录凭据或信用卡信息)。流量被传递,可能会进行修改。公共 Wi-Fi 网络是执行此类攻击的典型手段。

会话劫持

会话劫持是指获取并滥用用户的已验证会话。这可能是通过窃取现有会话的 cookie,或者通过欺骗用户(或其浏览器)设置可预测的会话 ID 的 cookie 来实现的。

通过部署严格的内容安全策略(Content-Security-Policy),可以限制数据泄露途径。

golang

可以使用 gorilla/sessions

nginx

Syntax: | location [ = | ~ | ~* | ^~ ] uri { ... } location @name { ... } |

| Default: | —

| Context: | serverlocation

A location can either be defined by a prefix string, or by a regular expression. Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching). To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

If the longest matching prefix location has the “^~” modifier then regular expressions are not checked.

using the “=” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates.