简书技术团队首页投稿(暂停使用,暂停投稿)

Session & JWT-Auth & CSR

2017-10-25  本文已影响106人  冰淤

Session(传统的用户认证):

关键:session_id & remember_user_token

session_id 是一个 key,session 可以存在 file 或 cache 中

只要开启 session,无论用户是否登录,rails(或其他后端框架)每次都会向客户端传递一个 session_id

一个新的访客由于在 request 中没有携带 session_id,所以服务端会生成一个新的 session 并向客户端返回 session_id,如果 request 中携带了有效的 session_id,那么就不会生成新的 session

如果用户登录,那么旧的 session 就会被销毁,服务端会生成新的 session,并告诉客户端存储新的 session_id

因为 session 不能永久存储(否则 session 只会越来越多),所以是需要过期的,假设 session 存储 1 小时,那么 1 小时后用户就会自动丢失登录信息,所以有了 remember_user_token,它是存在数据库中的

如果用户在 request 中没有携带 session_id 或者 session_id 对应的值已经过期被销毁了,那么就可以通过 request 中的 remember_user_token 来获取用户信息,并且创建一个新的 session,返回给客户端一个 session_id

这就是原本的 session 用户认证机制,相比于下面的 jwt-auth,session 的优势在于它可以在 session-value 中存储额外的数据


JWT-Auth(令牌认证):

关键:token-ttl( token 存储时效) && token-refresh-ttl ( token 可刷新时效)

与 session 不同,只有登录后的用户才会有 token

用户登录后,服务端给客户端返回一个 token,这个 token 需要客户端手动存储起来( cookie 或 localStorage )

用户每次 request 的时候,都要在 header 中携带 token,服务端通过 token 获取用户

token 与 session 相同,都是会过期的,这个过期时间是 token-ttl,只要超过了 token-ttl,服务端就会在存储中删除相关的 token,与 session 相同

如果此时用户再次携带 token 来请求,那么就需要 token-refresh-ttl 了,它和 session 中的 remember_user_token 起相同的作用,只不过它不存储在数据库中,而是分发给了客户端(服务端将 token 解密之后就可以拿到 token-refresh-ttl 了),只要 token-refresh-ttl 还在时效范围内,服务端就会在 response 的 header 中返回一个新的 token,客户端保存起来就可以了,如果 token-refresh-ttl 也过期了,那么就需要重新登录了

相比于 session 的用户认证机制,它的优势在于跨终端( web,App )


CSRF(跨站点访问攻击)防御:

如果使用 session 的认证机制,rails 或者其他框架会在 session-value 中存储一个 csrf_token 的值,类似这样(猜想):

'session_id': {
  'user_id': 'xxx',
  'csrf_token': 'xxx',
  'expired_at': 'xxxxx',
  'other': 'xxxx',
  'user-agent': 'xxx',
  'accept-language': 'xxx'
}

所以当用户发送 GET-request 时,服务端就会返回两个数据在 response 的 header 的 Set-Cookie 中,一个是 session_id,一个是 csrf_token

当用户发送 POST 请求时,需要携带 csrf_token 这个头,以及 cookie 中的 session_id,(PS:如果客户端在 cookie 中也携带了 csrf_token,会优先使用 cookie 中的值)然后服务端通过 session_id 来获取 [server-side] csrf_token 与 [client-side] csrf_token 做比较,只要相同就是一个有效的请求

当用户发送一个有效的 POST / PUT / DELETE 请求后,服务端原有的 csrf_token 就会更新,并且会携带在 response 的 header 中,让客户端更新,如果用户发送 GET 请求,csrf_token 不需要验证也不需要更新,这就是现有的 CSRF 的防御原理

因为 JWT-Auth 无法在服务端存储额外的信息,所以 JWT-Auth 无法防御 CSRF,如果使用 JWT-Auth 又要防御 CSRF,就需要搭配 session 了


SSR 中的 Auth 与 CSRF:

经过上面的分析,如果 SSR 中,endpoint(API的服务端) 仍然使用的是 session 认证机制,那么需要做 CSRF 防护,只需要将 endpoint 发给 SSR 服务器的 session_id、remember_user_token、csrf_token 原样传给客户端就行了,SSR 服务器只起到一个中转的作用

部分后端框架户对 session 的 user-agent 和 accept-language 等 header 头做防劫持,因此最好还是把整个 header 转发过去比较好

如果是 JWT-Auth 的 SSR,要分 endpoint 是 Node.js 和非 Node.js 的情况


待续。

上一篇 下一篇

猜你喜欢

热点阅读