跨站脚本
JSF 旨在具有内置的 XSS 预防。您可以使用任何 JSF 组件安全地重新显示所有用户控制的输入(请求标头(包括 cookie!)、请求参数(也保存在 DB 中的参数!)和请求正文(上传的文本文件等))。
<h:outputText value="#{user.name}" />
<h:outputText value="#{user.name}" escape="true" />
<h:inputText value="#{user.name}" />
etc...
请注意,当您在 Facelets 上使用 JSF 2.0 时,您可以在模板文本中使用 EL,如下所示:
<p>Welcome, #{user.name}</p>
这也将被隐式转义。你不一定需要<h:outputText>
这里。
仅当您使用以下命令明确转义用户控制的输入时escape="false"
:
<h:outputText value="#{user.name}" escape="false" />
那么你就有了一个潜在的 XSS 攻击漏洞!
如果您想将用户控制的输入重新显示为 HTML,其中您希望只允许 HTML 标记的特定子集,如<b>
、<i>
、<u>
等,那么您需要通过白名单来清理输入。HTML 解析器Jsoup在这方面非常有帮助。
itemLabelEscaped
Mojarra < 2.2.6 中的错误
2.2.6 之前的旧 Mojarra 版本存在错误,当提供<f:selectItems itemLabel>
via 而不是或作为值时,错误地呈现未转义的List<T>
标签<f:selectItems var>
(List<SelectItem>
问题SelectItem[]
3143 )。换句话说,如果您通过 a 将用户控制的数据重新显示为项目标签List<T>
,那么您就有一个潜在的 XSS 漏洞。如果升级到至少 Mojarra 2.2.6 不是一个选项,那么您需要显式设置itemLabelEscaped
属性true
来防止这种情况发生。
<f:selectItems value="#{bean.entities}" var="entity" itemValue="#{entity}"
itemLabel="#{entity.someUserControlledProperty}" itemLabelEscaped="true" />
CSRF
JSF 2.x 已经javax.faces.ViewState
在使用服务器端状态保存时在表单中的隐藏字段的风格中内置了 CSRF 预防。在 JSF 1.x 中,这个值非常弱且太容易预测(它实际上从未打算用作 CSRF 预防)。在 JSF 2.0 中,这已通过使用长而强的自动生成值而不是相当可预测的序列值来改进,从而使其成为一种强大的 CSRF 预防措施。
在 JSF 2.2 中,这甚至通过使其成为 JSF 规范的必需部分以及用于加密客户端状态的可配置 AES 密钥来进一步改进,以防启用客户端状态保存。另请参阅JSF 规范问题 869和Reusing ViewState value in other session (CSRF)。JSF 2.2 中的新功能是对 GET 请求的 CSRF 保护<protected-views>
。
只有当您使用无状态视图时<f:view transient="true">
,或者应用程序中存在 XSS 攻击漏洞时,您才有潜在的 CSRF 攻击漏洞。
SQL注入
这不是 JSF 的责任。如何防止这种情况取决于您使用的持久性 API(原始 JDBC、现代 JPA 或好的 ol'Hibernate),但归根结底,您永远不应该像这样将用户控制的输入连接到 SQL 字符串中
String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = md5(" + password + ")";
String jpql = "SELECT u FROM User u WHERE u.username = '" + username + "' AND u.password = md5('" + password + "')";
想象一下如果最终用户选择以下名称会发生什么:
x'; DROP TABLE user; --
您应该始终在适用的情况下使用参数化查询。
String sql = "SELECT * FROM user WHERE username = ? AND password = md5(?)";
String jpql = "SELECT u FROM User u WHERE u.username = ?1 AND u.password = md5(?2)";
在普通的 JDBC 中,您需要使用PreparedStatement
来填充参数值,而在 JPA(和 Hibernate)中,该Query
对象也为此提供了 setter。