Examen de secretos de código
Las aplicaciones modernas dependen de las credenciales para acceder a bases de datos, API, servicios en la nube y otras integraciones externas. Cuando los desarrolladores codifican de forma rígida estos secretos directamente en el código fuente, crean una vulnerabilidad de seguridad crítica que puede provocar infracciones de datos, pérdidas financieras y infracciones de cumplimiento.
¿Qué son los secretos de código?
Los secretos de código son credenciales confidenciales y tokens de autenticación que las aplicaciones usan para acceder a los recursos protegidos. A diferencia de las vulnerabilidades de seguridad en la lógica de código (como la inyección de CÓDIGO SQL), los secretos son credenciales válidas que, si se exponen, conceden a los atacantes acceso legítimo a sistemas y servicios.
Piense en secretos como claves para los servicios externos de la aplicación. Al igual que no adjuntaría la clave de la casa al exterior de la puerta, no debería codificar de forma rígida las credenciales en los archivos de código fuente que podrían exponerse a través del control de versiones, los registros o los repositorios públicos.
Tipos comunes de secretos
Las distintas aplicaciones requieren distintos tipos de secretos en función de su arquitectura y dependencias. Comprender qué constituye un secreto le ayuda a identificarlos en el código base.
claves de API
Las claves de API autentican la aplicación en plataformas y servicios externos.
Algunos ejemplos son:
- Puertas de enlace de pago: claves de API de Stripe, PayPal, claves secretas de cliente, credenciales de acceso de Square.
- Plataformas en la nube: claves de suscripción de Azure, claves de acceso de Amazon Web Services (AWS) (y claves de acceso secretas) y claves de API de Google Cloud.
- Communication Services: claves de API de SendGrid, tokens de autenticación de Twilio, webhooks de Slack.
- Análisis y supervisión: identificadores de seguimiento de Google Analytics, claves de API de Datadog, claves de instrumentación de Application Insights.
Este es un ejemplo de una vulnerabilidad de clave de API codificada de forma rígida:
// DANGEROUS: Hard-coded Stripe API key
public class PaymentProcessor
{
private const string StripeKey = "sk_live_51Abc123XYZ789..."; // Real secret exposed
public async Task<bool> ProcessPayment(decimal amount)
{
StripeConfiguration.ApiKey = StripeKey;
// Process payment...
}
}
Si este código se confirma en un repositorio, la clave Stripe se vuelve visible para cualquier persona con acceso al repositorio. En los repositorios públicos, la clave Stripe es visible inmediatamente para toda Internet.
Cadenas de conexión de base de datos
Las cadenas de conexión contienen credenciales para acceder a bases de datos y a menudo incluyen varios componentes confidenciales.
Una cadena de conexión típica incluye:
// DANGEROUS: Hard-coded connection string with credentials
string connectionString = "Server=myserver.database.windows.net;" +
"Database=ProductionDB;" +
"User ID=admin;" +
"Password=MySecretP@ssw0rd123;" +
"Encrypt=true;";
Esta única cadena expone lo siguiente:
- Nombre de host del servidor.
- Nombre de la base de datos.
- Nombre de usuario del administrador.
- Contraseña de administrador.
Un atacante con esta información podría acceder directamente a la base de datos, omitir toda la seguridad de nivel de aplicación y, posiblemente, extraer, modificar o eliminar todos los datos.
Claves privadas y certificados
Las claves privadas para el cifrado, la firma y los certificados TLS nunca deben aparecer en el código fuente.
Algunos ejemplos son:
- Claves privadas RSA para el cifrado.
- Claves privadas SSH para el acceso al servidor.
- Claves de firma JWT para la autenticación de tokens.
- Claves privadas del certificado TLS.
// DANGEROUS: Hard-coded private key
private const string JwtPrivateKey = @"-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7...
-----END PRIVATE KEY-----";
Las claves privadas en el código fuente ponen en peligro la seguridad de todo lo que protegen, lo que permite a los atacantes descifrar datos, suplantar la aplicación o acceder a los servidores.
Tokens de OAuth y secretos de cliente
Las credenciales de OAuth permiten a la aplicación actuar en nombre de los usuarios o acceder a las API protegidas.
Los tipos incluyen:
- Identificadores y secretos de cliente de OAuth.
- Tokens de acceso personal (PAT) para servicios como GitHub, GitLab.
- Credenciales del servicio principal para Microsoft Entra ID.
- Actualizar tokens con acceso de larga duración.
// DANGEROUS: Hard-coded OAuth credentials
public class GitHubService
{
private const string ClientId = "Iv1.abc123def456";
private const string ClientSecret = "1234567890abcdef1234567890abcdef12345678";
}
Estas credenciales podrían permitir a los atacantes acceder a los datos de usuario, realizar acciones como si fueran su aplicación o suplantar a los usuarios.
Claves de cifrado
Las claves de cifrado simétricas usadas para proteger los datos en reposo o en tránsito deben mantenerse en secreto.
// DANGEROUS: Hard-coded encryption key
private static readonly byte[] EncryptionKey = Convert.FromBase64String(
"YourBase64EncodedKeyHere123456789ABCDEF=="
);
Si se expone una clave de cifrado, todos los datos cifrados con esa clave se vuelven vulnerables al descifrado.
¿Por qué los secretos expuestos son peligrosos?
Las implicaciones de seguridad de los secretos expuestos se extienden mucho más allá de los riesgos teóricos. Representan caminos directos para que los atacantes pongan en peligro sistemas, datos y operaciones.
Acceso inmediato y sin restricciones
A diferencia de las vulnerabilidades de código que requieren aptitudes técnicas y condiciones específicas, los secretos expuestos proporcionan acceso instantáneo y legítimo a los recursos protegidos. Un atacante no necesita omitir las medidas de seguridad porque tienen credenciales válidas.
Tenga en cuenta las consecuencias:
- Credenciales de base de datos: acceso directo para leer, modificar o eliminar todo el contenido de la base de datos.
- Claves de servicio en la nube: capacidad de poner en marcha recursos costosos, acceder al almacenamiento o modificar configuraciones.
- Claves de API de pago: cargos no autorizados, reembolsos o acceso a los datos de pago del cliente.
- Tokens de servicio de correo electrónico: enviar correos electrónicos de correo no deseado o phishing desde su dominio.
Persistencia y dificultad de revocación
Una vez que se expone un secreto, el daño persiste hasta que se gira (cambia) la credencial. Hay varios problemas que complican la revocación:
- Historial de Git: incluso si quita un secreto del código actual, permanece en el historial de confirmaciones del repositorio.
- Repositorios bifurcados: en los repositorios públicos, otros desarrolladores pueden bifurcar su código sin alterar el secreto.
- Versiones almacenadas en caché o archivadas: los motores de búsqueda, los servicios de archivo o las memorias caché internas pueden capturar el secreto expuesto.
- Ventana de exposición desconocida: es posible que no sepa cuándo se expone por primera vez el secreto o quién accedió a él.
La única respuesta segura a un secreto expuesto es revocarla inmediatamente y generar una nueva y, a continuación, actualizar todas las aplicaciones que la usan. La remediación puede ser perturbadora y que requiere mucho tiempo.
Consecuencias normativas y de cumplimiento
Muchos marcos normativos requieren explícitamente la protección de credenciales y datos de autenticación confidenciales:
- Estándar de seguridad de datos del sector de tarjetas de pago (PCI DSS): los estándares del sector de tarjetas de pago prohíben almacenar credenciales de autenticación en texto no cifrado.
- Ley de portabilidad y responsabilidad del seguro de salud (HIPAA): la protección de los datos sanitarios requiere proteger las credenciales de acceso.
- Control de organización de servicio 2 (SOC 2): las auditorías de seguridad comprueban que las credenciales se administran y protegen correctamente.
Los secretos expuestos pueden dar lugar a auditorías erróneas, multas normativas y pérdida de certificaciones necesarias para operar en determinados sectores.
Escenarios reales
Comprender cómo los secretos expuestos se traducen en infracciones reales pueden ayudarle a apreciar la urgencia de protegerlos.
Escenario 1: Exposición del repositorio público
Un desarrollador confirma el código en un repositorio público de GitHub con una clave de acceso de AWS codificada de forma rígida. Dentro de las horas siguientes:
- Los bots automatizados que examinan GitHub detectan la clave.
- Los atacantes usan la clave para poner en marcha instancias EC2 costosas para la minería de criptomoneda.
- La empresa recibe una factura de 50 000 USD en un plazo de 24 horas.
- La clave debe revocarse, interrumpiendo todos los servicios legítimos que la usan.
- El incidente requiere investigación de seguridad, respuesta a incidentes y notificaciones legales potenciales.
Este escenario se produce con regularidad. La funcionalidad de protección de push de GitHub impide estos incidentes.
Escenario 2: Exposición de credenciales de base de datos
Una cadena de conexión con credenciales de la base de datos de producción se sube a un repositorio interno. Un contratista con acceso al repositorio:
- Extrae la cadena de conexión del código.
- Se conecta directamente a la base de datos de producción.
- Exporta información personal del cliente.
- La infracción no se detecta durante semanas porque el acceso aparece legítimo.
Los controles de seguridad y registro de nivel de aplicación se omiten completamente porque el atacante tiene credenciales de base de datos válidas.
Escenario 3: clave de API en la aplicación móvil
Una clave de API está codificada de forma rígida en una aplicación móvil. Los investigadores de seguridad descompilan la aplicación y:
- Extraiga la clave de API del código compilado.
- Úselo para acceder a la API sin limitación de velocidad ni seguimiento de uso.
- Enumerar todos los datos de usuario accesibles a través de la API.
- La empresa debe revocar la clave, lo que obliga a todos los usuarios a actualizar la aplicación.
El código móvil y del lado cliente es vulnerable porque los atacantes pueden analizar la aplicación compilada en su tiempo libre.
Procedimientos recomendados para administrar secretos
La protección de secretos requiere implementar patrones seguros durante el ciclo de vida de desarrollo.
Nunca codifiques directamente los secretos
La regla fundamental: los secretos no pertenecen al código fuente, los archivos de configuración a los que realiza el seguimiento el control de versiones o el código del lado cliente.
En lugar de codificar secretos de forma rígida, implemente uno de los métodos siguientes:
- Variables de entorno.
- Archivos de configuración excluidos del control de versiones.
- Servicios de administración de secretos.
- Sistemas de configuración en tiempo de ejecución.
En el ejemplo siguiente se muestra cómo usar variables de entorno:
// SECURE: Reading secret from environment variable
public class PaymentProcessor
{
private readonly string _stripeKey;
public PaymentProcessor()
{
_stripeKey = Environment.GetEnvironmentVariable("STRIPE_API_KEY")
?? throw new InvalidOperationException("STRIPE_API_KEY not configured");
}
}
Uso de servicios de administración de secretos
Las plataformas en la nube modernas proporcionan servicios dedicados para almacenar y acceder a secretos de forma segura.
Algunos ejemplos son:
- Azure Key Vault: almacenamiento secreto centralizado con control de acceso y auditoría.
- AWS Secrets Manager: rotación automatizada y directivas de acceso específicas.
- HashiCorp Vault: administración de secretos empresariales con secretos dinámicos.
- Secretos de GitHub: secretos cifrados para flujos de trabajo de Acciones de GitHub.
Estos servicios proporcionan:
- Cifrado en reposo y en tránsito.
- Registro de acceso y auditoría.
- Controles de permisos detallados.
- Funcionalidades de rotación automática.
- Administración centralizada de secretos.
Rote los secretos con regularidad y de inmediato cuando se expongan
Implementar procesos para rotar secretos según un cronograma y de inmediato cuando se sospeche de una exposición.
La rotación regular limita la ventana de oportunidad si un secreto está en peligro. Rotación inmediata cuando se detecta exposición minimiza los posibles daños.
Uso de secretos diferentes para distintos entornos
Nunca use credenciales de producción en entornos de desarrollo, pruebas o ensayo.
Separar secretos por entorno:
- Desarrollo: credenciales con privilegios inferiores, potencialmente cuentas de prueba.
- Puesta en escena: credenciales independientes que reflejan los permisos de producción.
- Producción: mayor seguridad, acceso supervisado, permisos restringidos.
La separación de secretos por entorno garantiza que las credenciales de desarrollo en peligro no puedan afectar a los sistemas de producción.
Implementación del acceso con privilegios mínimos
Configure los secretos con solo los permisos mínimos necesarios para su finalidad prevista.
Por ejemplo:
- Credenciales de base de datos: acceso de lectura únicamente si no se necesitan escrituras.
- Claves de API: restrinja a puntos de conexión o operaciones específicos.
- Credenciales en la nube: limite a recursos y acciones específicos.
Si se expone un secreto, los permisos limitados reducen los posibles daños.
Escanear repositorios en busca de secretos
Use herramientas automatizadas para detectar secretos en el código antes de confirmarlos o insertarlos en repositorios.
Por ejemplo, GitHub proporciona las siguientes herramientas:
- Examen de secretos de GitHub: examina automáticamente los patrones de secreto conocidos.
- Protección contra envíos de GitHub: bloquea cualquier confirmación que contenga secretos.
Estas herramientas actúan como redes de seguridad, detectando secretos que los desarrolladores incluyen accidentalmente en el código.