¿Estrategia para mantener la información secreta, como las claves API, fuera del control de la fuente?

217

Estoy trabajando en un sitio web que permitirá a los usuarios iniciar sesión con credenciales de OAuth de Twitter, Google, etc. Para hacer esto, tengo que registrarme con estos diversos proveedores y obtener una clave API súper secreta que tengo para proteger con promesas contra diversas partes del cuerpo. Si mi llave se engancha, la parte se arranca.

La clave API tiene que viajar con mi fuente, ya que se usa en tiempo de ejecución para realizar solicitudes de autenticación. En mi caso, la clave debe existir dentro de la aplicación en un archivo de configuración o dentro del propio código. Eso no es un problema cuando construyo y publico desde una sola máquina. Sin embargo, cuando agregamos el control de la fuente a la mezcla, las cosas se vuelven más complicadas.

Como soy un bastardo barato, preferiría usar servicios de control de fuente gratuitos como TFS en la nube o GitHub. Esto me deja con un ligero enigma:

¿Cómo puedo mantener mi cuerpo intacto cuando mis claves API están en mi código y mi código está disponible en un repositorio público?

Se me ocurren varias formas de manejar esto, pero ninguna de ellas es tan satisfactoria.

  • Podría eliminar toda la información privada del código y volver a editarla después de la implementación. Implementar esto sería un dolor severo (no detallaré las muchas formas), y no es una opción.
  • Podría encriptarlo. Pero como tengo que descifrarlo, cualquiera con la fuente podría descubrir cómo hacerlo. Inútil.
  • Podría pagar por el control de fuentes privadas. LOL j / k gastar dinero? Por favor.
  • Podría usar las funciones del lenguaje para segregar información confidencial del resto de mi fuente y, por lo tanto, mantenerla fuera del control de la fuente. Esto es lo que estoy haciendo ahora, pero podría arruinarse fácilmente al registrar erróneamente el archivo secreto.

Realmente estoy buscando una forma garantizada de garantizar que no comparta mis partes privadas con el mundo (excepto en Snapchat) que funcione sin problemas durante el desarrollo, la depuración y la implementación, y que también sea infalible. Esto es completamente poco realista. Entonces, ¿qué puedo hacer de manera realista?

Detalles técnicos: VS2012, C # 4.5, el control de origen será el servicio TF o GitHub. Actualmente utiliza una clase parcial para dividir las claves confidenciales en un archivo .cs separado que no se agregará al control de origen. Creo que GitHub puede tener la ventaja ya que .gitignore podría usarse para garantizar que el archivo de clase parcial no esté registrado, pero lo he estropeado antes. Espero un "oh, problema común, así es como lo haces", pero es posible que tenga que conformarme con "eso no apesta tanto como podría haber",: /

Será
fuente
66
Puede asegurarse de que el archivo de configuración que contiene su clave API no esté en el directorio controlado por la fuente, lo que hará imposible verificarlo en primer lugar.
David Sergey
22
BitBucket.org tiene repositorios privados ilimitados. Gratis. Y el importador de repositorios gitHub (mantiene el historial)
Rob van der Veer
44
@Dainius No confío en mis desarrolladores porque los conozco. Intimamente De hecho, soy íntimo conmigo mismo al menos ... no, dejaré que eso mienta. Pero sé lo fácil que es fastidiar y lo difícil que será eliminar el historial de dicho error.
Será el
15
@Dainius: Sí. Miro a cada personaje que mi equipo codifica. Seriamente. No tengo otra opción. No puedo codificar con los ojos vendados. No confiablemente, al menos. Pero lo hago, porque soy mi equipo. Soy el yo en el equipo. Hay un desarrollador, y soy yo. Soy el Si. Soy el tipo que lo arruinará si no lo hace bien. Yo.
Será el
3
¿Por qué estás tratando de compilar la clave en el código en primer lugar? Es habitual poner ese tipo de cosas en un archivo de configuración.
Donal Fellows

Respuestas:

128

No ponga su información secreta en su código. Póngalo en un archivo de configuración que su código lea al inicio. Los archivos de configuración no deben colocarse en el control de versiones, a menos que sean los "valores predeterminados de fábrica", y luego no deben tener ninguna información privada.

