<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SEBASTIAN ALEJANDRO CORTEZ APAZA</title>
    <description>The latest articles on DEV Community by SEBASTIAN ALEJANDRO CORTEZ APAZA (@sebacor13).</description>
    <link>https://dev.to/sebacor13</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4160778%2Fe52ae165-455f-48bb-aa95-92b6a7a11863.jpg</url>
      <title>DEV Community: SEBASTIAN ALEJANDRO CORTEZ APAZA</title>
      <link>https://dev.to/sebacor13</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sebacor13"/>
    <language>en</language>
    <item>
      <title>Completo, diferencial o incremental: cómo elegir tu estrategia de respaldo (y probarla bloque a bloque con SQLite)</title>
      <dc:creator>SEBASTIAN ALEJANDRO CORTEZ APAZA</dc:creator>
      <pubDate>Sun, 04 Oct 2026 03:45:02 +0000</pubDate>
      <link>https://dev.to/sebacor13/completo-diferencial-o-incremental-como-elegir-tu-estrategia-de-respaldo-y-probarla-bloque-a-1a9g</link>
      <guid>https://dev.to/sebacor13/completo-diferencial-o-incremental-como-elegir-tu-estrategia-de-respaldo-y-probarla-bloque-a-1a9g</guid>
      <description>&lt;p&gt;&lt;strong&gt;Autor:&lt;/strong&gt; Sebastian Alejandro Cortez Apaza, estudiante de Ingeniería de Sistemas de la Universidad Privada de Tacna, curso Base de Datos II.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;strong&gt;Demo en vivo:&lt;/strong&gt; &lt;a href="https://sebacor-13.github.io/backup-strategies-sqlite/" rel="noopener noreferrer"&gt;https://sebacor-13.github.io/backup-strategies-sqlite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;Repositorio público:&lt;/strong&gt; &lt;a href="https://github.com/SEBACOR-13/backup-strategies-sqlite" rel="noopener noreferrer"&gt;https://github.com/SEBACOR-13/backup-strategies-sqlite&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Los atacantes también buscan tus respaldos
&lt;/h2&gt;

&lt;p&gt;Según el estudio de Sophos de 2024, aplicado a casi 3 000 organizaciones, en el &lt;strong&gt;94 %&lt;/strong&gt; de los ataques de &lt;em&gt;ransomware&lt;/em&gt; los delincuentes intentaron comprometer también los respaldos, y lo lograron en el 57 % de esos casos. Cuando lo consiguieron, las víctimas tuvieron casi el doble de probabilidad de pagar el rescate, y el costo de recuperación fue unas ocho veces mayor (Adam, 2024).&lt;/p&gt;

&lt;p&gt;Por eso la pregunta ya no es solo “¿tengo respaldo?”, sino también:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿qué tipo de respaldo tengo?&lt;/li&gt;
&lt;li&gt;¿cuántas piezas necesito para restaurarlo?&lt;/li&gt;
&lt;li&gt;¿está cifrado y separado del sistema atacado?&lt;/li&gt;
&lt;li&gt;¿lo he probado alguna vez?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En este artículo comparo las tres políticas clásicas (completa, diferencial e incremental) con &lt;strong&gt;datos medidos&lt;/strong&gt;, gracias a &lt;strong&gt;BlockBackupLab&lt;/strong&gt;, una aplicación que respalda un archivo SQLite real página por página.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Las tres políticas, en una frase cada una
&lt;/h2&gt;

&lt;p&gt;La literatura clásica de respaldos distingue entre copiar todo y copiar solo lo modificado (Chervenak et al., 1998):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Política&lt;/th&gt;
&lt;th&gt;Qué copia&lt;/th&gt;
&lt;th&gt;Para restaurar necesito&lt;/th&gt;
&lt;th&gt;Ventaja&lt;/th&gt;
&lt;th&gt;Riesgo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Completa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Todo, cada vez&lt;/td&gt;
&lt;td&gt;1 pieza&lt;/td&gt;
&lt;td&gt;Simplicidad y restauración rápida&lt;/td&gt;
&lt;td&gt;Mucho espacio y ventanas largas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Diferencial&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lo que cambió desde el último &lt;strong&gt;completo&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;2 piezas: completo + último diferencial&lt;/td&gt;
&lt;td&gt;Restauración sencilla&lt;/td&gt;
&lt;td&gt;Crece cada día hasta el siguiente completo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Incremental&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lo que cambió desde el respaldo &lt;strong&gt;anterior&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Completo + &lt;strong&gt;todos&lt;/strong&gt; los incrementales&lt;/td&gt;
&lt;td&gt;Mínimo espacio y ventanas cortas&lt;/td&gt;
&lt;td&gt;La cadena es frágil: si falta un eslabón, se pierde lo posterior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Un esquema típico es &lt;strong&gt;un completo el domingo y diferenciales o incrementales de lunes a sábado&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ¿Qué significa “lo que cambió”? Respaldos por bloques
&lt;/h2&gt;

