¿Cuál es la mejor práctica para manejar contraseñas en repositorios git?

225

Tengo un pequeño script Bash que uso para acceder a Twitter y mostrar una notificación de Growl en ciertas situaciones. ¿Cuál es la mejor manera de manejar el almacenamiento de mi contraseña con el script?

Me gustaría enviar este script al repositorio de git y ponerlo a disposición en GitHub, pero me pregunto cuál es la mejor manera de mantener mi nombre de usuario / contraseña privada mientras lo hago. Actualmente, la contraseña se almacena en el script mismo. No puedo eliminarlo justo antes de presionar porque todas las confirmaciones antiguas contendrán la contraseña. Desarrollar sin contraseña no es una opción. Me imagino que debería estar almacenando la contraseña en un archivo de configuración externo, pero pensé en verificar si había una forma establecida de manejar esto antes de intentar y armar algo.

kubi
fuente

Respuestas:

256

La forma típica de hacer esto es leer la información de la contraseña de un archivo de configuración. Si se llama a su archivo de configuración foobar.config, entonces confirmaría un archivo llamado foobar.config.exampleal repositorio, que contiene datos de muestra. Para ejecutar su programa, crearía un archivo local (no rastreado) llamado foobar.configcon sus datos de contraseña reales .

Para filtrar su contraseña existente de confirmaciones anteriores, consulte la página de ayuda de GitHub en Eliminar datos confidenciales .

Greg Hewgill
fuente
44
Por cierto, puede agregar un ejemplo foobar.config al repositorio y luego agregar foobar.config al archivo .ignore. De esta manera, el ejemplo foobar.config aparecerá cuando se clone y sus contraseñas reales no se agregarán al repositorio.
Mr_Chimp
16
@ Mr_Chimp: el .gitignorearchivo no se aplica a los archivos rastreados que ya están en el repositorio. Por ejemplo, git add -uagregará un archivo modificado incluso si ya está en .gitignore.
Greg Hewgill el
1
Como complemento, aquí hay un enlace interesante en caso de que agregue el archivo de configuración por accidente y desee eliminarlo del historial de git: help.github.com/articles/remove-sensitive-data
Loïc Lopes
16
¿Cómo harías para compartir esas contraseñas con tu equipo? Una cosa es tener una copia local (no comprometida con el repositorio), la otra es compartirla con un equipo más grande incluso con herramientas automáticas (para implementación, etc.)
blueFast
2
Tengo la misma pregunta que @dangonfast. Esto no parece práctico para un equipo grande.
Jacob Stamm
25

Un enfoque puede ser establecer una contraseña (o clave API) utilizando una variable de entorno. Entonces esta contraseña está fuera de control de revisión.

Con Bash, puede establecer variables de entorno utilizando

export your_env_variable='your_password'

Este enfoque se puede usar con servicios de integración continua como Travis , su código (sin contraseña) almacenado en un repositorio de GitHub puede ser ejecutado por Travis (con su contraseña configurada usando la variable de entorno).

Con Bash, puede obtener el valor de una variable de entorno utilizando:

echo "$your_env_variable"

Con Python, puede obtener el valor de una variable de entorno utilizando:

import os
print(os.environ['your_env_variable'])

PD: tenga en cuenta que probablemente sea un poco arriesgado (pero es una práctica bastante común) https://www.bleepingcomputer.com/news/security/javascript-packages-caught-stealing-environment-variables/

PS2: este dev.toartículo titulado "Cómo almacenar claves API de forma segura" puede ser interesante de leer.

scls
fuente
1
¿Cómo evitar que la posible creación de código "inseguro" lea los contenidos de la variable de entorno?
gorootde
16

Lo que Greg dijo pero agregaría que es una buena idea registrar un archivo foobar.config-TEMPLATE.