Consulte también la pregunta Control de versiones y archivo de configuración personal para saber cómo hacerlo bien.

Philipp
fuente
8
@RobertHarvey simplemente no lo pone en el control de versiones, agregando una regla de ignorar cuando sea necesario. Cualquiera que use el software tiene que construir su propio archivo de configuración con su propia clave API.
Philipp
10
Entonces, cuando vaya a construir y crear una distribución de su software, ¿cómo está seguro de que se envía con un archivo de configuración? A menos que tenga algún archivo con valores predeterminados razonables, generalmente no es razonable esperar que su usuario realice un proceso de creación de un archivo de configuración.
Thomas Owens
44
Bueno, los valores predeterminados de fábrica son una parte, "instaladores" o "asistentes de primera ejecución", otra
johannes
66
Si muchos usuarios tienen su propia instalación, ¿no deberían crear y usar su propia clave API? Múltiples sitios / instalaciones usando la misma clave es probablemente una mala idea. Si solo se trata de una instalación, el uso de un archivo de configuración no es una gran molestia.
Mike Weller
10
@Will, si no puede hacer esto debido a la impracticabilidad de los detalles de implementación, entonces diría que simplemente no tiene las herramientas adecuadas para la implementación. El despliegue utilizando un archivo de configuración secreto no comprometido debe ser completamente sencillo. No puedo ofrecerle consejos específicos ya que vivo en el ecosistema Ruby, no en C #. Pero la gente de Ruby tiende a usar Capistrano para implementaciones automatizadas. Estoy seguro de que C # también tiene su herramienta para la implementación automatizada, y esto debería facilitar el proceso.
Ben Lee
29

Puede poner todas las claves privadas / protegidas como variables de entorno del sistema. Su archivo de configuración se verá así:

private.key=#{systemEnvironment['PRIVATE_KEY']}

Así es como manejamos esos casos y nada entra en el código. Funciona muy bien combinado con diferentes archivos de propiedades y perfiles. Utilizamos diferentes archivos de propiedades para diferentes entornos. En nuestro entorno de desarrollo local, colocamos las claves de desarrollo en los archivos de propiedades para simplificar la configuración local:

private.key=A_DEVELOPMENT_LONG_KEY
Ioannis Tzikas
fuente
Esta sería una solución razonable si puedo hacer que funcione con mi opción de alojamiento. No serán variables de entorno, pero quizás algunos pares de configuración clave / valor que no se borren después de la publicación ...
Será
¿Qué le parece poner esas variables de entorno en su servidor de compilación antes de enviarlas al entorno en vivo? De esa manera, tendrá listos los recursos de producción / archivos de configuración.
Ioannis Tzikas
El servidor de compilación es la máquina de desarrollo, razón por la cual me preocupa que esta información posiblemente se registre accidentalmente en el control de origen.
Will
El problema con esto puede ser que cualquier persona en el servidor pueda leer el entorno.
JasonG
Los envvars de un usuario solo pueden ser leídos por el usuario o root. (Sin embargo, Linux y AIX antiguos no hicieron esto)
Neil McGuigan
27

Pura manera Git

  • .gitignore archivo incluido con datos privados
  • Use una sucursal local, en la que reemplace TEMPLATEconDATA
  • Utilice filtros de borrones / limpieza, en los que el script del filtro (local) realiza el reemplazo bidireccional TEMPLATE<->DATA

Camino mercurial

  • MQ-patch (es) encima del código ficticio, que se reemplaza TEMPLATEcon DATA(los conjuntos de cambios son públicos, el parche es privado)
  • Extensión de palabras clave con palabras clave especialmente diseñadas (expandidas solo en su directorio de trabajo )

Manera agnóstica de SCM

  • Reemplazar palabras clave como parte del proceso de compilación / implementación
