Shield Your Secrets: A Practical Guide to Spring Boot Data Masking

Naveen Metta
3 min readMay 22, 2024
credit goes to the owner : https://www.delphix.com/blog/leveraging-data-masking-technology
source : delphix.com

In today’s digital age, data security is paramount. With increasing regulations and data breaches, it’s crucial to protect sensitive information, especially when dealing with user data. Data masking is a powerful technique to safeguard this information. This comprehensive guide will walk you through implementing data masking in a Spring Boot application with practical examples.

What is Data Masking?

Data masking is the process of hiding original data with modified content (characters or other data). The main purpose is to protect sensitive data while ensuring it is still usable for various purposes like testing and analytics. Masking replaces actual data with fictional data that maintains the format of the original data.

Why Use Data Masking?

  • Security: Prevents unauthorized access to sensitive data.
  • Compliance: Helps in adhering to data privacy regulations such as GDPR, HIPAA, etc.
  • Testing and Development: Provides realistic data without exposing sensitive information.

Implementing Data Masking in Spring Boot

Setting Up Spring Boot

First, let’s set up a basic Spring Boot project. Use Spring Initializr or create the project structure manually.

pom.xml:

<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database for testing -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

Creating the Entity

Let’s create an entity that contains sensitive information. For this example, we’ll use a User entity.

User.java:

package com.example.demo.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String phoneNumber;

// Getters and Setters
}

Implementing Data Masking Logic

We’ll create a utility class for data masking.

DataMaskingUtil.java:

package com.example.demo.util;

public class DataMaskingUtil {

public static String maskEmail(String email) {
if (email == null || !email.contains("@")) {
return email;
}
String[] parts = email.split("@");
String maskedLocal = parts[0].replaceAll(".", "*");
return maskedLocal + "@" + parts[1];
}

public static String maskPhoneNumber(String phoneNumber) {
if (phoneNumber == null || phoneNumber.length() < 10) {
return phoneNumber;
}
return phoneNumber.substring(0, 2) + "******" + phoneNumber.substring(phoneNumber.length() - 2);
}

public static String maskName(String name) {
if (name == null || name.length() < 2) {
return name;
}
return name.charAt(0) + "*".repeat(name.length() - 1);
}
}

Applying Data Masking

We can apply the masking logic before returning the data from the controller. Here’s how to do it using a DTO (Data Transfer Object).

UserDTO.java:

package com.example.demo.dto;

public class UserDTO {
private Long id;
private String name;
private String email;
private String phoneNumber;

// Getters and Setters
}

UserService.java:

package com.example.demo.service;

import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import com.example.demo.util.DataMaskingUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

public List<UserDTO> getAllUsers() {
List<User> users = userRepository.findAll();
return users.stream().map(this::convertToDTO).collect(Collectors.toList());
}

private UserDTO convertToDTO(User user) {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setName(DataMaskingUtil.maskName(user.getName()));
dto.setEmail(DataMaskingUtil.maskEmail(user.getEmail()));
dto.setPhoneNumber(DataMaskingUtil.maskPhoneNumber(user.getPhoneNumber()));
return dto;
}
}

UserController.java:

package com.example.demo.controller;

import com.example.demo.dto.UserDTO;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private UserService userService;

@GetMapping
public List<UserDTO> getUsers() {
return userService.getAllUsers();
}
}

Running the Application

Now, run the application and navigate to http://localhost:8080/users. You should see the masked data.

Testing the Implementation

To ensure our data masking is working as expected, we can write unit tests.

UserServiceTest.java:

package com.example.demo.service;

import com.example.demo.dto.UserDTO;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {

@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;

@Test
public void testGetAllUsers() {
User user1 = new User();
user1.setId(1L);
user1.setName("John Doe");
user1.setEmail("john.doe@example.com");
user1.setPhoneNumber("1234567890");

User user2 = new User();
user2.setId(2L);
user2.setName("Jane Smith");
user2.setEmail("jane.smith@example.com");
user2.setPhoneNumber("0987654321");

when(userRepository.findAll()).thenReturn(Arrays.asList(user1, user2));

List<UserDTO> result = userService.getAllUsers();

assertEquals(2, result.size());
assertEquals("J***", result.get(0).getName());
assertEquals("****@example.com", result.get(0).getEmail());
assertEquals("12******90", result.get(0).getPhoneNumber());

assertEquals("J***", result.get(1).getName());
assertEquals("****@example.com", result.get(1).getEmail());
assertEquals("09******21", result.get(1).getPhoneNumber());
}
}

Conclusion

Data masking is a critical technique for protecting sensitive information in your applications. By following this guide, you’ve learned how to implement data masking in a Spring Boot application. This process included setting up the project, creating entities, applying masking logic, and verifying the implementation with tests.

By integrating data masking, you enhance your application’s security and compliance, ensuring that sensitive information remains protected while still being useful for non-production environments. Happy coding!

--

--

Naveen Metta

I'm a Full Stack Developer with 2.5 years of experience. feel free to reach out for any help : mettanaveen701@gmail.com