Debe contener nombres de ejemplo, contraseñas u otra información de configuración. Entonces es muy obvio lo que debe contener el foobar.config real, sin tener que buscar en todo el código qué valores deben estar presentes foobar.configy en qué formato deben tener.

A menudo, los valores de configuración pueden no ser obvios, como cadenas de conexión de base de datos y cosas similares.

Prof. Falken
fuente
7

El manejo de las contraseñas en los repositorios se manejaría de diferentes maneras dependiendo de cuál sea su problema exacto.

1. No lo hagas.

Y algunas formas de evitar hacerlo están cubiertas: .gitignore, config.example, etc.

o 2. Hacer que el repositorio sea accesible solo para personas autorizadas

Es decir, personas a las que se les permite conocer la contraseña. chmody grupos de usuarios vienen a la mente; ¿También problemas como los empleados de Github o AWS deberían poder ver cosas si aloja sus repositorios o servidores de forma externa?

o 3. Cifre los datos confidenciales (propósito de esta respuesta)

Si desea almacenar sus archivos de configuración que contienen información confidencial (como contraseñas) en una ubicación pública, entonces debe cifrarse. Los archivos pueden descifrarse cuando se recuperan del repositorio, o incluso usarse directamente desde su forma cifrada.

A continuación se muestra un ejemplo de solución de JavaScript para usar datos de configuración cifrados.

const fs = require('fs');
const NodeRSA = require('node-rsa');

let privatekey = new NodeRSA();
privatekey.importKey(fs.readFileSync('private.key', 'utf8'));
const config = privatekey.decrypt(fs.readFileSync('config.RSA', 'utf8'), 'json');

console.log('decrypted: ', config);

Archivo de configuración descifrado

Para que pueda recuperar un archivo de configuración cifrado escribiendo solo unas pocas líneas de Javascript.

Tenga en cuenta que poner un archivo config.RSAen un repositorio de git lo convertiría efectivamente en un archivo binario y, por lo tanto, perdería muchos de los beneficios de algo como Git, por ejemplo, la capacidad de elegir cambios en él.

La solución a eso podría ser encriptar pares de valores clave o quizás solo valores. Puede encriptar todos los valores, por ejemplo, si tiene un archivo separado para información confidencial, o encriptar solo los valores sensibles si tiene todos los valores en un archivo. (vea abajo)

Mi ejemplo anterior es un poco inútil para cualquiera que quiera hacer una prueba con él, o como un ejemplo para comenzar, ya que supone la existencia de algunas claves RSA y un archivo de configuración cifrado config.RSA.

Aquí hay algunas líneas adicionales de código agregadas para crear claves RSA y un archivo de configuración para jugar.

const fs = require('fs');
const NodeRSA = require('node-rsa');

/////////////////////////////
// Generate some keys for testing
/////////////////////////////

const examplekey = new NodeRSA({b: 2048});

fs.writeFileSync('private.key', examplekey.exportKey('pkcs8-private'));
fs.writeFileSync('public.key', examplekey.exportKey('pkcs8-public'));

/////////////////////////////
// Do this on the Machine creating the config file
/////////////////////////////

const configToStore = {Goodbye: 'Cruel world'};

let publickey = new NodeRSA();
publickey.importKey(fs.readFileSync('public.key', 'utf8'));

fs.writeFileSync('config.RSA', publickey.encrypt(configToStore, 'base64'), 'utf8');

/////////////////////////////
// Do this on the Machine consuming the config file
/////////////////////////////

let privatekey = new NodeRSA();
privatekey.importKey(fs.readFileSync('private.key', 'utf8'));

const config = privatekey.decrypt(fs.readFileSync('config.RSA', 'utf8'), 'json');
console.log('decrypted: ', config);

Cifrar solo valores

fs.writeFileSync('config.RSA', JSON.stringify(config,null,2), 'utf8');

ingrese la descripción de la imagen aquí

Puede descifrar un archivo de configuración con valores cifrados usando algo como esto.

