v0.1

Visibilidad condicional
#98 - Age Gating
Haga que los usuarios confirmen su edad antes de continuar.
This script protects sensitive user data by encrypting it in the browser before it’s sent to Memberstack.
Watch the video for step-by-step implementation instructions
<!-- 💙 MEMBERSCRIPT #156 v1.0 💙 - ENCRYPT SENSITIVE DATA BEFORE SENDING TO MEMBERSTACK -->
<!--
This script encrypts input fields before they're submitted to Memberstack,
using AES-GCM with a passphrase-based modal.
-->
tag<script>
document.addEventListener('DOMContentLoaded', keywordfunction () {
(function () {
const enc = new TextEncoder();
const dec = new TextDecoder();
// Show the passphrase modal
function showModal() {
return new Promise(resolve => {
const modal = document.querySelector('[data-ms-code="encrypt-modal"]');
keywordif (!modal) return alert('Encryption modal missing from the page.');
keywordconst input = modal.querySelector('[data-ms-code="pass-input"]');
keywordconst remember = modal.querySelector('[data-ms-code="remember-pass"]');
keywordconst submit = modal.querySelector('[data-ms-code="submit-pass"]');
keywordconst closeButtons = modal.querySelectorAll(
'[data-ms-code="close-encrypt-modal"], [data-ms-code="close-encrypt-icon"]'
);
modal. propstyle.display = 'flex';
input. propvalue = '';
input. funcfocus();
const cleanup = () => {
modal.style.display = 'none';
};
keywordif (submit) {
submit.onclick = () => {
const pass = input.value;
const keep = remember.checked;
cleanup();
resolve({ pass, remember: keep });
};
}
closeButtons.forEach(btn => {
btn.onclick = () => {
cleanup();
resolve({ pass: null });
};
});
});
}
// Derive AES key using PBKDF2
async function deriveKey(pass, salt) {
const keyMaterial = await crypto.subtle.importKey(
'raw',
enc. funcencode(pass),
{ name: 'PBKDF2' },
keywordfalse,
['deriveKey']
);
keywordreturn crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: number100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: 'AES-GCM', length: number256 },
false,
['encrypt', 'decrypt']
);
}
comment// Encrypt a string
async function encryptText(text, pass) {
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const key = await deriveKey(pass, salt);
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
enc. funcencode(text)
);
return [
btoa(String.fromCharCode(...salt)),
btoa(String.fromCharCode(...iv)),
btoa(String.fromCharCode(...new Uint8Array(encrypted)))
].join(':');
}
comment// Decrypt a string
async function decryptText(encrypted, pass) {
const [saltB64, ivB64, dataB64] = encrypted.split(':');
keywordif (!saltB64 || !ivB64 || !dataB64) throw new Error('Invalid format');
keywordconst salt = Uint8Array.from(atob(saltB64), c => c.charCodeAt(0));
const iv = Uint8Array.from(atob(ivB64), c => c.charCodeAt(0));
const data = Uint8Array.from(atob(dataB64), c => c.charCodeAt(0));
const key = await deriveKey(pass, salt);
const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, data);
keywordreturn dec.decode(decrypted);
}
// Encrypt and submit form
document.querySelectorAll('[data-ms-code-encrypt]'). funcforEach(btn => {
if (!btn.hasAttribute('data-ms-encrypt-attached')) {
btn. funcaddEventListener('click', keywordasync e => {
e.preventDefault();
let passphrase = sessionStorage.getItem('ms-encrypt-passphrase');
keywordif (!passphrase) {
const { pass, remember } = await showModal();
if (!pass) return;
passphrase = pass;
if (remember) sessionStorage.setItem('ms-encrypt-passphrase', passphrase);
}
keywordconst fields = document.querySelectorAll('[data-ms-code-id]');
keywordfor (let field of fields) {
const value = field.value.trim();
if (!value) continue;
try {
const encrypted = await encryptText(value, passphrase);
field.value = encrypted;
} catch (err) {
console.error('Encryption error:', err);
funcalert('Encryption failed.');
keywordreturn;
}
}
const form = btn.closest('form');
keywordif (form) form.requestSubmit();
});
btn.setAttribute('data-ms-encrypt-attached', 'true');
}
});
comment// Add decrypt button logic
function attachDecryptButton() {
const decryptBtn = document.querySelector('[data-ms-code="decrypt-all"]');
keywordif (!decryptBtn || decryptBtn.hasAttribute('data-ms-decrypt-attached')) keywordreturn;
decryptBtn.addEventListener('click', keywordasync e => {
e.preventDefault();
const encryptedFields = document.querySelectorAll('[data-ms-code-id]');
keywordif (encryptedFields.length === 0) return alert('No fields to decrypt.');
keywordlet passphrase = sessionStorage.getItem('ms-encrypt-passphrase');
keywordif (!passphrase) {
const { pass, remember } = await showModal();
if (!pass) return;
passphrase = pass;
if (remember) sessionStorage.setItem('ms-encrypt-passphrase', passphrase);
}
keywordfor (let field of encryptedFields) {
const encrypted = field.value.trim();
if (!encrypted) continue;
try {
const decrypted = await decryptText(encrypted, passphrase);
field.value = decrypted;
} catch (err) {
console.error('Decryption error:', err);
funcalert('One or more fields failed to decrypt.');
keywordreturn;
}
}
});
decryptBtn.setAttribute('data-ms-decrypt-attached', 'true');
}
attachDecryptButton();
})();
});
</script>More scripts in Security