Skip to content

Seguridad y cumplimiento ​

SkylerX se diseña para entornos dev / test / prod a la vez e incorpora un modelo de seguridad extremo a extremo, desde las credenciales hasta el renderizado del resultado, desde el envío del SQL hasta las exportaciones masivas. Esta página repasa cada línea de defensa que realmente está en el código: qué hace, qué no hace y qué evidencias puede dejar para operaciones y auditoría.

1. Resumen ​

El modelo de seguridad se reparte en cinco etapas según el "flujo del dato", cada una con su propio respaldo en código:

EtapaMódulo / ficheroResponsabilidad principal
Persistencia de credencialesapps/desktop/src/main/db/connectionStore.tsCifrado de contraseñas y claves SSH con el llavero del SO (Electron safeStorage)
Identificación de entornopackages/ui/src/connEnv.tsEtiquetas tricolor dev / test / prod + conexión solo lectura + whitelist de lectura
Interceptación de sentenciaspackages/ui/src/sqlLint.ts7 reglas heurísticas (UPDATE/DELETE sin WHERE, DROP/TRUNCATE en prod, etc.)
Presentación del datopackages/ui/src/masking.ts + DataMaskingViewDialogMatching por nombre de columna → masking en render + vistas de desensibilización en BD
Gobierno / auditoríacompliance.ts / PiiScannerDialog / DataContractDialog / export-encrypt.tsComprobaciones de cumplimiento, escaneo PII, contratos de datos, exportación cifrada

A continuación, cada bloque tal y como está en el código.

2. Cifrado de contraseñas de conexión (llavero del SO) ​

Ubicación: apps/desktop/src/main/db/connectionStore.ts.

Al crear/editar una conexión, la contraseña no se guarda en plano en la SQLite; pasa por safeStorage de Electron (macOS = Keychain, Windows = DPAPI, Linux = libsecret / kwallet):

ts
function encryptPassword(plain?: string): string | null {
  if (!plain) return null
  if (safeStorage.isEncryptionAvailable()) {
    return `enc:${safeStorage.encryptString(plain).toString('base64')}`
  }
  return `plain:${Buffer.from(plain, 'utf8').toString('base64')}`
}

Los campos almacenados llevan un prefijo para identificar la versión:

PrefijoSignificadoCuándo aparece
enc:Cifrado con llavero del SOCamino normal en macOS / Windows / la mayoría de Linux
plain:base64 de respaldo (solo dev)Cuando safeStorage.isEncryptionAvailable() devuelve false; típico en contenedores Linux mínimos sin libsecret/kwallet
OtroCompatibilidad con versiones anteriores sin prefijoDatos históricos

Importante: si ves plain:, SkylerX sigue funcionando, pero es equivalente a texto plano. En Linux, instala gnome-keyring o kwallet y pídele al usuario que reedite la conexión (cualquier cambio guardará y forzará el recifrado).

Claves para túnel SSH ​

La configuración SSH incluye password / privateKey / passphrase, y todas usan la misma cadena de cifrado. Al listar las conexiones (listConnections) se eliminan estos campos para no llevar secretos redundantes en memoria:

ts
function decryptSsh(stored, withSecrets) {
  const ssh = JSON.parse(decryptPassword(stored)) as SshConfig
  return withSecrets
    ? ssh
    : { ...ssh, password: undefined, privateKey: undefined, passphrase: undefined }
}

Solo al conectar realmente o al rehidratar el formulario de edición (getConnection) se devuelven las claves completas.

3. Etiqueta de entorno dev / test / prod + protección de producción ​

Ubicación: packages/ui/src/connEnv.ts.

El campo extra.env de la conexión guarda una enumeración tristate:

ValorEtiqueta UIColor (ENV_META.color)Severidad por defecto
devDesarrollo#4caf50 verdeEstándar
testTest#e0a020 naranjaEstándar
prodProducción#e04050 rojoActiva reglas SQL extra + doble confirmación antes de ejecutar

Conexión completa de solo lectura (extra.readOnly) ​

connReadOnly() la etiqueta. SkylerX revisa por dos vías independientes:

  1. A nivel de conexión: isReadOnlyStatement(sql) aplica una whitelist por primera palabra clave (select / with / show / explain / desc(ribe) / pragma) y bloquea las que no estén.
  2. Modo de commit: una conexión de solo lectura fuerza auto (las transacciones manuales no aplican); ver initialCommitMode().

