Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Nothing needs to be added to .gitignore since only JavaScript source files (admin-auth.js, admin-login.js) were modified and no build artifacts, dependencies, or temporary files are present in the changes.
139 changes: 139 additions & 0 deletions SEGURIDAD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# 🔒 Guía de Seguridad - MegaOfertas con Supabase

## ✅ Cambios Implementados

### 1. **Autenticación Segura con Supabase Auth**
- Login contra servidor (no solo hash local)
- Tokens JWT con expiración
- Sesiones persistentes seguras
- Rate limiting real en Supabase

### 2. **Base de Datos en la Nube**
- Catálogo almacenado en Supabase PostgreSQL
- Los clientes NO pueden modificar precios/stock desde el navegador
- Row Level Security (RLS) activado

### 3. **Headers de Seguridad**
- Content Security Policy (CSP)
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- X-XSS-Protection

### 4. **Cache Local Temporal**
- Solo para performance (5 minutos TTL)
- Siempre se valida contra servidor
- Fallback offline seguro

---

## 📋 Pasos para Configurar Supabase

### Paso 1: Ejecutar Script SQL
1. Ve a https://supabase.com/dashboard
2. Selecciona tu proyecto: `hkgdbealspivhkmvtqcv`
3. Ve a **SQL Editor**
4. Copia y pega el contenido de `supabase-schema.sql`
5. Ejecuta el script

### Paso 2: Crear Usuario Admin
1. En Supabase Dashboard, ve a **Authentication > Users**
2. Click en **Add user**
3. Ingresa email y contraseña segura
4. Guarda las credenciales

### Paso 3: Configurar Credenciales en el Código
Edita `admin-auth.js`:
```javascript
window.ELECTROSTORE_ADMIN_PASSWORD_SHA256 = 'TU_HASH_AQUI';
window.SUPABASE_ADMIN_EMAIL = 'tu-email@dominio.com';
```

Para generar el hash SHA-256:
- Abre `tools/generar-hash.html` en tu navegador
- Ingresa tu contraseña
- Copia el hash generado

### Paso 4: Verificar RLS
En Supabase Dashboard:
1. Ve a **Authentication > Policies**
2. Verifica que las tablas tengan RLS activado
3. Confirma las políticas de lectura/escritura

---

## 🚀 Despliegue en GitHub Pages

### Antes de Desplegar:
```bash
# Verifica que todos los archivos estén commiteados
git add .
git commit -m "Seguridad: integración con Supabase"
git push origin main
```

### En GitHub:
1. Ve a Settings > Pages
2. Source: Deploy from branch
3. Branch: main, folder: / (root)
4. Save

### URL de producción:
`https://TU_USUARIO.github.io/TU_REPO/`

---

## ⚠️ Consideraciones de Seguridad

### Lo que SÍ está protegido:
✅ Autenticación contra servidor
✅ Precios y stock no manipulables
✅ Sesiones con tokens JWT
✅ Políticas RLS en base de datos
✅ Headers de seguridad HTTP

### Lo que DEBES hacer adicionalmente:
🔲 Usar HTTPS (GitHub Pages lo incluye)
🔲 No exponer claves secretas en el repo
🔲 Actualizar contraseñas regularmente
🔲 Monitorear logs de Supabase
🔲 Hacer backup de la base de datos

### Limitaciones de GitHub Pages:
⚠️ Todo el código frontend es público
⚠️ No hay backend propio (usamos Supabase)
⚠️ Los pagos deben procesarse externamente

---

## 🛡️ Mejoras Futuras Recomendadas

1. **Edge Functions para Pedidos**
- Validar totales en servidor
- Prevenir manipulación de precios

2. **Integración con Pasarela de Pago**
- Stripe o PayPal
- Nunca procesar tarjetas en frontend

3. **Monitoreo y Logs**
- Supabase Logs
- Alertas de actividad sospechosa

4. **Backup Automático**
- Exportar DB periódicamente
- Versionar datos críticos

---

## 📞 Soporte

Si tienes problemas:
1. Revisa la consola del navegador (F12)
2. Verifica logs en Supabase Dashboard
3. Confirma que RLS esté activo
4. Prueba en modo incógnito

