I have a Spring Boot application that stores payment information in the database. The application has end-points
GET /api/orders
- get orders by filterPOST /api/orders
- add a new orderPUT /api/orders
- update orderDELETE /api/orders
- delete order
These endpoints are not secure itself, and I do not want to secure them on the application level. All traffic from the user goes to HTTPS proxy, which will decrypt it and forward to the application.
However, I use mongo atlas free version for prototyping https://www.mongodb.com/cloud/atlas/faq
All the data I put into the database must be encrypted. Even the document structure (fields names and types) must be encrypted.
How to encrypt a field does not work for me, because I want to encrypt the whole document. I do not want to use unofficial libraries like bellow (thus no one guarantees if the library is secure)
<dependency>
<groupId>com.bol</groupId>
<artifactId>spring-data-mongodb-encrypt</artifactId>
<version>1.3.0</version>
</dependency>
One idea that came to me is to configure the application to use a password (somehow configured or generated at given moment of time).
/**
* Provides a password to encrypt a document.
**/
// TODO How to do it better? I still have to improve it.
@Component
public class PasswordProviderImpl implements PasswordProvider {
private static final byte[] MASTER_PASSWORD = {1, 11, 37, 166, 11, 77};
@Autowired
private Environment environment;
// I do not care about the implementation yet
public char [] getPassword() {
final byte[] envPassword = environment.getProperty("appplicationPassword").toString().toByteArray();
return envPassword;
}
}
/**
* Encrypts input byte array with provided password, afterwards, cleans input data and password - populates them with zeros - 0.
**/
@Component
public class Encryptor {
private PasswordProvider passwordProvider;
public byte [] encrypt(final byte unecrypted) {
final byte [] password = passwordProvider.getPassword();
final byte [] encrypted = xor(unecrypted, password);
makeZeros(password);
makeZeros(unecrypted);
return encrypted;
}
private void makeZeros(final byte[] array) { /*Implementation*/}
private byte[] xor(final byte [] arg1, final byte[] arg2) {/*Implementation*/}
}
/**
* Represents a unit of data in my application. Its values and fields and structure - everything should be encrypted.
**/
public class Order {
private ObjectId id;
private Instant createdDate;
private Instant updatedDate;
private Money amount;
private String additionalDetails;
// gettters, setters, constructor
}
Converter, found Set MongoDb converter programmatically
/**
* It is declared application configuration. It defines how to store {@link Order} in mongodb
**/
@Component
public OrderConverter implements onverter<Order, SecuredOrder> {
private ObjectMapper objectMapper;
private Encryptor encryptor;
private Base64Converter base64Converter;
@Override
public SecuredOrder convert(Order source) {
final String json = objectMapper.writeValueAsString(source);
final unencrypted = json.toByteArray();
final byte[] encrypted = encryptor.encrypt(unencrypted);
final String payload = base64Converter.toBase64(encrypted);
return SecuredOrder.of(order.getId(), payload);
}
}
public class SecuredOrder {
private ObjectId id; // same as in order id
private String encryptedPayload; // The converter will make it
}
@Service
public OrderService {
public void saveOrder(Order order) {
orderRepository.save(order);
}
}
If you have done a similar thing, please give me a direction. I would really like to do it properly.
Also, mongodb provides encryption mechanism, so maybe I should use it?
https://docs.mongodb.com/manual/core/security-encryption-at-rest/
https://docs.mongodb.com/manual/core/security-encryption-at-rest/#encrypted-storage-engine