Quase todas as semanas, lemos que um site no qual alguns de nós confiávamos foi violado. E normalmente, eles apenas dizem algo como: “não se preocupe, apenas seus dados pessoais foram violados, não suas informações de pagamento”. Honestamente, eu prefiro que meus dados de pagamento vazem do que minhas informações pessoais, pois posso facilmente pedir ao meu banco para cancelar meu cartão de crédito e emitir um novo cartão de crédito.
Você, como usuário, não preferiria ouvir algo assim? “Todos os nossos dados vazaram, mas criptografamos tudo com uma chave privada armazenada no servidor do aplicativo, não no banco de dados. Alteramos a chave e criptografamos novamente os dados desde que soubemos da violação ” Você, como provedor de serviços, tem essa opção e não é tão difícil quanto parece.
Em Java, você pode usar o UserType
recurso do Hibernate para dizer ao Hibernate como armazenar e restaurar dados do banco de dados, sem nenhum esforço para o consumidor desses dados (como, seu front end). O exemplo a seguir usa JPA, Hibernate UserType
e Jasypt , uma biblioteca muito boa para lidar com criptografia e hashing.
O primeiro passo é adicionar s personalizados do JasyptUserType
acessíveis ao Hibernate (eles estão localizados no módulo Maven org.jasypt:jasypt-hibernate4
):
Arquivo: src / main / java /…/ entity / package-info.java
@TypeDefs({
@TypeDef(
name="encryptedString",
typeClass=EncryptedStringType.class,
parameters= {
@Parameter(name="encryptorRegisteredName", value="defaultStringEncryptor")
}
)
})
package com.cascaio.appinfo.v1.entity;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.jasypt.hibernate4.type.EncryptedStringType;
Agora, vamos dizer a Jasypt o que queremos dizer com defaultStringEncryptor
. Se você estiver implementando seu aplicativo em um servidor de aplicativos Java EE moderno (como JBoss AS 7.x / Wildfly), você pode fazer isso como um @Startup @Singleton
EJB. Neste exemplo, temos dois criptografadores: um criptografador de senha e nosso defaultStringEncryptor
. Para nossa implementação específica, usamos PBEWITHSHA256AND256BITAES-CBC-BC"
, de BouncyCastle ( org.bouncycastle:bcprov-jdk15on
). Observe que passamos a chave privada por meio de uma variável de ambiente. Como alternativa, também procuramos uma propriedade de sistema específica (-Dfoo = bar), útil para nossos testes de unidade.
Arquivo: src / main / java /…/ control / JasyptConfigurator.java
package com.cascaio.appinfo.v1.control;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.hibernate4.encryptor.HibernatePBEEncryptorRegistry;
import org.jasypt.util.password.PasswordEncryptor;
import org.jasypt.util.password.StrongPasswordEncryptor;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.inject.Produces;
/**
* User: jpkrohling
* Date: 2013-05-11 1:19 PM
*/
@Startup
@Singleton
public class JasyptConfigurator {
private StandardPBEStringEncryptor defaultStringEncryptor;
private PasswordEncryptor passwordEncryptor;
@PostConstruct
public void configureJasypt() {
// by default, we get from the system's ENV vars
String password = System.getenv("CASCAIO_APPINFO_JASYPT_PASSWORD");
// fallback: system properties (like, -Dcascaio.....=bla)
if (null == password || password.isEmpty()) {
password = System.getProperty("cascaio.appinfo.jasypt.password");
}
// cannot determine the password, fail! we don't want to set an empty password
if (null == password || password.isEmpty()) {
throw new RuntimeException("Cannot configure Jasypt, as the encryption password is empty!");
}
this.defaultStringEncryptor = new StandardPBEStringEncryptor();
this.defaultStringEncryptor.setProvider(new BouncyCastleProvider());
this.defaultStringEncryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC");
this.defaultStringEncryptor.setKeyObtentionIterations(1000);
this.defaultStringEncryptor.setPassword(password);
HibernatePBEEncryptorRegistry registry = HibernatePBEEncryptorRegistry.getInstance();
registry.registerPBEStringEncryptor("defaultStringEncryptor", defaultStringEncryptor);
this.passwordEncryptor = new StrongPasswordEncryptor();
}
@Produces
public StandardPBEStringEncryptor getDefaultStringEncryptor() {
if (null == defaultStringEncryptor) {
configureJasypt();
}
return defaultStringEncryptor;
}
@Produces
public PasswordEncryptor getPasswordEncryptor() {
if (null == passwordEncryptor) {
configureJasypt();
}
return passwordEncryptor;
}
}
Então, você está pronto para anotar os campos que deseja criptografar em seu banco de dados, como este:
Arquivo: src / main / java /…/ Application.java
package com.cascaio.appinfo.v1.entity;
import com.cascaio.appinfo.v1.control.JasyptConfigurator;
import org.hibernate.annotations.Type;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import java.util.UUID;
/**
* User: jpkrohling
* Date: 2013-05-11 1:11 PM
*/
@Entity
public class Application {
@Id
private String id = UUID.randomUUID().toString();
@NotNull
@Type(type = "encryptedString")
private String name;
@NotNull
private String accessKey;
@NotNull
@Type(type = "encryptedString")
private String secretKey;
protected Application() {}
public Application(String name, String accessKey, String secretKey) {
this(UUID.randomUUID().toString(), name, accessKey, secretKey);
}
public Application(String id, String name, String accessKey, String secretKey) {
this.id = id;
this.name = name;
this.accessKey = new JasyptConfigurator().getPasswordEncryptor().encryptPassword(accessKey);
this.secretKey = secretKey;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getAccessKey() {
return accessKey;
}
public String getSecretKey() {
return secretKey;
}
}
Neste exemplo, a representação do banco de dados para as propriedades name
e secretKey
são criptografadas ao serem armazenadas no banco de dados e descriptografadas ao serem restauradas. Você também pode querer dar uma olhada no teste de unidade testNameAndAccessKeyAreEncrypted
, para verificar se a própria entidade tem as propriedades criptografadas / descriptografadas sob demanda.
Existem algumas pegadinhas, no entanto:
- Você não pode usar os campos criptografados nas
where
cláusulas. Meio óbvio, mas vale a pena mencionar. - Isso reduz sua capacidade de fazer alterações manuais no banco de dados.
- Isso tornará as operações do banco de dados mais lentas e consumirá mais espaço, mas você sempre pode ajustar o criptografador e / ou o banco de dados. Além disso, é um preço justo a pagar pela segurança extra.