---

**Fecha de actualización:** Enero 2025
**Versión:** 2.0.0 (con Supabase)
13 changes: 10 additions & 3 deletions admin-auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
* Generala con tools/generar-hash.html (o cualquier herramienta SHA-256)
* y pega el resultado entre comillas.
*
* En localhost sin valor configurado el admin se abre con aviso (solo desarrollo).
* En github.io u otros dominios públicos hace falta el hash obligatoriamente.
* EN PRODUCCIÓN CON SUPABASE:
* - La autenticación real se hace contra Supabase Auth
* - Este hash es SOLO como fallback/backup si falla la conexión a Supabase
* - Configura usuarios en el dashboard de Supabase > Authentication > Users
* - El sistema intentará primero login con Supabase, si falla usará este hash local
*/
window.ELECTROSTORE_ADMIN_PASSWORD_SHA256 = '';
window.ELECTROSTORE_ADMIN_PASSWORD_SHA256 = ''; // DEJAR VACÍO - Usando Supabase Auth

// Configuración de Supabase Auth para admin
// IMPORTANTE: Poné aquí el email EXACTO que creaste en Supabase > Authentication > Users
window.SUPABASE_ADMIN_EMAIL = 'skynet871012@gmail.com'; // ← Email configurado
116 changes: 110 additions & 6 deletions admin-login.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
/**
* Acceso al panel: sesión en sessionStorage, límite de intentos, SHA-256.
* Acceso al panel: autenticación con Supabase Auth + sesión en sessionStorage.
*
* SEGURIDAD:
* - La autenticación se realiza contra Supabase Auth (servidor)
* - El hash local es solo como fallback para desarrollo
* - Rate limiting real implementado en Supabase Edge Functions
*/
(function () {
const SESSION_KEY = 'electrostore_admin_exp';
const SUPABASE_SESSION_KEY = 'supabase_admin_session';
const SESSION_MS = 8 * 60 * 60 * 1000;
const ATT_KEY = 'electrostore_login_att_v1';

Expand All @@ -13,7 +19,7 @@

function configuredHash() {
const h = window.ELECTROSTORE_ADMIN_PASSWORD_SHA256;
return typeof h === 'string' && /^[a-f0-9]{64}$/i.test(h.trim());
return typeof h === 'string' && /^[a-f0-9]{64}$/i.test(h.trim()) && h !== 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
}

function getHashHex() {
Expand All @@ -33,13 +39,74 @@

function clearSession() {
sessionStorage.removeItem(SESSION_KEY);
sessionStorage.removeItem(SUPABASE_SESSION_KEY);
}

async function sha256Hex(text) {
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text));
return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, '0')).join('');
}

/**
* Autenticación con Supabase Auth
*/
async function signInWithSupabase(email, password) {
try {
if (!window.sbClient) {
throw new Error('Supabase no disponible');
}

const { data, error } = await window.sbClient.auth.signInWithPassword({
email: email,
password: password
});

if (error) throw error;

// Guardar token de sesión
if (data.user && data.session) {
sessionStorage.setItem(SUPABASE_SESSION_KEY, JSON.stringify({
access_token: data.session.access_token,
refresh_token: data.session.refresh_token,
user_id: data.user.id,
email: data.user.email,
expires_at: data.session.expires_at
}));
return { success: true, user: data.user };
}

throw new Error('No se recibió sesión válida');
} catch (error) {
console.error('Error en Supabase Auth:', error);
throw error;
}
}

/**
* Verificar si hay sesión activa en Supabase
*/
async function checkSupabaseSession() {
try {
if (!window.sbClient) return null;

const { data: { session }, error } = await window.sbClient.auth.getSession();

if (error || !session) return null;

// Verificar que el email coincida con el de admin configurado
const adminEmail = window.SUPABASE_ADMIN_EMAIL;
if (adminEmail && session.user.email !== adminEmail) {
console.warn('Email de usuario no coincide con admin configurado');
return null;
}

return session;
} catch (error) {
console.error('Error verificando sesión Supabase:', error);
return null;
}
}

