Cifrado SQlite DB simple

Sucedió que realmente me gusta usar SQLite DBMS.


Cuando programo en ensamblador, a veces necesito un DBMS completo. Mis programas rara vez superan unos pocos cientos de kilobytes. Está claro que usar un DBMS de varios cientos de megabytes con él es al menos ridículo, pero en última instancia muy inconveniente: los requisitos de hardware y la complejidad de la instalación y la configuración aumentan de inmediato, y como resultado, la confiabilidad de todo el sistema disminuye.


SQLite es un asunto completamente diferente. En primer lugar, es pequeño: solo unos pocos cientos de kilobytes, una gran adición a los programas de ensambladores compactos. En segundo lugar, es un sistema de almacenamiento de datos ultra confiable. Ella no necesita ninguna configuración especial y configuraciones. Bueno, en cuanto al rendimiento, no es el último.


Por ejemplo, usé SQLite en mi motor de foro AsmBB sobre el cual ya escribí en Habré. (Por cierto, después de eso no se cayó ).


Desde entonces, el proyecto se ha desarrollado lenta pero seguramente. Han aparecido nuevas características, mayor seguridad y rendimiento.


Y luego, un día, pensé en cómo aumentar la seguridad ya buena del proyecto. E inmediatamente pensé que sería bueno encriptar la base de datos del foro. De hecho, incluso si la base de datos se filtra, nadie tendrá acceso a los datos personales de los usuarios.


Una búsqueda rápida en Internet mostró que hay varias extensiones SQLite para el cifrado de la base de datos. Desafortunadamente, la extensión SEE oficial no es gratuita y generalmente se vende por dinero.


Pero, por supuesto, un lugar sagrado nunca está vacío e inmediatamente me topé con la extensión SQLeet . Y en eso me gustó literalmente todo.


SQLeet utiliza el algoritmo ChaCha20 para cifrar la base de datos. La clave de cifrado se calcula a través de PBKDF2-HMAC-SHA256 utilizando una sal de 16 bytes y 12345 hash iteraciones. Para la autenticación, se utiliza Poly1305.


Tanto SQLeet como SQLite se distribuyen bajo el dominio público (dominio público). Esto es conveniente ya que no aumenta el caos de licencias en el proyecto.


Todavía SQLeet es muy compacto. Todo el código solo toma alrededor de mil quinientas líneas en C y no tiene dependencias externas.


El proyecto cuenta con un apoyo activo y el autor responde rápidamente preguntas y corrige errores, si los hay.


SQLeet se distribuye de la misma manera que SQLite, en la forma de un único archivo fuente C que se puede compilar simplemente de la misma manera que se compila SQLite.


Además, dado que la extensión no cambia el código SQLite de ninguna manera, la actualización del DBMS principal se puede hacer de manera muy simple: reemplazando el archivo sqlite3.c y recreando la fuente combinada.


Como no uso una compilación estándar en AsmBB (SQLite en AsmBB se compila a través de MUSL libc ), y no soy un programador en C, para mí la simplicidad de la compilación es muy importante.


Por ejemplo, aquí está el código bash que uso para descargar la última versión de SQLeet y crear y construir la fuente:


 wget -q -O - https://github.com/resilar/sqleet/archive/master.tar.gz | tar -xz cd ./sqleet-master script/amalgamate.sh < ./sqleet.c > ../sqlite3.c cd .. rm -rf ./sqleet-master/ 

El resultado es un archivo sqlite3.c que se puede insertar donde el archivo SQLite original se insertó antes y se usó de la misma manera.


Usar la extensión no es diferente a usar SQLite. La única diferencia es que si la base de datos está encriptada, inmediatamente después de abrirla es necesario llamar a la función sqlite3_key (), en la que especifica la contraseña de encriptación. Bueno, o incluso mejor, simplemente ejecute SQL pragma key='%%' . (Esto es mejor porque la API SQLite no cambia y
siempre puede reemplazar SQLeet con SQLite y viceversa).