Marca de agua en producción ​

Settings → Marca de agua permite personalizar texto / ángulo / opacidad / color; las conexiones prod muestran una marca SVG sobre el editor SQL, la cuadrícula de resultados y los previews de exportación, dificultando la difusión por capturas.

4. SQL Linter — 7 reglas integradas ​

Ubicación: packages/ui/src/sqlLint.ts.

Escaneo heurístico por cadena, sin parser completo; solo detecta patrones "obviamente peligrosos". Tres niveles:

SeveridadFeedback UI¿Ejecuta?
errorDiálogo modal de doble confirmaciónSe ejecuta solo si el usuario confirma
warnToastSí ejecuta (solo avisa)
infoDecide la integración (badge en el sidebar del editor)Sí ejecuta

Tabla completa:

IDSeveridadDisparadorMensaje
no-where-updateerrorUPDATE + sin WHEREUPDATE sin WHERE: actualizará la tabla entera
no-where-deleteerrorDELETE FROM + sin WHEREDELETE sin WHERE: vaciará la tabla
prod-droperrorconexión env=prod + DROP TABLE/DATABASE/SCHEMA/INDEX/VIEWDROP XXX en producción
prod-truncatewarnconexión env=prod + TRUNCATETRUNCATE en producción
cross-joinwarnSELECT + FROM a, b (JOIN por coma) o JOIN sin ON/USINGMultitabla sin condición de unión (posible producto cartesiano)
select-starinfoSELECT *SELECT *: mejor enumerar columnas
forgotten-limitinfoSELECT sin LIMIT / FETCH FIRST / TOP n / COUNT()SELECT sin LIMIT: podría traer demasiadas filas

Restricciones "baratas" ​

