play 框架的安全设计只是存在于脑海中的一个概念——这就不可能阻止开发者在设计上出漏洞。本指南描述了在 Web 应用中常见的安全问题,并指导如何在play 应用开发中去避免。
16.1. Sessions
一般情况下,你需要保存用户登录信息。如果不使用 session,用户就需要在每次请求时传递证书。
这就是 session 要做的事:一系列 cookies 存储在用户的浏览器里,以用于标识不同的站点,并提供其他数据层之外的信息,比如语言。
守住你的安全…安全
session 是一个 key/values 哈希表,有签名但没有加密。也就是说主要你的secret 是安全的,那么第三方就不可能伪造 session。
而 secret 存储在 conf/application.conf 里。 保持这个文件的私有化非常重要:不要把它提交给公共仓库,为防止在安装某些人写的应用程序时对 secret key进行修改,你可以运行命令 play secret 进行保护。
不要存储关键性的数据
然后, 既然 session 没有加密, 那么就不应该在 session 里存储关键性安全数据,通过本地网络连接或 wifi 连接进行嗅探,它将被看成是用户的 cookie。
session 被存储在 cookie 里, 而 cookies 被限制为 4 KB 大小。而且只能存储 String。
16.2. 跨站点脚本攻击
在 web 应用程序中,Cross-site scripting 跨站点脚本是最常见的弱点。它包含了利用应用程序提供的窗体恶意注入到 web 页面的脚本代码。
假定这里有一个博客程序,任何人都可以发表评论,如果评论内容里包含了攻击脚本,那么你将打开你的站点进行攻击,可能步骤为:
- 为访问者弹出一个窗体
- 跳转访问者到被攻击者控制的站点
- 窃取当前用户的可见信息,并发回攻击者的站点
因此为防止类似攻击,进行保护非常重要。
play的模板引擎会自动转义字符串。 如果需要在模板里插入未转义的 HTML代码,那就需要使用 java 字符串扩展的 raw() 方法。但如果这个字符串是用户输入的,那么你就需要首先确定它是否是无害的。
在对用户输入进行 sanitizing 消毒的时候,只允许白名单(只允许列表中的安全标签通过)通过,黑名单 (禁止一些不安全的标签列表)则禁止。
16.3. SQL 注入
SQL 注入所谓 SQL 注入,就是通过把 SQL 命令插入到 Web 表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的 SQL 命令,比如先前的很多影视网站泄露 VIP 会员密码大多就是通过 WEB 表单递交查询字符暴出的, 这类表单特别容易受到 SQL 注入式攻击。这个漏洞可以摧毁数据,或访问不应该让当前用户看到的数据。
当使用高级的“find”方法时, 就应该小心 sql 注入。 当手工构建自己的查询时,尽量不要用+来作连接符,而是用?占位符。
比如:
createQuery("SELECT * from Stuff WHERE type= ?1").setParameter(1,theType);
下面这个就不好了:
createQuery("SELECT * from Stuff WHERE type=" + theType;
16.4. 跨站点请求伪造
跨站点请求伪造 CSRF 攻击在 web 应用程序里非常危险:这个攻击方法通过在用户信任的 web 应用程序的页面里加入恶意代码或链接进行攻击。如果 web 应用程序的 session 没有设置超时,那么攻击者就可以执行未经验证的命令。
为预防这类攻击,首先就是要正确全用 GET 和 POST 方法。意思是仅允许 POST 方法用于修改应用程序的状态。
对 POST 请求来说,只有真实的 token 才会触发动作。Play 现在内建了一个帮助类用于处理这样的问题:
- checkAuthenticity() 检查真实性方法在控制器里可直接使用, 用于检查请求参数里的 token 的真实有效,如果存在问题,就发送一个禁止response 的命令。
- session.getAuthenticityToken()方法用于为当前 session 生成一个真实可信的 token
- #{authenticityToken /}用于创建一个隐藏的输入域,可用于任何窗体
示例:
public static destroyMyAccount() {
checkAuthenticity();
...
}
上面的方法仅当窗体包含了真实有效的 token 时,才会被调用:
<form method="post" action="/account/destroy">
#{authenticityToken /}
<input type="submit" value="destroy my account">
</form>
对 POST 请求来说,Play 提供 form 标签会自动生成一个有效的 token:
#{form @destroyMyAccount()}
<input type="submit" value="destroy my account">
#{/form}
如果你想保护所有控制器的 action 动作,你也可以使用 checkAuthenticity() 方法作为 before filter 进行听说。
更多跨站点请求伪造见:More on cross-site request forgery。