El cifrado inicial de la base de datos, así como el reemplazo de la contraseña, ocurre a través de la sqlite3_rekey() o pragma con el pragma rekey='%NEW_PASSWORD%' .


Y aquí tenía esa pregunta. ¿De dónde viene la contraseña? Después de todo, si la contraseña se almacena en el servidor en algún archivo, entonces el hacker potencial podrá leerla.


Entonces decidí hacerlo de manera diferente. El hecho es que AsmBB es una aplicación FastCGI de larga duración. Una vez que se inicia en un servidor, se ejecuta durante meses e incluso años sin necesidad de reiniciar.


Y si es así, el administrador puede simplemente ingresar la contraseña a través de la interfaz web inmediatamente después de iniciar AsmBB. Por lo tanto, la contraseña existe solo en la RAM y solo durante la ejecución de la solicitud POST durante el inicio de la aplicación. (Por supuesto, no olvide poner a cero toda la memoria en la que existía la contraseña durante la ejecución de la solicitud POST).


A partir de la contraseña establecida, SQLeet genera una clave de cifrado a través de PBKDF2-HMAC-SHA256, y esta clave también se almacena solo en la RAM.


Por supuesto, tal decisión es imperfecta. La clave de cifrado probablemente se puede encontrar en la memoria RAM, durante la ejecución de AsmBB, si el atacante tiene derechos de administrador.


Pero aun así, el sistema sigue siendo mucho más seguro que sin cifrado. Por ejemplo, ahora las copias de seguridad de la base de datos pueden almacenarse en todas partes y enviarse a través de canales abiertos sin temor a la fuga de datos.


Por cierto, hay un rastrillo que puedes pisar usando SQLeet (u otras extensiones criptográficas de SQLite). Y yo, por supuesto, los pisé.


El problema es el tamaño de la página de la base de datos. En versiones de SQLite anteriores a 3.12.0 (marzo de 2016), el tamaño de página predeterminado era 1024 bytes. En v3.12.0, 4096 bytes lo hicieron. Este tamaño, el usuario de la base de datos puede cambiar por razones de rendimiento, y el tamaño de la página se escribe en la base de datos.


Pero si la base de datos está encriptada, entonces el tamaño de la página no se puede leer, y para desencriptar este tamaño es necesario, porque cada bloque está encriptado por separado.


Por lo tanto, si cifra una base de datos con un tamaño de página no estándar (para SQLeet, el estándar es 4096 bytes), no podrá descifrarlo, incluso si establece la contraseña correcta.


Esto se soluciona simplemente: antes de configurar la contraseña a través de sqlite3_key() o pragma key='%%' , debe establecer el tamaño de página correcto a través de pragma page_size=%% .


Otro posible problema es que después del cifrado, el sistema operativo ya no podrá reconocer que el archivo es una base de datos SQLite. Esto (por lo que he leído) a veces conduce a algunos problemas, en particular en iOS. Hay una solución a este problema, simplemente no cifre los primeros 32 bytes del archivo, pero no entré en detalles.


Y finalmente, sobre el rendimiento. SQLeet es muy rápido. Después del cifrado, no noté ninguna desaceleración en el sistema en el contexto de las fluctuaciones normales en el rendimiento de VPS. Las mediciones de precisión pueden mostrar algún tipo de desaceleración, pero probablemente será inferior al 10% de la velocidad de una base de datos sin cifrar.


Por supuesto, hay otras extensiones gratuitas de SQLite para el cifrado. Por ejemplo, SQLcipher . No me convenía porque tiene una licencia de distribución diferente (BSD), el código es mucho más grande y hay dependencias externas.


Pero, por otro lado, SQLcipher es mucho más antiguo y por lo tanto (posiblemente) más estable. Alguien puede ser útil.

Source: https://habr.com/ru/post/470295/


All Articles