&lt;p&gt;Las herramientas modernas no comparan filas ni archivos enteros: comparan &lt;strong&gt;bloques&lt;/strong&gt;. SQLite es un caso ideal para entenderlo, porque toda la base vive en un único archivo dividido en &lt;strong&gt;páginas&lt;/strong&gt; de tamaño fijo. El tamaño de página se guarda en la cabecera, en el desplazamiento 16 (SQLite, s.f.-a).&lt;/p&gt;

&lt;p&gt;La idea es simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Divido el archivo en páginas de 4 KB.&lt;/li&gt;
&lt;li&gt;Calculo el &lt;strong&gt;SHA-256 de cada página&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Comparo esas huellas con las del respaldo de referencia y &lt;strong&gt;copio solo las páginas distintas&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Así funciona también &lt;code&gt;sqlite3_rsync&lt;/code&gt;, la herramienta oficial de SQLite para copiar una base viva: la réplica envía &lt;em&gt;hashes&lt;/em&gt; de sus páginas y el origen devuelve solo las que difieren. En bases parecidas, el tráfico puede ser del orden del 0,01 % del tamaño (SQLite, s.f.-c). PostgreSQL 17 aplica la misma idea en su respaldo incremental nativo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tamaño de página: bytes 16-17 de la cabecera, big-endian (1 significa 65 536)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;tamanoPagina&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;65536&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;crearRespaldo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tipo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;catalogo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hashes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;hashesDePaginas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;// SHA-256 de cada página&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tipo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;diferencial&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;catalogo&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tipo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// último completo&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tipo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;incremental&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;catalogo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;              &lt;span class="c1"&gt;// respaldo anterior&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cambiadas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]))&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tipo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;padre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;paginas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cambiadas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;copiaPagina&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para restaurar, se aplica la cadena en orden: primero el completo y luego cada respaldo posterior, sobrescribiendo sus páginas. Al final se verifica el SHA-256 del archivo reconstruido contra el original.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ojo en SQLite real:&lt;/strong&gt; nunca copies el archivo &lt;code&gt;.db&lt;/code&gt; con &lt;code&gt;cp&lt;/code&gt; mientras está en uso. Usa la &lt;strong&gt;Online Backup API&lt;/strong&gt;, &lt;code&gt;VACUUM INTO 'respaldo.db'&lt;/code&gt; o &lt;code&gt;sqlite3_rsync&lt;/code&gt;, que garantizan una copia consistente (SQLite, s.f.-b). Para respaldos continuos existe &lt;strong&gt;Litestream&lt;/strong&gt;, que replica el WAL de SQLite hacia almacenamiento S3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. BlockBackupLab: medir en lugar de suponer
&lt;/h2&gt;

&lt;p&gt;La aplicación simula un inventario con &lt;strong&gt;4 almacenes, 300 productos y 6 000 movimientos&lt;/strong&gt; sobre SQLite real, ejecutado en el navegador con sql.js (WebAssembly). Cada vez que avanzas un día simulado:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Se registran unos 120 movimientos nuevos y se actualiza el stock.&lt;/li&gt;
&lt;li&gt;Se ejecuta el respaldo que indique la política.&lt;/li&gt;
&lt;li&gt;El respaldo se &lt;strong&gt;comprime con gzip&lt;/strong&gt; y se &lt;strong&gt;cifra con AES-256-GCM&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwyq34ksd1jn6pdopaxrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwyq34ksd1jn6pdopaxrg.png" alt="Mapa de páginas copiadas en un respaldo incremental" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Resultados de dos semanas simuladas
&lt;/h3&gt;

&lt;p&gt;Con los mismos datos, al final la base pesa 584 KB (146 páginas). Estos son los resultados medidos, sin comprimir:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Política&lt;/th&gt;
&lt;th&gt;Almacenamiento total&lt;/th&gt;
&lt;th&gt;Páginas copiadas por día (lun → sáb, semana 1)&lt;/th&gt;
&lt;th&gt;Piezas para restaurar el día 13&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Completo diario&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7,27 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;122 → 132 (todo)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completo dominical + diferencial&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,45 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25 → 35 (crece)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completo dominical + incremental&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,23 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;25 → 26 (estable)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqdj2md72x31j4w2hk3kg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqdj2md72x31j4w2hk3kg.png" alt="Comparación de políticas en la aplicación" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lectura de los resultados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El &lt;strong&gt;completo diario&lt;/strong&gt; ocupa unas &lt;strong&gt;3 veces más&lt;/strong&gt; que las otras dos políticas.&lt;/li&gt;
&lt;li&gt;El &lt;strong&gt;diferencial&lt;/strong&gt; crece cada día: el sábado vuelve a copiar todo lo del lunes. Aun así, restaurar solo necesita dos piezas.&lt;/li&gt;
&lt;li&gt;El &lt;strong&gt;incremental&lt;/strong&gt; copia siempre lo mínimo, pero restaurar un sábado exige leer y aplicar &lt;strong&gt;siete&lt;/strong&gt; archivos, sin que falte ninguno.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En la app hay un botón 🗑 que simula “perder” un respaldo, por un disco dañado o un borrado accidental. Si se pierde un incremental intermedio, la cadena de restauración de los días siguientes &lt;strong&gt;se rompe&lt;/strong&gt; y la aplicación lo indica en rojo.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Cifrar y verificar: el respaldo como objetivo
&lt;/h2&gt;

&lt;p&gt;Si el atacante puede leer o borrar tus respaldos, no tienes respaldos. BlockBackupLab empaqueta cada uno así:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Serializa&lt;/strong&gt; las páginas con una cabecera JSON de metadatos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprime&lt;/strong&gt; con gzip: un completo de 480 KB queda en unos 186 KB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cifra&lt;/strong&gt; con &lt;strong&gt;AES-256-GCM&lt;/strong&gt;, un modo autenticado: cualquier byte alterado hace fallar el descifrado (Dworkin, 2007).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deriva la clave&lt;/strong&gt; desde una frase con &lt;strong&gt;PBKDF2-HMAC-SHA256 y 600 000 iteraciones&lt;/strong&gt;, el valor que recomienda OWASP (OWASP Foundation, s.f.).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clave&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deriveKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PBKDF2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHA-256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;encrypt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;decrypt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cifrado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;clave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comprimido&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En la demostración, un &lt;em&gt;ransomware&lt;/em&gt; simulado cifra el archivo de la base y lo vuelve ilegible. La restauración trabaja &lt;strong&gt;solo con los paquetes cifrados&lt;/strong&gt; y hace lo siguiente:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Los descifra y reconstruye el archivo página por página.&lt;/li&gt;
&lt;li&gt;Comprueba que el &lt;strong&gt;SHA-256&lt;/strong&gt; sea idéntico al original.&lt;/li&gt;
&lt;li&gt;Ejecuta &lt;code&gt;PRAGMA integrity_check&lt;/code&gt;, que devuelve &lt;code&gt;ok&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Compara el número de filas de cada tabla.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F99m11sp6ro3hizv0rbhv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F99m11sp6ro3hizv0rbhv.png" alt="Restauración verificada después del ransomware" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. De 3-2-1 a 3-2-1-1-0
&lt;/h2&gt;

&lt;p&gt;La regla clásica pide &lt;strong&gt;3 copias, en 2 medios distintos y 1 fuera del sitio&lt;/strong&gt; (Ruggiero &amp;amp; Heckathorn, 2012). Frente al &lt;em&gt;ransomware&lt;/em&gt;, la industria la amplió a &lt;strong&gt;3-2-1-1-0&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; copia &lt;strong&gt;inmutable o desconectada&lt;/strong&gt; (&lt;em&gt;offline&lt;/em&gt; o &lt;em&gt;air-gapped&lt;/em&gt;), que el atacante no pueda cifrar ni borrar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0&lt;/strong&gt; errores al &lt;strong&gt;verificar&lt;/strong&gt; las restauraciones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Es lo mismo que pide la guía #StopRansomware de CISA, la NSA y el FBI: mantener respaldos &lt;strong&gt;desconectados y cifrados&lt;/strong&gt;, y probar con regularidad su disponibilidad e integridad (CISA et al., 2023).&lt;/p&gt;

&lt;p&gt;A esto se suma una &lt;strong&gt;política de retención&lt;/strong&gt;. La aplicación aplica el esquema &lt;strong&gt;GFS&lt;/strong&gt; (abuelo-padre-hijo): conserva 7 respaldos diarios, 4 semanales y 6 mensuales, y marca el resto como “expira”.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Despliegue automatizado
&lt;/h2&gt;

