Enhancing Data Privacy Protection in Indonesia: Integrating Next.js with PgCrypto for Secure Solutions
This story start when in my current jobs there are regulations to add some security in data privacy because of the government regulations. The government regulations is about personal data protection law in Indonesia which passed in 2022. Personal data is any data about an individual that can identify them directly or indirectly, which includes names, identification numbers, location data, and online identifiers.
To comply with these regulations, I conducted thorough research and identified what I believe to be the most effective approach for safeguarding user data and preventing network breaches. My approaches like in the image below.
I’ll explain it in three parts,
1. PostgreSQL
Implement encryption in PostgreSQL is a huge challenge. There are several information we need to consider, such as performance, extensions, and data type. Therefore, PostgreSQL itself had a built in function which the name is pgcrypto. References: https://www.postgresql.org/docs/current/pgcrypto.html
This extension is supported for PostreSQL versions higher than 12. There are some key functions available with pgcrypto such as
a. Encryption and Decryption
b. Hashing functions
c. Random Data Generation
d. Key Management
e. Data Integrity
In this story, we only focus on point a. How to encrypt and decrypt our personal data information in database.
Explanation
install extensions
CREATE EXTENSION IF NOT EXISTS pgcrypto;
Run sample code to ensure that your pgcrypto is work properly
SELECT encode(pgp_sym_encrypt('hello world', 'keynya'), 'hex') AS encrypted_data;
Based on image above pgp_sym_encrypt
is used to encrypt the information plaint text into cipher, which the first arguments
is the plain text and the second arguments
is the key of our cipher to decrypt or encrypt information.
Implementation
My sample data users in database is such as image below which the data is plain text, and don’t forget to change the data type into text
to make sure there no missing information if we use varchar because the text is too long
.
To convert it into cipher we will do command update
to encrypt that name informations using this command
UPDATE users SET "name" = encode(pgp_sym_encrypt("name", 'keynya'), 'hex')
Check it again using command SELECT SQL
Done, our columns name is changed into cipher information. 😁😁
2. Next.js
In this story implement with my next versions is 14, page router
concept, and implement server actions
with axios
. Task of the Next.js is only for decrypt the information from PostgreSQL but in the client side. Therefore, to handle that we need to install library which support to decrypt the postgresql information using openpgp.js.
Explanation
To install that library run this command
npm install --save openpgp
OpenPGP is an encryption standard used to secure emails, files, and other data. With OpenPGP
you can allow for Encryption
, Digital Signatures,
and Key Management
.
Scenario
a. Encryption
Based on the image above, the encryption process is do by client side using openpgp
then send it to backend using server actions. Sample code client side encryption.
const onSubmit = forms.handleSubmit(async (values) => {
const formData = new FormData();
forms.setValue("exclude.dataUser.phone", "");
forms.setValue("exclude.dataUser.alasan_pembuatan_akun", "");
forms.setValue("exclude.dataUser.name", await encryptAesDBKey(values.exclude.dataUser.name));
forms.setValue("exclude.dataUser.birth_date", await encryptAesDBKey(values.exclude.dataUser.birth_date));
forms.setValue("exclude.dataUser.ktp", await encryptAesDBKey(values.exclude.dataUser.ktp));
forms.setValue("exclude.dataUser.gender", await encryptAesDBKey(values.exclude.dataUser.gender));
})
const encryptAesDBKey = async (data: string) => {
try {
const message = await createMessage({ text: data });
// Encrypt the message
const encrypted = await encrypt({
message,
passwords: ["d9bdc6209164d622"],
format: "binary",
});
// Convert the encrypted message to hex
const encryptedHex = Buffer.from(new Uint8Array(encrypted)).toString("hex");
return encryptedHex;
} catch (error) {
return data;
}
};
validation ciphertext with PgCrypto
SELECT pgp_sym_decrypt(decode('c32e040903087b6a93f785abd8d7e0d9c428f3c909ce7503448ab00a4ee4906380e532e7ffb79a32f28415b08c7fa6dcd23801529a85e57748900191d5b7e8128b1af624ab04d3f95003ba059d3dfa57b8632ae253b6b7ab8ba87f3be8c40d8bc1c55b8365d8d88d8145', 'hex'), 'd9bdc6209164d622')
b. Decryption
To prevent data breaches from network browsers, we need to decrypt the ciphertext to plain text in client side. Sample code to decrypt the information is like below
const processAfterProfile = async (profileData: ProfileData, isIAM: boolean = false) => {
/**Information needed in user and decode it */
const { id, name, email, phone, file_avatar } = profileData.user;
const newUser = {
id,
name: await decryptAesDBKey(name),
email,
phone: await decryptAesDBKey(phone),
file_avatar,
};
};
const decryptAesDBKey = async (key: string) => {
try {
// Decode the hex-encoded data
const encryptedData = Buffer.from(key, "hex");
// Read the encrypted message
const message = await readMessage({
binaryMessage: encryptedData,
});
// Decrypt the message
const { data: decryptedData } = await decrypt({
message,
passwords: ["d9bdc6209164d622"],
format: "utf8",
});
return decryptedData;
} catch (error) {
return key;
}
};