Skip to content

Missing Tenant Isolation and Orphaned Data in Equipment Module (IDOR) #117

Description

@diksha78dev

Problem Description

The Equipment module currently lacks tenant-level data isolation. Although Equipment is modeled to belong to a specific Hospital, the service layer ignores this association. This leads to two critical vulnerabilities:

  1. Any authenticated hospital user can view, edit, or delete equipment records belonging to any other hospital.
  2. Due to the JPA mapping configuration (insertable = false, updatable = false on the hospital relationship), all newly created equipment via the POST /api/equipment endpoint is saved without a hospital_id, resulting in permanently orphaned records in the database.

Steps to Reproduce

To reproduce unauthorized data modification:

  1. Authenticate as a user belonging to "Hospital A" and obtain a token.
  2. Send a GET /api/equipment request to fetch the global inventory.
  3. Identify the {id} of an equipment record that belongs to "Hospital B".
  4. Send a DELETE /api/equipment/{id} or PUT /api/equipment/{id} request using Hospital A's token.
  5. The request succeeds, modifying or destroying Hospital B's data.

To reproduce orphaned data creation:

  1. Authenticate as a hospital user.
  2. Send a POST /api/equipment request with a valid payload to add new equipment.
  3. Query the underlying database for the newly created record.
  4. Observe that the hospital_id column is NULL.

Expected Behavior

  • GET /api/equipment should only return equipment associated with the currently authenticated user's hospital.
  • DELETE and PUT operations should verify that the requested equipment belongs to the authenticated user's hospital, throwing an authorization error if there is a mismatch.
  • POST /api/equipment should successfully link the newly created equipment to the authenticated user's hospital in the database.

Actual Behavior

  • GET /api/equipment exposes all equipment across all hospitals globally.
  • DELETE and PUT operations execute directly via the repository without verifying ownership, allowing cross-tenant data tampering.
  • New equipment added via POST is saved with a null hospital_id, failing to associate the record with the hospital that created it.

Evidence from Code

In EquipmentService.java:

    public void deleteEquipment(Long id) {
        // Blindly deletes the record without resolving security context or checking ownership
        equipmentRepository.deleteById(id);
    }

    public Equipment addEquipment(Equipment equipment) {
        // ...
        // Saves directly without establishing the Hospital relationship
        return equipmentRepository.save(equipment);
    }

In Equipment.java:

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id", insertable = false, updatable = false)
    private Hospital hospital; // Prevents hospital_id from being saved when equipment is persisted directly

In EquipmentController.java:

    @DeleteMapping("/{id}")
    @PreAuthorize("hasRole('HOSPITAL')") // Only checks role, not tenant/ownership
    public ResponseEntity<Void> deleteEquipment(@PathVariable Long id) { ... }

Environment

  • Backend: Spring Boot 3.x
  • Spring Data JPA

Impact

This breaks data isolation across the application. It allows unauthorized users to tamper with or destroy medical equipment records belonging to other institutions. Additionally, it corrupts the database by permanently creating orphaned data entries upon every new equipment insertion, rendering the equipment unusable for hospital-specific workflows.

Additional Notes

The issue stems from a disconnect between the JPA relationship mapping and the service layer logic. Filtering repository queries using the current user's security context and properly establishing the Hospital to Equipment association prior to saving will resolve both the unauthorized access and the orphaned record creation.

Metadata

Metadata

Assignees

Labels

ECSoc26Required for ECSOC SentinelL3Hard (Advanced)enhancementNew feature or requestgood-backendExcellent backend implementationgood-issueGood issue

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions