我有一个 Spring MVC 应用程序,就目前而言,它向用户公开 JPA 实体的 ID(在隐藏的 html 输入或浏览器 url 中)。
这可能允许恶意用户使用他们的浏览器对属于另一个用户的实体执行操作。
任何人都可以建议解决这个安全问题吗?
- 加密/解密 ID是一个好的解决方案吗?
- 如果是这样,在哪个层(Web、服务、存储库)适合这样做?
- 推荐哪种加密解决方案(对称/非对称)?
- 有更好的解决方案吗?
我有一个 Spring MVC 应用程序,就目前而言,它向用户公开 JPA 实体的 ID(在隐藏的 html 输入或浏览器 url 中)。
这可能允许恶意用户使用他们的浏览器对属于另一个用户的实体执行操作。
任何人都可以建议解决这个安全问题吗?
有一个更好的解决方案。您可以出于某些目的将您的用户 ID 作为主键,但出于此特定目的,我建议在您需要的所有表中创建一个列,例如称为:IDENTIFIER
并为其生成一些强随机 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
东西。
在我看来,加密 ID 不是一个好主意,更像是隐藏真正的问题。干净地做起来可能会很棘手。并且恶意用户仍然可以拦截其他用户的请求并使用加密的 Id 进行攻击。
真正的解决方案是在您的业务逻辑中实现某种访问控制,并拒绝尝试访问未经授权的资源,例如属于另一个用户的实体。
如果它很简单,您可以自己实现这个逻辑(没有属于多个用户的共享实体,没有组,只有属于一个用户的实体,这应该很简单)。
您可以将其实现为一种拦截器(例如,使用面向方面,向您的 DAO 或服务方法添加方面),以便自动完成并避免过多重复的样板代码。
您还可以使用具有一些访问控制机制的 Spring Security。
如果需求更复杂,可以使用 Spring Security 在您的域对象上实现完整的 ACL(访问控制列表)系统。这比较复杂,因为 ACL 是单独存储的,所以需要在数据库中添加一些额外的基础设施,并且正确配置似乎相当复杂,但在我看来,它是更灵活和可扩展的解决方案。虽然我自己还没有实现 ACL,所以我不能对此提供太多具体的建议。
如果您坚持对用户隐藏 ID,我建议您不要真正加密 ID,而是使用真实 ID 和一些随机生成的临时 ID 之间的每个会话对应表。通过这种方式,您可以避免频繁地加密/解密 ID,并使一个可见的 ID 对另一个用户完全无用。
希望这可以帮助。