【问题标题】:How to avoid exposing IDs of JPA entities to end users如何避免将 JPA 实体的 ID 暴露给最终用户
【发布时间】:2013-09-05 22:36:48
【问题描述】:

我有一个 Spring MVC 应用程序,就目前而言,向用户公开 JPA 实体的 ID(在隐藏的 html 输入或浏览器 url 中)

这可能允许恶意用户使用他们的浏览器对属于另一个用户的实体执行操作

谁能提出解决这个安全问题的方法?

  • 加密/解密 ID 是一个好的解决方案吗?
  • 如果是这样,在哪一层(网络、服务、存储库)适合这样做?
  • 推荐哪种加密解决方案(对称/非对称)?
  • 有更好的解决方案吗?

【问题讨论】:

    标签: spring security encryption spring-security


    【解决方案1】:

    有一个更好的解决方案。您可以出于某些目的将您的用户 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 东西。

    【讨论】:

    • 嗨,保利乌斯!您的解决方案似乎很整洁!只有一个问题:replaceAll("-", "").toUpperCase(); 是干什么用的?
    • 仅使用 UUID.randomUUID().toString() 将生成类似这样的 ID:74ad4db8-9271-4d0c-9809-06871eb43628。我喜欢所有字符都是大写的,ID 没有破折号。所以我的东西会生成这样的东西:E7405B5669C2451A8FC36EDCBEEBA1F0。这对我来说更好:)
    • 也许我错过了什么,但我看不出您的解决方案如何阻止用户访问其他用户的实体,前提是他知道生成的 ID,这似乎与知道原始 ID 相同?
    • 通过使用 UUID,恶意用户无法使用某种循环来生成 ID (1..9999) 并删除该范围内的所有实体,例如在 DB 中。
    • 我明白了,确实很难随机或通过蛮力找到有效的 ID。
    【解决方案2】:

    在我看来,加密 ID 并不是一个好主意,更像是隐藏真正的问题。干净地做起来可能会很棘手。并且恶意用户仍然可以拦截其他用户的请求并使用加密的 Id 进行攻击。

    真正的解决方案是在您的业务逻辑中实施某种访问控制,并拒绝尝试访问未经授权的资源,例如属于另一个用户的实体。

    如果它很简单,您可以自己实现这个逻辑(没有属于多个用户的共享实体,没有组,只有属于一个用户的实体,这应该很简单)。

    您可以将其实现为一种拦截器(例如使用面向方面,向您的 DAO 或服务方法添加方面),以便自动执行并避免过多重复的样板代码。

    您还可以使用具有一些访问控制机制的 Spring Security。

    如果需求更复杂,可以使用 Spring Security 在您的域对象上实现完整的 ACL(访问控制列表)系统。这比较复杂,因为 ACL 是单独存储的,所以需要在数据库中添加一些额外的基础设施,并且正确配置似乎相当复杂,但在我看来,它是更灵活和可扩展的解决方案。不过,我自己还没有实现 ACL,所以我不能对此提供太多具体的建议。

    如果您坚持对用户隐藏 ID,我建议您不要真正加密 ID,而是使用真实 ID 和一些随机生成的临时 ID 之间的每个会话对应表。这样可以避免频繁地加密/解密 ID,并使一个可见的 ID 对另一个用户完全无用。

    希望这会有所帮助。

    【讨论】:

    • 您使用 AOP 来检查实体是否确实属于某个用户的想法非常好,我将实施它!你认为我可以取消加密吗?
    • 谢谢。我认为那时加密的用处会小得多,但它不能提供更多的安全性并实施 Paulius 的提议。
    猜你喜欢
    • 2011-03-12
    • 1970-01-01
    • 2018-06-17
    • 2021-10-11
    • 2018-08-04
    • 2011-09-14
    • 2016-02-29
    • 1970-01-01
    • 2012-07-21
    相关资源
    最近更新 更多