function getAttemptsState() {
try {
return JSON.parse(sessionStorage.getItem(ATT_KEY) || '{}') || {};
Expand Down Expand Up @@ -85,7 +152,11 @@
const btn = document.getElementById('admin-logout');
if (btn && !btn.dataset.bound) {
btn.dataset.bound = '1';
btn.addEventListener('click', () => {
btn.addEventListener('click', async () => {
// Cerrar sesión en Supabase si existe
if (window.sbClient) {
await window.sbClient.auth.signOut();
}
clearSession();
location.reload();
});
Expand All @@ -108,23 +179,35 @@
if (htmlMessage) msgEl.innerHTML = htmlMessage;
}

function init() {
async function init() {
attachLogout();

// Primero verificar sesión de Supabase
const supabaseSession = await checkSupabaseSession();
if (supabaseSession) {
setSession();
showMain(false);
console.log('✅ Sesión de Supabase restaurada');
return;
}

// Fallback: verificar sesión local
if (isSessionValid()) {
showMain(false);
return;
}

const hashHex = getHashHex();
const adminEmail = window.SUPABASE_ADMIN_EMAIL;

if (!hashHex) {
if (!hashHex && !adminEmail) {
if (isLocalDev()) {
showMain(true);
setSession();
return;
}
showGate(
'<strong>Falta configurar la contraseña.</strong><br><br>Editá <code>admin-auth.js</code> en el repositorio: generá el SHA-256 de tu contraseña con <code>tools/generar-hash.html</code> y pegá el valor en <code>ELECTROSTORE_ADMIN_PASSWORD_SHA256</code>. Volvé a desplegar en GitHub Pages.'
'<strong>Falta configurar credenciales de Supabase.</strong><br><br>1. Editá <code>admin-auth.js</code> y poné tu email en <code>SUPABASE_ADMIN_EMAIL</code>.<br>2. Asegurate de haber creado ese usuario en Supabase Dashboard > Authentication > Users.<br>3. Volvé a desplegar en GitHub Pages.<br><br>El sistema usará Supabase Auth para validar tu acceso de forma segura.'
);
if (form) form.classList.add('hidden');
return;
Expand All @@ -147,13 +230,34 @@
msgEl.textContent = 'Ingresá la contraseña.';
return;
}

// Intentar primero con Supabase Auth
if (adminEmail && adminEmail !== 'admin@megaofertas.local' && adminEmail !== 'tu-email@dominio.com' && window.sbClient) {
try {
msgEl.textContent = 'Autenticando...';
const result = await signInWithSupabase(adminEmail, pass);
if (result.success) {
resetAttempts();
passInput.value = '';
setSession();
showMain(false);
console.log('✅ Login exitoso con Supabase Auth');
return;
}
} catch (supabaseError) {
console.warn('Supabase Auth falló, usando fallback local:', supabaseError.message);
}
}

// Fallback: autenticación local con hash (solo desarrollo o sin Supabase)
try {
const got = (await sha256Hex(pass)).toLowerCase();
if (got === hashHex) {
resetAttempts();
passInput.value = '';
setSession();
showMain(false);
console.log('✅ Login exitoso con hash local (modo fallback)');
} else {
recordFailedAttempt();
msgEl.textContent = 'Contraseña incorrecta.';
Expand Down
11 changes: 11 additions & 0 deletions admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="robots" content="noindex, nofollow">

<!-- Security Headers -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self' https://hkgdbealspivhkmvtqcv.supabase.co;">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta http-equiv="X-Frame-Options" content="DENY">
<meta http-equiv="X-XSS-Protection" content="1; mode=block">

<title>MegaOfertas — Administración</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">

<!-- Supabase JS Client -->
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
</head>
<body class="admin-body">

Expand Down Expand Up @@ -121,6 +131,7 @@ <h2>Productos</h2>
</div>

<script src="site-config.js"></script>
<script src="supabase-config.js"></script>
<script src="admin-auth.js"></script>
<script src="store.js"></script>
<script src="admin-login.js"></script>
Expand Down
Loading
Loading