Tejón perezoso
fuente
Hmmm ... El consejo de git es bueno, y tu consejo agnóstico me da una buena idea ... Puedo usar eventos de compilación para introducir el archivo en el proceso de publicación, luego eliminarlo después, ayudando así a asegurar que no se añadió accidentalmente al control de código fuente ..
Will
77
No, no y una vez más, ¡no! ignorar archivos es bueno para agregar una personalización muy específica al proceso de compilación o algo así, pero nunca debe usarse para almacenar datos seguros. No almacene datos seguros en el repositorio, incluso si los está ignorando.
shabunc
11
@shabunc - RTFM! Archivo ignorado no almacenado en el repositorio
Lazy Badger
99
@LazyBadger: sé bastante bien que se ignora. También sé que, estando en el repositorio, SIEMPRE existe la posibilidad de que alguien sin equivocarse lo agregue de alguna manera al repositorio. Alguna ruta de configuración externa es mucho mejor.
shabunc
44
@shabunc: buen punto para mantener la configuración fuera de la ruta SCM. Es por eso que, por ejemplo, Postgres le permite omitir las verificaciones de contraseña al colocar la contraseña en un archivo. Pero requieren que el archivo de contraseña se coloque en ~ / .pgpass, lo que presumiblemente no es una ubicación que sea muy conveniente para verificar en el control de origen. Saben que, para la automatización, tienen que darte un arma, pero trabajan duro para evitar que te
Steve Midgley
14

Pongo secretos en los archivos encriptados que luego confirmo. La frase de contraseña se proporciona cuando se inicia el sistema, o se almacena en un archivo pequeño que no confirmo. Es bueno que Emacs gestione alegremente estos archivos cifrados. Por ejemplo, el archivo de inicio de emacs incluye: (cargar "secrets.el.gpg"), que simplemente funciona, solicitándome la contraseña en esas raras ocasiones cuando inicio el editor. No me preocupa que alguien rompa el cifrado.

Ben Hyde
fuente
3
Esta es una gran solución. Me sorprende que no tenga más votos. Trabajo con una empresa que se ocupa de los datos de los estudiantes, que está regulada a nivel federal en los EE. UU., Por lo que deben tener mucho cuidado con las credenciales y los secretos. También son una gran empresa, por lo que deben usar SCM para obtener credenciales para que TI pueda encontrarlas / administrarlas después de que engr las cree. Su solución es exactamente lo que hacen. Han descifrado los archivos de clave que contienen claves de descifrado para dev / staging / prod / etc (un archivo para cada uno). Luego, todos los secretos se cifran y se registran en archivos. Los archivos descifrados se utilizan para obtenerlos en cada entorno.
Steve Midgley
77
Bueno, en cierto sentido, encriptar el secreto (clave API en este caso) solo cambia el problema de no confirmar los datos secretos a no comprometer la frase de contraseña (que ahora se convierte en los datos secretos ). Pero, por supuesto, pedirlo al iniciar el sistema es una buena opción.
siegi
Me gusta esta solución El tipo de archivo cifrado que confirma puede ser un archivo KeePass. Tendría una entrada para cada entorno, utilizando el notescampo para almacenar el contenido del archivo .env. Hace unos meses escribí una herramienta que puede leer un archivo keepass y crear un archivo .env usando el notescampo de una entrada. Estoy pensando en agregar una función para poder hacer lo mejor require('switchenv').env()en el programa Node.js y crear variables process.env basadas en la entrada que coincida con NODE_ENV o algo así. -> github.com/christiaanwesterbeek/switchenv
Christiaan Westerbeek
14

Esto es muy específico de Android / Gradle, pero podría definir las claves en su gradle.propertiesarchivo global ubicado en user home/.gradle/. Esto también es útil, ya que puede utilizar diferentes propiedades según buildType o flavour, es decir, API para desarrolladores y diferentes para el lanzamiento.

gradle.properties

MY_PRIVATE_API_KEY=12356abcefg

build.gradle

buildTypes {
        debug{
            buildConfigField("String", "GOOGLE_VERIFICATION_API_KEY", "\"" + MY_PRIVATE_API_KEY +"\"")
            minifyEnabled false
            applicationIdSuffix ".debug"
            }
        }

En el código que haría referencia como este

