Skip to content

femcodersclub/encrypted-private-notes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Encrypted Private Notes

Editor de notas con encriptación extremo a extremo construido con Web APIs nativas del navegador. Sin dependencias externas. Sin servidor. Sin base de datos remota.

Este proyecto está hecho para explicar el post Web APIs de nueva generación en JavaScript: más allá del localStorage | femCoders Club.

Proyecto de la serie JavaScript Avanzado de FemCoders Club.


Qué hace este proyecto

  • Encripta cada nota con AES-GCM-256 antes de guardarla — el texto plano nunca toca IndexedDB
  • Deriva la clave a partir de tu contraseña usando PBKDF2 (310.000 iteraciones, recomendación OWASP 2023)
  • Persiste las notas encriptadas en IndexedDB — funcionan offline, sin servidor
  • Exporta e importa notas como ficheros .enc usando File System Access API
  • La contraseña vive solo en memoria — se borra al cerrar o recargar la página

APIs nativas usadas

API Para qué
crypto.subtle (Web Crypto API) Derivación de clave PBKDF2 + encriptación AES-GCM
indexedDB Persistencia de notas encriptadas
showSaveFilePicker / showOpenFilePicker Export/import de ficheros .enc

Estructura del proyecto

encrypted-private-notes-js/
├── src/
│   ├── crypto.js       # Web Crypto API: deriva clave, encripta, desencripta
│   ├── storage.js      # IndexedDB: CRUD de notas encriptadas
│   ├── fileSystem.js   # File System Access API: export/import .enc
│   └── app.js          # Orquestador: sesión, notas, export/import
├── index.html          # UI funcional lista para usar y publicar en GitHub Pages
├── tests/
│   └── crypto.test.js  # 16 tests con assert nativo de Node.js
└── package.json

Interfaz de usuario

Así se ve la app en acción:

Encrypted Private Notes — Interfaz de usuario

Pantalla de bloqueo: Ingresa tu contraseña maestra para desbloquear tus notas. La contraseña nunca se almacena — solo sirve para derivar la clave de encriptación.

Editor: Una vez desbloqueado, edita, crea, importa o exporta notas encriptadas. Todo el contenido se encripta con AES-GCM-256 antes de guardarse en IndexedDB.

Cómo ejecutar

App en navegador

npm run start

Abre http://localhost:3000 en Chrome o Edge (Firefox tiene soporte parcial de File System Access API). También puedes abrir index.html directamente, pero para probar File System Access API es mejor servirlo por HTTP.

GitHub Pages

El proyecto ya está preparado para publicarse desde la raíz del repositorio. El HTML principal carga ./src/app.js, así que puedes desplegar la rama main o usar la carpeta raíz como origen de Pages sin necesidad de una carpeta demo.

Tests

node tests/crypto.test.js

Requiere Node.js 19+. La Web Crypto API está disponible como global desde esa versión.

Cómo funciona la encriptación

contraseña  +  salt aleatorio
        ↓
      PBKDF2 (SHA-256, 310.000 iteraciones)
        ↓
    CryptoKey AES-GCM-256
        ↓
   encrypt(texto + IV aleatorio)
        ↓
  { salt, iv, ciphertext }  →  IndexedDB

Cada nota tiene su propio salt e IV generados aleatoriamente. Esto significa que dos notas encriptadas con la misma contraseña producen ciphertexts completamente distintos — no hay patrones que filtren información.

Flujo del fichero .enc exportado

{
  "version": 1,
  "salt": "base64...",
  "iv": "base64...",
  "ciphertext": "base64...",
  "title": "Mi nota",
  "exportedAt": "2025-01-15T10:30:00.000Z"
}

El fichero no contiene el texto plano. Para leerlo hace falta la contraseña original.

Decisiones de diseño

¿Por qué no guardar la contraseña en sessionStorage? sessionStorage es texto plano accesible desde JavaScript. Guardarla ahí anula la encriptación. La contraseña vive en una variable de módulo en memoria — se pierde al recargar, que es exactamente el comportamiento correcto.

¿Por qué PBKDF2 y no bcrypt/argon2? Web Crypto API no implementa bcrypt ni argon2. PBKDF2 con 310.000 iteraciones (recomendación OWASP 2023 para SHA-256) es la opción segura disponible de forma nativa en el navegador.

¿Por qué un IV nuevo por cada encriptación? Reutilizar el mismo IV con la misma clave en AES-GCM es una vulnerabilidad crítica. Cada llamada a encryptNote() genera un IV aleatorio de 12 bytes.


FemCoders Club · femcodersclub.com

About

End-to-end encrypted notes with Web Crypto API • Zero dependencies • GitHub Pages ready

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors