我有一个Spring MVC应用程序,就其而言,将JPA实体的ID公开给用户(隐藏的html输入或浏览器网址)。
这可能允许恶意用户使用其浏览器对属于另一个用户的实体执行操作。
有人可以建议解决这个安全问题吗?
答案 0 :(得分:4)
有一个更好的解决方案。出于某些目的,您可以将用户ID保留为主键,但出于此特定目的,我建议您在所有需要的表中创建一个列,例如:调用:IDENTIFIER
并为其生成一些强大的随机ID,我使用它来生成ID:
public static String generateId() {
return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
}
然后您可以在视图中使用这些标识符。我还为JPA
编写了一个通用方法来查找具有这些列的实体:
public T findByGeneratedId(String generatedId) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root<T> entity = cq.from(entityClass);
CriteriaQuery query = cq.select(entity).where(
cb.equal(entity.get("generatedId"), generatedId));
try {
return (T) this.entityManager.createQuery(query).getSingleResult();
} catch (RuntimeException e) {
return null;
}
}
请注意,我的列名为GENERATED_ID
,所有实体都有一个字段:
@Column(name = "GENERATED_ID", nullable = false, unique = true)
private String generatedId = generateId();
这将保证您实体的唯一性和安全性,并且不需要一些复杂的encoding/decoding
内容。
答案 1 :(得分:2)
在我看来,加密ID并不是一个好主意,更像隐藏真正的问题。干净利落可能会非常棘手。并且恶意用户仍然可以拦截另一个用户的请求并使用加密的ID来执行攻击。
真正的解决方案是在您的业务逻辑中实现某种访问控制,并拒绝尝试访问未经授权的资源,例如属于另一个用户的实体。
如果它很简单(没有属于多个用户的共享实体,没有组,只属于一个用户的实体,那应该非常简单),你可以自己实现这个逻辑。
您可以将其实现为一种拦截器(使用面向方面,例如为DAO或服务方法添加一个方面),以便自动执行并避免过多重复的样板代码。
您还可以使用Spring Security,它具有一些访问控制机制。
如果需求更复杂,可以使用Spring Security在域对象上实现完整的ACL(访问控制列表)系统。这更复杂,因为ACL是单独存储的,因此它需要数据库中的一些exxtra基础结构,配置正确似乎相当复杂,但在我看来,它是更灵活和可扩展的解决方案。我自己没有实现ACL,所以我不能就此提出很多具体的建议。
如果你坚持要隐藏用户的ID,我建议你不要真正加密ID,而是在真实ID和一些随机生成的临时ID之间使用每会话对应表。这样,您就可以避免频繁加密/解密ID,并使一个可见ID对另一个用户完全无用。
希望这有帮助。