String myAPI = BuildConfig.GOOGLE_VERIFICATION_API_KEY;
scottyab
fuente
BuildConfig se traduce en el archivo fuente correspondiente, por lo que la ingeniería inversa simple en su apk revelará todas esas claves y secretos que usted pone en BuildConfig
Dmitri Livotov
1
De hecho, un punto válido. Pero la pregunta era sobre cómo mantener las claves API fuera del código fuente, no el binario.
scottyab
11

Se supone que no debe distribuir esa clave con su aplicación o almacenarla en el repositorio de código fuente. Esta pregunta es cómo hacer eso, y eso no es lo que normalmente se hace.

Aplicación web móvil

Para Android / iPhone, el dispositivo debe solicitar la CLAVE de su propio servicio web cuando la aplicación se ejecuta por primera vez. La clave se almacena en un lugar seguro. En caso de que el editor cambie o revoque la clave. Su servicio web puede publicar una nueva clave.

Aplicación web alojada

Los clientes que usen una licencia de su software deberán ingresar la clave manualmente cuando configuren el software por primera vez. Puede darles a todos la misma clave, diferentes claves o ellos obtienen la suya.

Código fuente publicado

Usted almacena su código fuente en un repositorio público pero no la CLAVE. En la configuración del archivo, agregue las líneas * coloque la tecla aquí * . Cuando un desarrollador usa su código fuente, hace una copia del sample.cfgarchivo y agrega su propia clave.

No mantiene su config.cfgarchivo utilizado para desarrollo o producción en el repositorio.

Reactgular
fuente
44
Esta pregunta es cómo hacer eso , no, absolutamente NO. El hecho es que estas claves deben ser utilizadas por código, por lo tanto, se debe acceder mediante código, y eso generalmente significa a través de código o archivos de configuración, que si no están en la fuente juntas, al menos están cerca y pueden terminar accidentalmente en fuente. La aplicación web alojada no tiene sentido, desafortunadamente. No tuvo que solicitar una clave de API para iniciar sesión en StackOverflow a través de su cuenta (hipotética) de Facebook. la tecla de posición aquí es una simplificación masiva que no funcionará en entornos de desarrollo-> pub como se describe en la P.
Será el
He respondido la pregunta correctamente, al igual que muchos otros. El hecho de que no haya aceptado una de ellas implica que no comprende cómo trabajar con estas claves.
Reactgular
77
Entonces, ¿cómo protegemos el servicio web de publicación de claves? ¿Usando otra llave?
Jiangge Zhang
Lo mismo que dijo @JianggeZhang: este es un consejo peligroso
David K. Hess el
5

Utilice variables de entorno para cosas secretas que cambian para cada servidor.

http://en.wikipedia.org/wiki/Environment_variable

Cómo usarlos depende del idioma.

Filipe Giusti
fuente
3
La seguridad a través de la oscuridad no es un enfoque recomendado para muchos. ¿Te gustaría elaborar tu respuesta para ser más claro?
2
Eso no es oscuridad, las variables de entorno solo están disponibles para el usuario que las agregó, por lo que todas sus credenciales tienen la misma protección del contexto de usuario que ejecuta su aplicación. Actualicé la respuesta para incluir el concepto de variables de entorno. ¿Eso está más claro?
Filipe Giusti
4

Creo que este es un problema con el que todos han tenido problemas en algún momento.

Aquí hay un flujo de trabajo que he usado, que podría funcionar para usted. Utiliza .gitignore con un toque:

  1. Todos los archivos de configuración van en una carpeta especial (con archivos de configuración de muestra - opcional)
  2. Todos los archivos de configuración están incluidos en .gitignore, para que no se hagan públicos
  3. Configure un servidor gitolite (o su servidor git favorito) en una caja privada
  4. Agregue un repositorio con todos los archivos de configuración en el servidor privado
  5. Agregue un script para copiar archivos de configuración a la carpeta especial en el repositorio principal (opcional)

Ahora, puede clonar el repositorio de configuración en cualquier sistema de desarrollo e implementación. Simplemente ejecute el script para copiar los archivos en la carpeta correcta y listo.