&lt;p&gt;El repositorio público se despliega solo, sin pasos manuales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;/strong&gt;: en cada &lt;em&gt;push&lt;/em&gt; a &lt;code&gt;main&lt;/code&gt; ejecuta &lt;strong&gt;11 pruebas&lt;/strong&gt; con Vitest. Cubren cadenas completas, diferenciales e incrementales, la ruptura de la cadena, el ransomware, el cifrado con frase incorrecta o paquete alterado, la comparación de políticas y la retención GFS. Después construye con Vite, publica en &lt;strong&gt;GitHub Pages&lt;/strong&gt; y hace una prueba de humo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;simulacro.yml&lt;/code&gt;&lt;/strong&gt;: cada lunes repite el simulacro de restauración, porque un respaldo que nunca se restaura no está probado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqzyjqe789k88us426pns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqzyjqe789k88us426pns.png" alt="GitHub Actions" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. ¿Cuál elijo?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Base pequeña o RTO muy exigente&lt;/strong&gt; → completo frecuente, porque se restaura en un solo paso.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Equilibrio entre espacio y simplicidad&lt;/strong&gt; → completo semanal + &lt;strong&gt;diferencial&lt;/strong&gt; diario.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base grande con pocos cambios diarios, o ancho de banda limitado&lt;/strong&gt; → completo semanal + &lt;strong&gt;incremental&lt;/strong&gt;, con verificación automática de la cadena y copias redundantes de cada eslabón.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RPO de minutos&lt;/strong&gt; → ninguna de las tres basta por sí sola. Hay que sumar replicación o archivado continuo del log de transacciones (WAL en SQLite o PostgreSQL, &lt;em&gt;binlog&lt;/em&gt; en MySQL).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y siempre: cifra, separa al menos una copia del sistema principal y &lt;strong&gt;ensaya la restauración&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;p&gt;Adam, S. (2024). &lt;em&gt;The impact of compromised backups on ransomware outcomes&lt;/em&gt;. Sophos. &lt;a href="https://www.sophos.com/en-us/blog/the-impact-of-compromised-backups-on-ransomware-outcomes" rel="noopener noreferrer"&gt;https://www.sophos.com/en-us/blog/the-impact-of-compromised-backups-on-ransomware-outcomes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chervenak, A., Vellanki, V., &amp;amp; Kurmas, Z. (1998). Protecting file systems: A survey of backup techniques. En &lt;em&gt;Proceedings of the Joint NASA and IEEE Mass Storage Conference&lt;/em&gt;. &lt;a href="https://www.researchgate.net/publication/2431989_Protecting_File_Systems_A_Survey_of_Backup_Techniques" rel="noopener noreferrer"&gt;https://www.researchgate.net/publication/2431989_Protecting_File_Systems_A_Survey_of_Backup_Techniques&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CISA, MS-ISAC, NSA &amp;amp; FBI. (2023). &lt;em&gt;#StopRansomware guide&lt;/em&gt;. Cybersecurity and Infrastructure Security Agency. &lt;a href="https://www.cisa.gov/stopransomware/ransomware-guide" rel="noopener noreferrer"&gt;https://www.cisa.gov/stopransomware/ransomware-guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dworkin, M. (2007). &lt;em&gt;Recommendation for block cipher modes of operation: Galois/Counter Mode (GCM) and GMAC&lt;/em&gt; (NIST Special Publication 800-38D). National Institute of Standards and Technology. &lt;a href="https://doi.org/10.6028/NIST.SP.800-38D" rel="noopener noreferrer"&gt;https://doi.org/10.6028/NIST.SP.800-38D&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OWASP Foundation. (s.f.). &lt;em&gt;Password storage cheat sheet&lt;/em&gt;. OWASP Cheat Sheet Series. Recuperado el 3 de octubre de 2026, de &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ruggiero, P., &amp;amp; Heckathorn, M. A. (2012). &lt;em&gt;Data backup options&lt;/em&gt;. United States Computer Emergency Readiness Team (US-CERT). &lt;a href="https://www.cisa.gov/sites/default/files/publications/data_backup_options.pdf" rel="noopener noreferrer"&gt;https://www.cisa.gov/sites/default/files/publications/data_backup_options.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQLite. (s.f.-a). &lt;em&gt;Database file format&lt;/em&gt;. Recuperado el 3 de octubre de 2026, de &lt;a href="https://www.sqlite.org/fileformat.html" rel="noopener noreferrer"&gt;https://www.sqlite.org/fileformat.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQLite. (s.f.-b). &lt;em&gt;SQLite backup API&lt;/em&gt;. Recuperado el 3 de octubre de 2026, de &lt;a href="https://www.sqlite.org/backup.html" rel="noopener noreferrer"&gt;https://www.sqlite.org/backup.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SQLite. (s.f.-c). &lt;em&gt;Database remote-copy tool for SQLite (sqlite3_rsync)&lt;/em&gt;. Recuperado el 3 de octubre de 2026, de &lt;a href="https://www.sqlite.org/rsync.html" rel="noopener noreferrer"&gt;https://www.sqlite.org/rsync.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>sqlite</category>
      <category>backup</category>
      <category>security</category>
    </item>
  </channel>
</rss>