const savedconfig = JSON.parse(fs.readFileSync('config.RSA', 'utf8'));
let config = {...savedconfig};
Object.keys(savedconfig).forEach(key => {
    config[key] = privatekey.decrypt(savedconfig[key], 'utf8');
});

Con cada elemento de configuración en una línea separada (por ejemplo, Helloy Goodbyesuperior), Git reconocerá mejor lo que está sucediendo en un archivo y almacenará los cambios en los elementos de información como diferencias en lugar de archivos completos. Git también podrá gestionar fusiones y selecciones de cerezas, etc. mejor.

Sin embargo, cuanto más desee controlar los cambios de versión a información confidencial, más se moverá hacia una solución de REPOSITORIO SEGURO (2) y más lejos de una solución de INFORMACIÓN ENCRITADA (3).

Ivan
fuente
3

Se puede usar Vault, que asegura, almacena y controla el acceso a tokens, contraseñas, certificados, claves API, etc. Por ejemplo, Ansible usa Ansible Vault, que trata con contraseñas o certificados usados ​​en libros

El ruso
fuente
Encuentro que Ansible Vault es demasiado complejo en comparación con solo crear un archivo de configuración de ejemplo.
icc97
@ icc97 Sí, es triste verdad. Pero necesitamos mencionar esta posibilidad. En mi opinión, para tareas más complejas que almacenar pocas contraseñas para un entorno de usuario único es mejor usar soluciones especializadas desde el principio.
El Ruso
2
Para ayudar a los futuros lectores: Vault y Ansible Vault son proyectos no relacionados muy diferentes con nombres similares
bltavares
2

Aquí hay una técnica que uso:

Creo una carpeta en mi carpeta de inicio llamada: .config

En esa carpeta coloco los archivos de configuración para cualquier cantidad de cosas que quiero externalizar contraseñas y claves.

Normalmente uso la sintaxis de nombre de dominio inverso, como:

com.example.databaseconfig

Luego, en el script bash, hago esto:

#!/bin/bash
source $HOME/.config/com.example.databaseconfig ||exit 1

Esto || exit 1hace que la secuencia de comandos salga si no puede cargar el archivo de configuración.

Usé esa técnica para bash, python y scripts de hormigas.

Soy bastante paranoico y no creo que un archivo .gitignore sea lo suficientemente robusto como para evitar un registro inadvertido. Además, no hay nada que lo controle, por lo que si se realizara un registro, nadie se enteraría de cómo hacerlo.

Si una aplicación en particular requiere más de un archivo, creo una subcarpeta en lugar de un solo archivo.

Michael Potter
fuente
1

Si usa rubí sobre rieles, la gema de Figaro es muy buena, fácil y confiable. También tiene un bajo factor de dolor de cabeza con el entorno de producción.

ahnbizcad
fuente
44
¿Puedes dar algunos detalles sobre lo que hace esa gema? De esa manera, podría (potencialmente) considerarse como una 'práctica' aplicable en muchos idiomas.
mattumotu
medium.com/@MinimalGhost/… tiene una visión general, básicamente parece administrar la extracción de un archivo de configuración
tripleee
0

Confiar pero verificar.

En .gitignoreesto excluiría un directorio "seguro" del repositorio:

secure/

Pero comparto la paranoia de @ Michael Potter . Entonces, para verificar .gitignore, aquí hay una prueba de unidad de Python que generaría un klaxon si este directorio "seguro" alguna vez se registra. Y para verificar la verificación, también se prueba un directorio legítimo:

def test_github_not_getting_credentials(self):
    safety_url = 'https://github.com/BobStein/fliki/tree/master/static'
    danger_url = 'https://github.com/BobStein/fliki/tree/master/secure'

    self.assertEqual(200, urllib.request.urlopen(safety_url).status)

    with self.assertRaises(urllib.error.HTTPError):
        urllib.request.urlopen(danger_url)
Bob Stein
fuente