10.12. CORS 与 CSRF¶
CORS 和 CSRF 是一个面向开放互联网的摄像头在 HTTPS 和登录之外还需要的两项浏览器端防护。每一项只需几行代码即可设置。下面各小节会定义这个术语,并展示与 microdot 的集成方式。
10.12.1. CORS 的作用¶
跨源资源共享(Cross-Origin Resource Sharing,CORS)是一种浏览器机制,它让服务器可以选择性地允许特定的其他源读取它的响应。浏览器默认的同源策略会阻止这种读取:https://example.com 上的 JavaScript 无法读取来自 https://yard-cam.example.com 的响应,因为主机不同就算作不同的源。CORS 就是服务器端为选定的对端授予例外的方式。
如果仪表盘就是由摄像头自己提供的,那么每个请求都是同源的,CORS 不会起任何作用。只有当仪表盘位于别处时,这套设置才有意义——比如一个像 https://app.example.com 这样的公共 URL,它要与位于 https://yard-cam.example.com 的摄像头通信:
from microdot.cors import CORS
cors = CORS(
app,
allowed_origins=['https://app.example.com'],
allow_credentials=True,
max_age=86400,
)
allowed_origins 是被允许读取摄像头响应的源的列表。只有仪表盘的源、且仅仪表盘的源——而不是 *——这样第三方网站就不会意外地读取到摄像头的响应。
allow_credentials=True 让跨源请求得以携带会话 cookie,这正是仪表盘在跨越源边界时保持登录状态所需要的。
max_age=86400 告诉浏览器它可以把预检结果缓存一天。对于任何使用了 GET/HEAD/POST 以外方法、或发送了自定义请求头的跨源调用,浏览器都会先发出一次额外的 OPTIONS 请求;max_age 会把这项开销削减到每个路由每天只做一次预检。
10.12.2. CSRF 的作用¶
跨站请求伪造(Cross-Site Request Forgery,CSRF)是这样一种攻击:恶意页面让用户的浏览器向一个受信任的服务器发出一个已认证的请求。即便部署了 CORS,evil.com 上一个向 https://yard-cam.example.com/config 提交 POST 的隐藏 <form> 仍然能到达摄像头,而且浏览器会附上摄像头的会话 cookie——cookie 跟随的是目标主机,而不是发出请求的那个页面的源——于是摄像头会高高兴兴地处理这个 POST,仿佛它来自拥有者本人。
CSRF 防护会拒绝这类请求。microdot.csrf.CSRF 会添加一个中间件,它会检查每一个会改变状态的请求上的 Sec-Fetch-Site 请求头,并拒绝任何未被标记为 same-origin(或来自 CORS 允许的源)的请求:
from microdot.csrf import CSRF
CSRF(app, cors=cors)
传入这个 cors 实例可以让中间件把仪表盘的允许源一并放行——即使仪表盘的 POST 是跨源的,摄像头仍然会接受它们。
Sec-Fetch-Site 由现代浏览器自动设置;摄像头在客户端无需做任何事。对于不发送该请求头的较旧浏览器,CORS 允许列表就是兜底的检查手段。
10.12.3. 豁免 webhook¶
如果摄像头需要一个 webhook 端点来接受来自第三方云服务的 POST——比如存档服务商的回调——请用 @csrf.exempt 标记该路由,让中间件放行它。这时处理函数有责任以其他方式验证请求——通常是对载荷计算一个基于哈希的消息认证码(Hash-based Message Authentication Code,HMAC),用摄像头和第三方共享的一个密钥计算,从而证明请求来自一个知道该密钥的人。后院摄像头并没有这些场景,但当你需要时,这个装饰器就在那里。
10.12.4. 四行的基线方案¶
一旦部署了 HTTPS,对于任何面向互联网的摄像头部署,推荐的技术栈是:
Session(app, secret_key=SECRET,
cookie_options={'http_only': True, 'secure': True})
login = Login()
cors = CORS(app, allowed_origins=[...], allow_credentials=True)
CSRF(app, cors=cors)
本章前面讲过的会话与登录、这里讲的 CORS 与 CSRF,以及上一主题讲的 HTTPS。这四块层层叠加,并且都不会妨碍各个路由的处理。
现在摄像头可以安全地面对开放互联网了——HTTPS、登录、CSRF、CORS。