Para descartar comentarios se usan dos regex sencillas (/\/\*[\s\S]*?\*\//g y /--[^\n]*/g); así un -- WHERE 1=1 falso no engaña al linter. Todas las reglas son O(n) sobre la cadena; pueden correr en la ruta crítica sin penalizar al usuario.

Agregación de múltiples sentencias ​

lintStatements(stmts, ctx) conserva un único hallazgo por id, quedándose con la severidad más alta; perfecto para "selecciono y ejecuto un archivo entero".

5. Contratos de datos (notNull / range / regex) ​

Ubicación: packages/ui/src/components/DataContractDialog.vue.

Un contrato de datos define a priori qué valores no deben aparecer en un campo. Cuatro partes:

CampoTipoDescripción
namestringNombre del contrato
tablestringschema.table al que aplica
notNullstring[]Columnas que no pueden ser NULL
rangeRecord<string, [min, max]>Rango numérico; null = sin límite
regexRecord<string, string>Regex obligatorio para el valor
enabledbooleanInterruptor de activación

Se guarda en localStorage.skylerx.dataContracts como array JSON.

Uso típico ​

json
{
  "name": "用户表完整性",
  "table": "public.users",
  "notNull": ["phone", "email"],
  "range": { "age": [0, 150] },
  "regex": { "email": "^[^@]+@[^@]+$", "phone": "^1\\d{10}$" },
  "enabled": true
}

Importar / Exportar ​

  • 📋 Exportar → copia el JSON al portapapeles, lo puedes pegar en doc compartido / git
  • 📥 Importar → pega un JSON y sobrescribe la lista actual

De este modo, el DBA puede repartir los contratos por equipo / proyecto y los desarrolladores los obtienen al importarlos en su SkylerX.

6. Escaneo de campos sensibles (PII Scanner) ​

Ubicación: packages/ui/src/components/PiiScannerDialog.vue.

Heurística en dos pasos: matching por nombre de columna → verificación por muestreo.

Paso de matching por nombre ​

Usa la regex columnPattern de DEFAULT_MASK_RULES (ver siguiente sección). Por ejemplo, una columna user_phone cae en (phone|mobile|tel|手机|电话) y se clasifica como phone.

Paso de verificación por muestreo (opcional) ​

Para las columnas detectadas, toma las primeras N filas (50 por defecto, ajustable 10-1000) y aplica regex de confirmación:

kindRegex de verificación
phone/^\+?[\d\s\-()]{7,20}$/
email/^[^\s@]+@[^\s@]+\.[^\s@]+$/
idCard/^\d{15}$|^\d{17}[\dxX]$/
bankCard/^\d{12,19}$/
name / address / defaultSin verificación; solo por nombre

Si el porcentaje de aciertos es < 30%, se descarta como "coincidencia de nombre, no PII real" y se quita del informe.

Informe y siguientes pasos ​

El informe se agrupa por tabla en orden descendente de hits; se puede 📋 Exportar CSV (columnas: schema/table/column/data_type/rule/kind/sample). El CSV vale para auditoría; también puedes clic derecho en la base → "Generar vista de desensibilización" sobre esas columnas.

7. Vistas de desensibilización (DataMaskingViewDialog) ​

Ubicación: packages/ui/src/masking.ts + packages/ui/src/components/DataMaskingViewDialog.vue.

7.1 Reglas predeterminadas ​

DEFAULT_MASK_RULES es la base; el usuario puede borrar / cambiar / añadir reglas en Settings → Desensibilización.

ReglacolumnPatternkindActiva por defectoAlgoritmo
Teléfono(phone|mobile|tel|手机|电话)phone✅3 iniciales + **** + 4 finales
Email(email|mail|邮箱)email✅Inicial + ***@domain
DNI(id_?card|身份证|idno)idCard✅6 iniciales + *… + 4 finales
Tarjeta bancaria(bank_?card|card_?no|账号|账户)bankCard✅4 + **** **** + 4
Nombre(real_?name|user_?name|full_?name|姓名)name❌Inicial + * (resto oculto)
Dirección(address|addr|地址)address❌6 iniciales + ***
Password / Token(password|passwd|secret|pwd|token|api_?key|密码)default✅2 iniciales + **** + 2 finales

7.2 Masking al renderizar vs. vista de desensibilización en BD ​

SkylerX ofrece dos caminos independientes:

  • Masking en render: Settings → Desensibilización → Activar. El frontend aplica regla → algoritmo al vuelo; no toca la BD, y al exportar puedes elegir "original / enmascarado" en el diálogo.
  • Vista de desensibilización en BD (DataMaskingViewDialog): genera SQL CREATE OR REPLACE VIEW ... AS SELECT mask_expr(c) ... y la aplicación lee desde la vista, no de la tabla original. Seis estrategias:
EstrategiaExpresión SQL (ejemplo MySQL)
raw original`c` AS `c`
md5md5(CAST(`c` AS char(4000))) AS `c`
partial N inicio + M finCONCAT(LEFT(`c`,N), '***', RIGHT(`c`,M)) AS `c`
fixed valor fijo'***' AS \c``
truncate truncarLEFT(`c`, max) AS `c`
nullNULL AS `c`

Al abrir el diálogo, recommendStrategy(colName) sugiere por nombre; el usuario puede sobreescribir por columna. El SQL se puede editar antes de pulsar ▶ Crear vista.

8. Comprobación de cumplimiento (Cumplimiento GB17859, 等保 2.0) ​

Ubicación: packages/ui/src/compliance.ts + packages/ui/src/components/ComplianceDialog.vue.

Solo cubre lo que se puede verificar desde la conexión a la BD; no entra en firewall / cifrado de disco u otros aspectos del SO. Cuatro estados:

SeveridadSignificado
pass ✅Cumple
warn ⚠️No cumple, pero riesgo controlable (audit log no habilitado, SSL apagado, etc.)
fail ❌Incumplimiento grave (root remoto abierto, usuarios sin contraseña, etc.)
unknown —No se puede determinar (permisos insuficientes o función solo disponible en versión comercial)

Familia MySQL (MySQL / MariaDB / OceanBase / TiDB) — 7 reglas ​

IDCategoríaTítuloDetección
mysql.auth.password-policyIdentidadPolítica de contraseñas fuertesSHOW VARIABLES LIKE 'validate_password%', policy ≥ MEDIUM y length ≥ 8
mysql.audit.enabledAuditoríaAudit log activadoaudit_log_* (enterprise) o server_audit_* (MariaDB)
mysql.auth.root-remoteControl de accesoroot sin acceso remotoSELECT user, host FROM mysql.user WHERE user='root'
mysql.auth.anonymousControl de accesoSin usuarios anónimosmysql.user WHERE user=''
mysql.transport.sslIntegridadSSL obligatorio en transporterequire_secure_transport=ON
mysql.audit.slowlogAuditoríaSlow query log activadoslow_query_log=ON
mysql.integrity.binlogIntegridadbinlog habilitadolog_bin=ON (prerrequisito para PITR / replicación)

Familia PostgreSQL (PG / KingbaseES / openGauss / Greenplum / CockroachDB) — 6 reglas ​

IDCategoríaTítuloDetección
pg.auth.password-encryptionIdentidadCifrado SCRAM-SHA-256SHOW password_encryption
pg.audit.pgauditAuditoríaExtensión pgaudit instaladapg_extension WHERE extname='pgaudit'
pg.transport.sslIntegridadSSL activadoSHOW ssl
pg.access.superuser-countControl de accesoNúmero limitado de superusuarios (≤ 2)SELECT rolname FROM pg_roles WHERE rolsuper
pg.audit.log-statementAuditoríalog_statement configuradoSHOW log_statement ≠ none
pg.auth.empty-passwordIdentidadSin usuarios con contraseña vacíapg_authid WHERE rolpassword IS NULL AND rolcanlogin

Exportación del informe Markdown ​

Exportar Markdown llama a renderReport(); agrupa por categoría, añade una línea de resumen "Resumen: ✅ N · ⚠️ N · ❌ N · — N" y, por regla, su descripción, conclusión y evidence. El nombre incluye conexión y timestamp: compliance-<safeName>-<YYYY-MM-DDTHH-MM-SS>.md.

Ejecución concurrente ​

"Iniciar comprobación" lanza Promise.all para correr todas las reglas en paralelo; los fallos no afectan al resto (try/catch → unknown); el driver hace su propio pooling/reuso.

Otros dialectos ​

Los dialectos que no son MySQL ni PG muestran un placeholder:

El dialecto actual no tiene comprobaciones de cumplimiento — confirmar manualmente

Se añadirán Oracle / SQL Server / SQLite / DM cuando haga falta.

9. Cifrado nacional chino SM2 / SM3 / SM4 (planificado) ​

En el conjunto de reglas de cumplimiento ya está como criterio "password_encryption=md5 se considera débil por GB17859 / 国密" (ver pg.auth.password-encryption). Las APIs auxiliares SM2 / SM3 / SM4 (para firma/cifrado a nivel de aplicación antes de persistir) aún no se han publicado; el plan es entregar un cryptoCn.ts independiente en v0.6:

  • SM2 (curvas elípticas) firma / cifra / descifra (basado en sm-crypto)
  • SM3 digest
  • SM4 bloque simétrico (CBC / ECB)

Cuando la API se estabilice, se documentará en esta página en el apartado "APIs auxiliares de 国密".

10. Exportación cifrada .skbk ​

Ubicación: packages/ui/src/export-encrypt.ts.

Cifra cualquier texto (típicamente dump SQL o configuración de conexiones) con una contraseña, generando un JSON de una sola línea con extensión .sql.enc / .skbk.

Cadena de algoritmos ​

EtapaAlgoritmoParámetros
Derivación de clavePBKDF2-HMAC-SHA-256iter = DEFAULT_ITER = 200 000 (ajustable; queda en la cabecera)
CifradoAES-GCM 256salt 16B + iv 16B, renovados cada vez
IntegridadAuth tag de 128 bits propio de AES-GCMContraseña errónea / archivo modificado → WRONG_PASSWORD
Cabeceramagic: 'SKYLERX-ENC-v1'Identifica la versión al cambiar algoritmo/parámetros

Por qué 200 000 iteraciones: OWASP 2023 recomienda SHA-256 ≥ 600 000, pero el cliente de escritorio debe convivir con máquinas viejas (un Atom haría 1+ s con 600k). Para contenido muy sensible, sube manualmente iter al llamar a encryptText.

Formato persistido ​

json
{
  "magic": "SKYLERX-ENC-v1",
  "salt": "<base64 16B>",
  "iv":   "<base64 16B>",
  "iter": 200000,
  "data": "<base64 ciphertext + tag>"
}

Orden de campos fijo (cómodo para git diff); una sola línea, ideal para streaming.

Códigos de error ​

ErrorCuándo se lanzaReacción UI
INVALID_BLOBFaltan campos / tipo erróneo / magic no coincideAviso "Archivo corrupto"
WRONG_PASSWORDFalla el auth tag de AES-GCM (contraseña errónea o archivo alterado)Aviso "Contraseña incorrecta" (sin distinguir, para no filtrar el motivo)

Dependencia de Web Crypto ​

Todo va por globalThis.crypto.subtle, sin dependencias externas. Funciona en el renderer de Electron, en navegadores modernos y en Node 18+ (para tests). Entornos muy antiguos lanzan Web Crypto API unavailable: upgrade to Node 18+ or a modern browser.

11. Límite de privacidad con la IA ​

El asistente de IA (Anthropic / OpenAI / DeepSeek / Codex / Grok) es la pieza estrella de SkylerX, pero lo que envía a APIs externas se ciñe al contexto estrictamente necesario:

Tipo¿Se envía?Nota
Texto SQL actual✅Es la base de toda interacción / autocompletado iniciado por el usuario
Hints de schema (nombres de base / tabla / columna)✅Solo metadatos, no se envían filas de datos
Cuerpo del error + código✅Para "Preguntar a la IA", ver sección 4 del documento AI
Metadatos de la conexión (dialecto / nombre / base)✅Ayuda a la IA a usar el dialecto correcto
Filas de resultado❌Aunque el autocompletado IA esté activado, solo se mandan hints de schema, no las filas del SELECT
Contraseña de conexión / clave privada SSH❌El texto cifrado del keychain nunca se lee como prompt
Configuración local de conexiones completa❌Solo se toma dialect / database de la conexión activa

Para aislar la IA por completo:

  1. Settings → AI Provider → vaciar API Key → autocompletado / chat / Preguntar a la IA quedan desactivados
  2. Usa un endpoint local (Ollama / vLLM / despliegue privado) y cambia el campo endpoint a http://localhost:...

Los webhooks de notificación con IA siguen la misma regla: por defecto envían "título + resumen + hora de disparo", sin filas SQL. Lo puedes ajustar en la plantilla de Settings → Notificaciones.

12. Atajos a funciones de seguridad ​

AcciónEntrada
Comprobación de cumplimiento GB17859 (等保 2.0)⌘K → "Cumplimiento GB17859 · nombre de conexión" / clic derecho en la conexión → Cumplimiento
Escaneo PIIClic derecho en la base → PII Scanner
Generar vista de desensibilizaciónClic derecho en base / tabla → Generar vista de desensibilización
Contratos de datos⌘K → "Contratos de datos" / Herramientas → Contratos de datos
Exportación cifradaResultado / Editor SQL → Exportar → elige .skbk
Políticas de seguridad globalesSettings → Desensibilización / Settings → Marca de agua
Atajos personalizados (anti error de pulsación)Settings → Atajos

13. Limitaciones conocidas ​

Cosas que el DBA debe tener presentes según el código actual:

  • El SQL Linter es heurístico: sin parser SQL completo, el escaneo por cadena puede fallar en casos raros (comentarios /* ... */ anidados + literales que incluyan where). Para operaciones críticas, mantén también la doble confirmación en prod (escribir el nombre de la conexión).
  • Las comprobaciones de cumplimiento requieren permisos de lectura: mysql.user necesita SELECT; pg_authid necesita superusuario; sin permiso, la regla cae en unknown en lugar de fail; no equipares unknown a pass.
  • El masking en render solo vive en UI: en la BD sigue el texto original. Para evitar de raíz que la aplicación lea el original, usa vistas de desensibilización en BD y restringe los permisos del usuario.
  • El archivo cifrado no es inmune a fuerza bruta: 200k iteraciones de PBKDF2 dan un coste ~10^7; una contraseña débil sigue siendo crackeable offline. Usa contraseñas fuertes o distribuye con KMS / clave pública dentro del equipo.
  • La etiqueta de entorno es una restricción blanda: extra.env = 'prod' lo rellena el usuario al guardar la conexión; si por error elige dev, no se activan las reglas exclusivas de prod. Para estandarizarlo, comparte la configuración por "exportar conexiones → compañeros importan".

Apache License 2.0 · Wuhan Skyler Network Technology Co., Ltd.