Todavía obtienes todos los dulces de GitHub, comparte tu código con el mundo y los datos confidenciales nunca están en el repositorio principal, por lo que no se hacen públicos. Todavía están a solo un tirón y una copia de cualquier sistema de implementación.

Utilizo una caja de 15 $ / año para el servidor privado de git, pero también puedes configurar uno en casa, según el requisito de tacaño ;-)

PD: También puedes usar un submódulo git ( http://git-scm.com/docs/git-submodule ), pero siempre olvido los comandos, ¡reglas tan rápidas y sucias!

Kostas
fuente
2

Utilice el cifrado, pero proporcione una clave maestra al inicio, como una contraseña en la consola, en un archivo que solo el usuario del proceso pueda leer, o de un almacén de claves proporcionado por el sistema como el llavero Mac OS o el almacén de claves de Windows.

Para una entrega continua, querrá varias claves grabadas en algún lugar. La configuración debe estar delimitada del código, pero tiene mucho sentido mantenerla bajo control de revisión.

erickson
fuente
1

3 estrategias, aún no mencionadas (?)

En el check in o en un VCS pre-check in hook

  • buscar cadenas con alta entropía, por ejemplo, detectar secretos
  • Regex busca patrones de clave API bien conocidos. Las claves AKIA * de AWS son un ejemplo, git-secrets es una herramienta basada en eso. Además, nombres de variables como 'contraseña' con asignación constante.
  • busca secretos conocidos: conoces tus secretos, busca texto en ellos. O use una herramienta, escribí esta prueba de concepto .

Estrategias ya mencionadas

  • almacenar en un archivo fuera del árbol de origen
  • tenerlo en el árbol fuente, pero dile a VCS que lo ignore
  • Las variables de entorno son una variación en el almacenamiento de datos fuera del árbol de origen
  • simplemente no les des los valiosos secretos a los desarrolladores
MatthewMartin
fuente
0

Mantenga la información privada fuera de su control de origen. Cree un valor predeterminado no cargado para distribución y haga que su VCS ignore el real. Su proceso de instalación (ya sea manual, configurar / construir o asistente) debe manejar la creación y el llenado del nuevo archivo. Opcionalmente, modifique los permisos en el archivo para garantizar que solo el usuario requerido (¿servidor web?) Pueda leerlo.

Beneficios:

  • No asume entidad de desarrollo == entidad de producción
  • No asume que todos los colaboradores / revisores de código son confiables
  • Evite errores fáciles manteniéndolo fuera del control de versiones
  • Instalaciones fáciles de automatizar con configuración personalizada para control de calidad / compilaciones

Si ya está haciendo esto y lo está registrando accidentalmente, agréguelo a su proyecto .gitignore. Esto hará que sea imposible volver a hacerlo.

No son un montón de servidores gratuitos Git alrededor que proporcionan repositorios privados. Aunque nunca debe versionar sus credenciales, puede ser barato y tener repositorios privados también. ^ _ ^

Adrian Schneider
fuente
-2

En lugar de tener la clave OAuth almacenada como datos sin procesar en cualquier lugar, ¿por qué no ejecutar la cadena a través de algún algoritmo de cifrado y almacenarla como un hash salado? Luego use un archivo de configuración para restaurarlo en tiempo de ejecución. De esa forma, la clave no se almacena en ningún lado, ya sea en un cuadro de desarrollo o en el servidor mismo.

Incluso podría crear una API de modo que su servidor genere automáticamente una nueva clave API salada y hash por solicitud, de esa manera ni siquiera su equipo puede ver la fuente de OAuth.

Editar: Quizás pruebe la Biblioteca criptográfica Javascript de Stanford , ya que permite un cifrado / descifrado simétrico bastante seguro.

David Freitag
fuente
1
Los hashes generalmente son codificados en un sentido. Sin embargo, hay algoritmos de cifrado simétricos que harían lo que sugieres.
3
Amigo, no puedes desencriptar (fácilmente) un hash. Ese es el objetivo de los hashes. Esto es para que ME consuma la API de otra persona, donde me asignan una clave secreta. Mi hashing asegura (a menos que elija un pobre algoritmo y lo rompa cada vez) que no puedo usar su API.
Será el