Skip to content

Session

session: 安全的 cookie

Cookie 在 Web 程序中发挥了很大的作用,其中最重要的功能是存储用户的认证信息。
我们先来看看基于浏览器的用户认证是如何实现的。当我们使用浏览器登录某个社交网站时,会在登录表单中过填写用户名和密码,单击登录按钮后,这会向服务器发送一个包含认证数据的请求。
服务器接收请求后会查找对应的账户,然后验证密码是否匹配,如果匹配,就在返回的响应中设置一个 cookie,比如 "login_user": greyli

响应被浏览器接收后,cookie 会被保存在浏览器中。
当用户再次向这个服务器发送请求时,根据请求附带的 Cookie 字段中的内容,服务器上的程序就可以判读用户的认证状态,并识别出用户

但是这会带来一个问题,在浏览器中手动添加和修改 Cookie 是很容易的事,仅仅通过浏览器插件就可以实现。
所以,如果直接把认证信息以明文的方式存储在 Cookie 里,那么恶意用户就可以通过伪造 cookie 的内容获得对网站的权限,冒用别人的账户。
为了避免这个问题,我们需要对敏感的 Cookie 内容进行加密。

附注:
在编程中,session 指用户会话(user session),又称为对话(dislogue),即服务器和客户端/浏览器之间或桌面程序和用户之间建立的交互活动。
在 Flask 中,session 对象用来加密 Cookie。默认情况下,它会把数据存储在浏览器上一个名为 session 的 cookie 里

  • session 是另一种记录服务器和客户端会话状态的机制
  • session 是基于 cookie 实现的,session 存储在服务器端sessionID 会被存储到客户端的 cookie 中

session 认证流程:

  • 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session
  • 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器
  • 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名
  • 当用户二次访问服务器的时候,请求会自动判断域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失败,如果找到 Session 证明用户已经登录可执行后面操作

根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态

flask 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# session = LocalProxy(partial(_lookup_req_object, "session"))
# 默认 session 的在 cookie 中的 key 是 "session"
from flask import Flask, session, redirect, url_for, request
from flask_session import Session

app = Flask(__name__)
app.config['SECRET_KEY'] = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'

Session(app)

@app.route('/')
def index():
    if 'username' in session:
        username = session['username']
        return 'Logged in as ' + username + '<br>' + \
            "<b><a href = '/logout'>click here to log out</a></b>"
    return "You are not logged in <br><a href = '/login'></b>" + \
        "click here to log in</b></a>"

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action = "" method = "post">
            <p><input type = text name = username></p>
            <p><input type = submit value = Login></p>
        </form>
    '''

@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

app.run(port=2345)

注意,为了使用 session,我们需要设置一个 secret key,以便 Flask 可以对 session 数据进行加密。在上述示例中,我们设置了一个名为 SECRET_KEY 的配置变量。还需要配置 SESSION_TYPE 以指定存储 session 数据的位置。
在上述示例中,我们将其设置为 filesystem,这将在文件系统上存储 session 数据。

  • 安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 时存储在客户端的
  • 存取值的类型不同,Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型
  • 有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效
  • 存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源
  • 当客户访问量增加,服务端需要存储大量的 session 会话,对服务端有很大考验
  • 当服务端为集群时,用户登录其中一台服务器,会将 session 保存在该服务器的内存中,但是当用户访问其它服务器时。会无法访问
    • 可以使用缓存服务器来保证共享
    • 第三方缓存来保存 session
  • 由于依赖 cookie,所以存在 CSRF 安全问题