{"id":670,"date":"2026-03-08T12:13:44","date_gmt":"2026-03-08T11:13:44","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=670"},"modified":"2026-03-24T18:08:02","modified_gmt":"2026-03-24T17:08:02","slug":"lab-detecting-preventing-secret-leaks-ci-cd-pipelines-3","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-detecting-preventing-secret-leaks-ci-cd-pipelines-3\/","title":{"rendered":"Lab: Detecci\u00f3n y Prevenci\u00f3n de Fugas de Secretos en Pipelines CI\/CD"},"content":{"rendered":"<h2>Descripci\u00f3n General<\/h2>\n<p>Las fugas de secretos en los pipelines CI\/CD son la causa n\u00famero uno de compromiso de pipelines. Las credenciales expuestas \u2014 claves API, contrase\u00f1as de bases de datos, tokens de acceso a la nube \u2014 proporcionan a los atacantes un camino directo hacia los sistemas de producci\u00f3n. Seg\u00fan el informe State of Secrets Sprawl 2025 de GitGuardian, se detectaron m\u00e1s de 12 millones de nuevos secretos en commits p\u00fablicos de GitHub en un solo a\u00f1o.<\/p>\n<p>El problema no es que los desarrolladores sean descuidados. Es que la entrega moderna de software involucra decenas de archivos de configuraci\u00f3n, variables de entorno y puntos de integraci\u00f3n donde los secretos pueden terminar accidentalmente en el control de versiones. Una sola clave AWS filtrada puede costar a una organizaci\u00f3n decenas de miles de d\u00f3lares en minutos.<\/p>\n<p>Este laboratorio pr\u00e1ctico te gu\u00eda a trav\u00e9s de la configuraci\u00f3n de una estrategia de detecci\u00f3n de secretos multicapa que cubre tres puntos de control cr\u00edticos:<\/p>\n<ul>\n<li><strong>Escaneo pre-commit<\/strong> \u2014 Detecta secretos antes de que lleguen al repositorio.<\/li>\n<li><strong>Escaneo en el pipeline<\/strong> \u2014 Bloquea pull requests y pushes que contengan secretos.<\/li>\n<li><strong>Escaneo post-commit y en tiempo de ejecuci\u00f3n<\/strong> \u2014 Detecta secretos que se filtran y activa la remediaci\u00f3n.<\/li>\n<\/ul>\n<p>Al finalizar este laboratorio, tendr\u00e1s una configuraci\u00f3n funcional de defensa en profundidad utilizando gitleaks, truffleHog, GitHub secret scanning y reglas de detecci\u00f3n personalizadas.<\/p>\n<h2>Requisitos Previos<\/h2>\n<p>Antes de comenzar, aseg\u00farate de tener lo siguiente:<\/p>\n<ul>\n<li><strong>Git 2.30+<\/strong> instalado y configurado.<\/li>\n<li><strong>Python 3.8+<\/strong> con <code>pip<\/code> disponible.<\/li>\n<li><strong>Docker<\/strong> (opcional, pero recomendado para escaneo en contenedores).<\/li>\n<li>Una <strong>cuenta de GitHub<\/strong> con acceso para crear repositorios (necesario para el ejercicio de GitHub secret scanning).<\/li>\n<li>Una <strong>terminal<\/strong> (macOS, Linux o WSL en Windows).<\/li>\n<\/ul>\n<p>No se necesitan secretos reales ni cuentas en la nube. Utilizaremos secretos de prueba plantados intencionalmente a lo largo de este laboratorio.<\/p>\n<h2>Configuraci\u00f3n del Entorno<\/h2>\n<h3>Paso 1: Crear un Repositorio de Prueba<\/h3>\n<p>Comienza creando un repositorio Git nuevo que utilizaremos para todos los ejercicios:<\/p>\n<pre><code>mkdir secret-leak-lab && cd secret-leak-lab\ngit init\necho \"# Secret Leak Detection Lab\" > README.md\ngit add README.md\ngit commit -m \"Initial commit\"<\/code><\/pre>\n<h3>Paso 2: Plantar Secretos de Prueba Intencionales<\/h3>\n<p>Necesitamos secretos realistas (pero falsos) en varias ubicaciones para simular un escenario del mundo real. Crea los siguientes archivos:<\/p>\n<p><strong>Un archivo <code>.env<\/code> con credenciales de base de datos:<\/strong><\/p>\n<pre><code>cat > .env <<'EOF'\nDB_HOST=localhost\nDB_PORT=5432\nDB_USER=admin\nDB_PASSWORD=SuperSecret123!\nDB_NAME=production_db\nSECRET_KEY=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6\nEOF<\/code><\/pre>\n<p><strong>Un script Python con una clave AWS codificada directamente:<\/strong><\/p>\n<pre><code>cat > deploy.py <<'EOF'\nimport boto3\n\n# WARNING: These are intentionally fake credentials for testing\nAWS_ACCESS_KEY_ID = \"AKIAIOSFODNN7EXAMPLE\"\nAWS_SECRET_ACCESS_KEY = \"wJalrXUtnFEMI\/K7MDENG\/bPxRfiCYEXAMPLEKEY\"\n\ndef deploy_to_s3(bucket, file_path):\n    s3 = boto3.client(\n        's3',\n        aws_access_key_id=AWS_ACCESS_KEY_ID,\n        aws_secret_access_key=AWS_SECRET_ACCESS_KEY\n    )\n    s3.upload_file(file_path, bucket, file_path)\n    print(f\"Deployed {file_path} to s3:\/\/{bucket}\")\n\nif __name__ == \"__main__\":\n    deploy_to_s3(\"my-app-bucket\", \"dist\/app.zip\")\nEOF<\/code><\/pre>\n<p><strong>Un archivo YAML de configuraci\u00f3n con una cadena de conexi\u00f3n a base de datos:<\/strong><\/p>\n<pre><code>cat > config.yml <<'EOF'\napp:\n  name: my-application\n  environment: production\n\ndatabase:\n  url: \"postgresql:\/\/admin:SuperSecret123!@db.example.com:5432\/prod\"\n  pool_size: 10\n\nredis:\n  url: \"redis:\/\/:MyRedisPassword@cache.example.com:6379\/0\"\nEOF<\/code><\/pre>\n<p><strong>Un Dockerfile con una clave API en una instrucci\u00f3n ENV:<\/strong><\/p>\n<pre><code>cat > Dockerfile <<'EOF'\nFROM python:3.11-slim\n\nWORKDIR \/app\nCOPY requirements.txt .\nRUN pip install -r requirements.txt\n\n# WARNING: Never do this in production\nENV API_KEY=sk-proj-abc123def456ghi789jkl012mno345pqr678stu901vwx234\nENV STRIPE_SECRET_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc\n\nCOPY . .\nCMD [\"python\", \"app.py\"]\nEOF<\/code><\/pre>\n<h3>Paso 3: Verificar el Estado \"Antes\"<\/h3>\n<p>En este punto, nada impide que estos secretos sean commiteados:<\/p>\n<pre><code>git add -A\ngit status<\/code><\/pre>\n<p>Git prepara todo felizmente, secretos incluidos. No hay hooks, ni escaneo, ni barreras de protecci\u00f3n. Este es el estado en el que la mayor\u00eda de los repositorios comienzan. Nuestro objetivo es cambiar eso.<\/p>\n<pre><code># Resetear el staging para poder probar el escaneo antes de commitear\ngit reset HEAD<\/code><\/pre>\n<h2>Ejercicio 1: Escaneo Pre-commit con gitleaks<\/h2>\n<p><a href=\"https:\/\/github.com\/gitleaks\/gitleaks\" target=\"_blank\" rel=\"noopener\">Gitleaks<\/a> es una herramienta de c\u00f3digo abierto dise\u00f1ada para detectar secretos codificados directamente en repositorios Git. Soporta el escaneo del directorio de trabajo, el historial de commits y puede ejecutarse como un hook pre-commit para bloquear secretos antes de que sean commiteados.<\/p>\n<h3>Instalar gitleaks<\/h3>\n<p>Elige tu m\u00e9todo de instalaci\u00f3n preferido:<\/p>\n<pre><code># macOS (Homebrew)\nbrew install gitleaks\n\n# Docker\ndocker pull zricethezav\/gitleaks:latest\n\n# Go (desde el c\u00f3digo fuente)\ngo install github.com\/gitleaks\/gitleaks\/v8@latest<\/code><\/pre>\n<p>Verifica la instalaci\u00f3n:<\/p>\n<pre><code>gitleaks version<\/code><\/pre>\n<h3>Ejecutar gitleaks Manualmente<\/h3>\n<p>Escanea el directorio de trabajo en busca de secretos:<\/p>\n<pre><code>gitleaks detect --source . -v<\/code><\/pre>\n<p>Deber\u00edas ver una salida similar a la siguiente:<\/p>\n<pre><code>Finding:     AWS_ACCESS_KEY_ID = \"AKIAIOSFODNN7EXAMPLE\"\nSecret:      AKIAIOSFODNN7EXAMPLE\nRuleID:      aws-access-key-id\nEntropy:     3.52\nFile:        deploy.py\nLine:        4\n\nFinding:     AWS_SECRET_ACCESS_KEY = \"wJalrXUtnFEMI\/K7MDENG\/bPxRfiCYEXAMPLEKEY\"\nSecret:      wJalrXUtnFEMI\/K7MDENG\/bPxRfiCYEXAMPLEKEY\nRuleID:      aws-secret-access-key\nEntropy:     4.71\nFile:        deploy.py\nLine:        5\n\nFinding:     DB_PASSWORD=SuperSecret123!\nSecret:      SuperSecret123!\nRuleID:      generic-credential\nEntropy:     3.40\nFile:        .env\nLine:        4\n\nFinding:     STRIPE_SECRET_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc\nSecret:      sk_live_4eC39HqLyjWDarjtT1zdp7dc\nRuleID:      stripe-secret-key\nEntropy:     4.20\nFile:        Dockerfile\nLine:        9\n\n12:14PM INF 6 commits scanned.\n12:14PM WRN leaks found: 6<\/code><\/pre>\n<p>Gitleaks identifica correctamente claves AWS, credenciales gen\u00e9ricas, claves de Stripe y m\u00e1s. Cada hallazgo incluye el archivo, el n\u00famero de l\u00ednea y la regla de detecci\u00f3n que lo activ\u00f3.<\/p>\n<h3>Configurar gitleaks como Hook Pre-commit<\/h3>\n<p>El framework <a href=\"https:\/\/pre-commit.com\/\" target=\"_blank\" rel=\"noopener\">pre-commit<\/a> facilita la ejecuci\u00f3n autom\u00e1tica de gitleaks antes de cada commit. Primero, instala pre-commit:<\/p>\n<pre><code>pip install pre-commit<\/code><\/pre>\n<p>Crea un archivo <code>.pre-commit-config.yaml<\/code> en la ra\u00edz de tu repositorio:<\/p>\n<pre><code>repos:\n  - repo: https:\/\/github.com\/gitleaks\/gitleaks\n    rev: v8.21.2\n    hooks:\n      - id: gitleaks<\/code><\/pre>\n<p>Instala el hook:<\/p>\n<pre><code>pre-commit install<\/code><\/pre>\n<h3>Probar el Hook: Commitear un Secreto (Bloqueado)<\/h3>\n<pre><code>git add deploy.py\ngit commit -m \"Add deployment script\"<\/code><\/pre>\n<p>El commit es bloqueado:<\/p>\n<pre><code>Detect hardcoded secrets.................................................Failed\n- hook id: gitleaks\n- exit code: 1\n\n12:15PM WRN leaks found: 2\n<\/code><\/pre>\n<p>El hook pre-commit detiene el commit por completo. El secreto nunca llega al historial de Git.<\/p>\n<h3>Probar el Hook: Commitear C\u00f3digo Limpio (Pasa)<\/h3>\n<p>Crea un archivo sin secretos:<\/p>\n<pre><code>cat > utils.py <<'EOF'\ndef format_date(date_obj):\n    return date_obj.strftime(\"%Y-%m-%d\")\n\ndef sanitize_input(user_input):\n    return user_input.strip().replace(\"<\", \"&lt;\").replace(\">\", \"&gt;\")\nEOF\n\ngit add utils.py\ngit commit -m \"Add utility functions\"<\/code><\/pre>\n<p>Salida:<\/p>\n<pre><code>Detect hardcoded secrets.................................................Passed\n[main abc1234] Add utility functions\n 1 file changed, 5 insertions(+)\n<\/code><\/pre>\n<p>El c\u00f3digo limpio pasa sin problemas.<\/p>\n<h2>Ejercicio 2: Escaneo en el Pipeline con gitleaks en GitHub Actions<\/h2>\n<p>Los hooks pre-commit son una primera capa s\u00f3lida, pero se ejecutan localmente y pueden ser eludidos. El escaneo en el pipeline a\u00f1ade una capa de aplicaci\u00f3n del lado del servidor que no puede ser omitida.<\/p>\n<h3>Crear el Workflow de GitHub Actions<\/h3>\n<p>Crea el archivo <code>.github\/workflows\/secret-scan.yml<\/code> con el siguiente contenido:<\/p>\n<pre><code>name: Secret Scanning\n\non:\n  push:\n    branches: [main, develop]\n  pull_request:\n    branches: [main]\n\njobs:\n  gitleaks:\n    name: Detect Secrets with gitleaks\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Run gitleaks\n        uses: gitleaks\/gitleaks-action@v2\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}\n<\/code><\/pre>\n<p>El par\u00e1metro <code>fetch-depth: 0<\/code> es cr\u00edtico \u2014 asegura que el historial completo de Git est\u00e9 disponible para que gitleaks pueda escanear todos los commits, no solo el m\u00e1s reciente.<\/p>\n<h3>C\u00f3mo Funciona en la Pr\u00e1ctica<\/h3>\n<p><strong>Escenario A: Un PR con un secreto filtrado.<\/strong> Un desarrollador agrega accidentalmente una clave API a un archivo de configuraci\u00f3n y abre un pull request. La acci\u00f3n de gitleaks escanea el diff, detecta el secreto y marca la verificaci\u00f3n como fallida. El PR no puede fusionarse hasta que se elimine el secreto.<\/p>\n<p><strong>Escenario B: Un PR limpio.<\/strong> Un desarrollador abre un PR con l\u00f3gica de aplicaci\u00f3n y sin secretos. La acci\u00f3n de gitleaks escanea el diff, no encuentra nada y marca la verificaci\u00f3n como aprobada. El PR puede proceder a la revisi\u00f3n de c\u00f3digo y fusi\u00f3n.<\/p>\n<p>Para aplicar esto, ve a la configuraci\u00f3n de tu repositorio <strong>Settings \u2192 Branches \u2192 Branch protection rules<\/strong> y agrega <code>gitleaks<\/code> como verificaci\u00f3n de estado requerida para la rama <code>main<\/code>. Esto evita que alguien fusione un PR que falle el escaneo de secretos.<\/p>\n<h2>Ejercicio 3: Escaneo en el Pipeline con truffleHog<\/h2>\n<p><a href=\"https:\/\/github.com\/trufflesecurity\/trufflehog\" target=\"_blank\" rel=\"noopener\">TruffleHog<\/a> adopta un enfoque diferente para la detecci\u00f3n de secretos. Adem\u00e1s de la coincidencia de patrones, puede <strong>verificar<\/strong> si los secretos detectados est\u00e1n realmente activos prob\u00e1ndolos contra la API del servicio correspondiente.<\/p>\n<h3>Instalar truffleHog<\/h3>\n<pre><code># pip\npip install trufflehog\n\n# Docker\ndocker pull trufflesecurity\/trufflehog:latest\n\n# Homebrew\nbrew install trufflehog<\/code><\/pre>\n<h3>Ejecutar truffleHog Contra el Repositorio de Prueba<\/h3>\n<pre><code># Escanear el repositorio local\ntrufflehog git file:\/\/. --only-verified<\/code><\/pre>\n<p>La bandera <code>--only-verified<\/code> le indica a truffleHog que solo reporte secretos que ha confirmado como activos. Esto reduce dr\u00e1sticamente los falsos positivos. Si deseas ver todos los hallazgos, incluyendo los no verificados, omite la bandera:<\/p>\n<pre><code># Mostrar todos los hallazgos (verificados y no verificados)\ntrufflehog git file:\/\/.<\/code><\/pre>\n<p><strong>Verificado vs. No Verificado:<\/strong> Un secreto <em>verificado<\/em> es aquel que truffleHog ha probado contra la API del proveedor y ha confirmado que est\u00e1 activo. Por ejemplo, intentar\u00e1 autenticarse con una clave AWS para ver si funciona. Un secreto <em>no verificado<\/em> coincide con un patr\u00f3n conocido pero no se ha confirmado como activo \u2014 podr\u00eda ser una clave revocada, un marcador de posici\u00f3n o un falso positivo.<\/p>\n<h3>Crear un Job de Pipeline en GitLab CI<\/h3>\n<p>TruffleHog se integra bien en GitLab CI. Agrega lo siguiente a tu <code>.gitlab-ci.yml<\/code>:<\/p>\n<pre><code>stages:\n  - security\n\nsecret-scan:\n  stage: security\n  image:\n    name: trufflesecurity\/trufflehog:latest\n    entrypoint: [\"\"]\n  script:\n    - trufflehog git file:\/\/. --fail --json > trufflehog-results.json\n  artifacts:\n    when: always\n    paths:\n      - trufflehog-results.json\n    expire_in: 30 days\n  rules:\n    - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n    - if: '$CI_COMMIT_BRANCH == \"main\"'\n<\/code><\/pre>\n<p>La bandera <code>--fail<\/code> hace que truffleHog salga con un c\u00f3digo de estado distinto de cero si se encuentran secretos, lo que hace fallar el pipeline. La bandera <code>--json<\/code> genera resultados estructurados que pueden ser analizados por otras herramientas o dashboards.<\/p>\n<h3>gitleaks vs. truffleHog: Cu\u00e1ndo Usar Cada Uno<\/h3>\n<table>\n<thead>\n<tr>\n<th>Caracter\u00edstica<\/th>\n<th>gitleaks<\/th>\n<th>truffleHog<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>M\u00e9todo de detecci\u00f3n<\/td>\n<td>Regex + entrop\u00eda<\/td>\n<td>Regex + entrop\u00eda + verificaci\u00f3n<\/td>\n<\/tr>\n<tr>\n<td>Verificaci\u00f3n de secretos<\/td>\n<td>No<\/td>\n<td>S\u00ed (verifica si el secreto est\u00e1 activo)<\/td>\n<\/tr>\n<tr>\n<td>Velocidad<\/td>\n<td>Muy r\u00e1pido<\/td>\n<td>M\u00e1s lento (debido a la verificaci\u00f3n)<\/td>\n<\/tr>\n<tr>\n<td>Tasa de falsos positivos<\/td>\n<td>Moderada<\/td>\n<td>Baja (con --only-verified)<\/td>\n<\/tr>\n<tr>\n<td>Reglas personalizadas<\/td>\n<td>S\u00ed (.gitleaks.toml)<\/td>\n<td>S\u00ed (detectores personalizados)<\/td>\n<\/tr>\n<tr>\n<td>Soporte pre-commit<\/td>\n<td>Nativo<\/td>\n<td>Mediante script wrapper<\/td>\n<\/tr>\n<tr>\n<td>Ideal para<\/td>\n<td>Verificaciones r\u00e1pidas pre-commit y PR<\/td>\n<td>Escaneos profundos y verificaci\u00f3n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Recomendaci\u00f3n:<\/strong> Usa gitleaks para hooks pre-commit r\u00e1pidos y verificaciones de PR. Usa truffleHog para escaneos profundos peri\u00f3dicos y cuando necesites resultados verificados para priorizar la remediaci\u00f3n.<\/p>\n<h2>Ejercicio 4: GitHub Secret Scanning y Push Protection<\/h2>\n<p>GitHub ofrece escaneo de secretos integrado que funciona a nivel de plataforma. A diferencia de gitleaks y truffleHog, que instalas y configuras t\u00fa mismo, GitHub secret scanning est\u00e1 integrado directamente en la configuraci\u00f3n del repositorio.<\/p>\n<h3>Habilitar Secret Scanning<\/h3>\n<ol>\n<li>Ve a tu repositorio en GitHub.<\/li>\n<li>Navega a <strong>Settings \u2192 Code security and analysis<\/strong>.<\/li>\n<li>Habilita <strong>Secret scanning<\/strong>.<\/li>\n<li>Habilita <strong>Push protection<\/strong>.<\/li>\n<\/ol>\n<p>Push protection es la caracter\u00edstica clave aqu\u00ed. Cuando est\u00e1 habilitada, GitHub bloquear\u00e1 cualquier push que contenga un patr\u00f3n de secreto reconocido antes de que llegue al repositorio.<\/p>\n<h3>Probar Push Protection<\/h3>\n<p>Intenta hacer push de un commit que contenga un patr\u00f3n de secreto conocido, como una clave de acceso AWS o un token de acceso personal de GitHub:<\/p>\n<pre><code># Preparar y commitear un archivo con un secreto de prueba\ngit add deploy.py\ngit commit -m \"Add deploy script\"\ngit push origin main<\/code><\/pre>\n<p>GitHub bloquea el push con un mensaje como:<\/p>\n<pre><code>remote: error: GH013: Repository rule violations found for refs\/heads\/main.\nremote:\n remote: - GITHUB PUSH PROTECTION\nremote:   \u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\nremote:     Resolve the following violations before pushing again\nremote:\nremote:     \u2014 Push cannot contain secrets \u2014\nremote:\nremote:\nremote:      (?) To push, remove secret from commit(s) or follow this URL to allow the secret.\nremote:\nremote:      \u2014 Amazon AWS Access Key ID \u2014\nremote:        locations:\n remote:          - commit: abc1234def5678\n remote:            path: deploy.py:4\nremote:\n! [remote rejected] main -> main (push rule violations)\nerror: failed to push some refs<\/code><\/pre>\n<h3>Manejo de Falsos Positivos<\/h3>\n<p>Si GitHub marca un valor que no es un secreto real (por ejemplo, un dato de prueba o un ejemplo de documentaci\u00f3n), puedes eludir la protecci\u00f3n de push con una raz\u00f3n. GitHub proporciona una URL en el mensaje de rechazo donde puedes:<\/p>\n<ul>\n<li>Seleccionar una raz\u00f3n de omisi\u00f3n: <strong>\"Se usa en pruebas\"<\/strong>, <strong>\"Es un falso positivo\"<\/strong>, o <strong>\"Lo arreglar\u00e9 despu\u00e9s\"<\/strong>.<\/li>\n<li>Enviar la omisi\u00f3n, lo que permite el push pero registra el evento para auditor\u00eda.<\/li>\n<\/ul>\n<p>Los administradores de la organizaci\u00f3n pueden ver todas las omisiones en el dashboard <strong>Security \u2192 Secret scanning<\/strong>.<\/p>\n<h3>El Programa de Socios<\/h3>\n<p>GitHub se asocia con m\u00e1s de 200 proveedores de servicios (AWS, Stripe, Twilio, SendGrid y otros) a trav\u00e9s de su <strong>programa de socios de escaneo de secretos<\/strong>. Cuando se detecta un secreto de un socio:<\/p>\n<ol>\n<li>GitHub notifica al proveedor del servicio autom\u00e1ticamente.<\/li>\n<li>El proveedor revoca la credencial comprometida.<\/li>\n<li>El propietario del repositorio es notificado por correo electr\u00f3nico y en la pesta\u00f1a Security.<\/li>\n<\/ol>\n<p>Esto significa que incluso si un secreto pasa todas las dem\u00e1s defensas y llega a un repositorio p\u00fablico, la ventana de da\u00f1o puede reducirse a minutos mediante la revocaci\u00f3n autom\u00e1tica.<\/p>\n<h2>Ejercicio 5: Patrones de Secretos Personalizados<\/h2>\n<p>Las reglas de detecci\u00f3n predeterminadas cubren proveedores comunes (AWS, Stripe, GitHub, Google Cloud, etc.), pero la mayor\u00eda de las organizaciones tambi\u00e9n tienen secretos internos con formatos personalizados que las herramientas est\u00e1ndar no detectar\u00e1n. Gitleaks soporta reglas personalizadas a trav\u00e9s de un archivo de configuraci\u00f3n <code>.gitleaks.toml<\/code>.<\/p>\n<h3>Crear una Configuraci\u00f3n Personalizada de gitleaks<\/h3>\n<p>Crea un archivo <code>.gitleaks.toml<\/code> en la ra\u00edz del repositorio:<\/p>\n<pre><code>[extend]\n# Extend the default gitleaks configuration\n# useDefault = true\n\n[[rules]]\nid = \"mycompany-api-key\"\ndescription = \"MyCompany Internal API Key\"\nregex = '''MYCOMPANY-KEY-[A-Za-z0-9]{32}'''\ntags = [\"internal\", \"api-key\"]\nkeywords = [\"mycompany-key\"]\n\n[[rules]]\nid = \"internal-database-url\"\ndescription = \"Internal Database Connection String\"\nregex = '''postgresql:\/\/[^:]+:[^@]+@internal-db\\.[a-z0-9-]+\\.corp\\.[a-z]+\\.com'''\ntags = [\"internal\", \"database\"]\nkeywords = [\"internal-db\"]\n\n[[rules]]\nid = \"internal-jwt-signing-key\"\ndescription = \"Internal JWT Signing Key\"\nregex = '''JWT_SIGNING_KEY=[A-Za-z0-9+\/=]{64,}'''\ntags = [\"internal\", \"jwt\"]\nkeywords = [\"jwt_signing_key\"]\n\n[allowlist]\ndescription = \"Global allowlist\"\npaths = [\n  '''(.*?)test(.*?)\\.py''',\n  '''(.*?)_test\\.go''',\n  '''(.*?)spec(.*?)\\.js''',\n  '''(.*?)fixtures(.*?)''',\n  '''README\\.md'''\n]\n\n[[rules.allowlist]]\nid = \"mycompany-api-key\"\nregexes = [\n  '''MYCOMPANY-KEY-EXAMPLE[A-Za-z0-9]{24}''',\n  '''MYCOMPANY-KEY-TEST[A-Za-z0-9]{28}'''\n]\n<\/code><\/pre>\n<h3>Entendiendo la Configuraci\u00f3n<\/h3>\n<ul>\n<li><strong>Reglas personalizadas:<\/strong> Las secciones <code>[[rules]]<\/code> definen patrones espec\u00edficos de tu organizaci\u00f3n. El campo <code>regex<\/code> utiliza expresiones regulares compatibles con Go. El campo <code>keywords<\/code> ayuda a gitleaks a filtrar archivos r\u00e1pidamente \u2014 solo los archivos que contienen la palabra clave son escaneados con la expresi\u00f3n regular completa, lo que mejora el rendimiento.<\/li>\n<li><strong>Lista de permitidos global:<\/strong> La secci\u00f3n <code>[allowlist]<\/code> define rutas que deben excluirse de todo escaneo. Los archivos de prueba, fixtures y documentaci\u00f3n son exclusiones comunes.<\/li>\n<li><strong>Lista de permitidos por regla:<\/strong> La secci\u00f3n <code>[[rules.allowlist]]<\/code> define excepciones para reglas espec\u00edficas. Aqu\u00ed, excluimos claves de ejemplo conocidas que aparecen en documentaci\u00f3n o helpers de prueba.<\/li>\n<\/ul>\n<h3>Probar la Configuraci\u00f3n Personalizada<\/h3>\n<p>Crea un archivo con un secreto personalizado para probar:<\/p>\n<pre><code>cat > internal-config.py <<'EOF'\nMYCOMPANY_API_KEY = \"MYCOMPANY-KEY-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6\"\nEOF<\/code><\/pre>\n<p>Ejecuta gitleaks con la configuraci\u00f3n personalizada:<\/p>\n<pre><code>gitleaks detect --source . --config .gitleaks.toml -v<\/code><\/pre>\n<p>La regla personalizada detecta la clave API interna que habr\u00eda sido pasada por alto por las reglas predeterminadas.<\/p>\n<h2>Construyendo una Estrategia de Defensa en Profundidad<\/h2>\n<p>Ninguna capa individual de detecci\u00f3n de secretos es suficiente. Los desarrolladores pueden omitir los hooks pre-commit. Los escaneos del pipeline solo capturan secretos en el momento del PR. GitHub push protection solo cubre patrones de proveedores conocidos. Una estrategia robusta combina todas estas capas.<\/p>\n<h3>Las Cinco Capas de Defensa de Secretos<\/h3>\n<pre><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502  Capa 1: Hook Pre-commit (gitleaks)                 \u2502\n\u2502  \u2192 Captura secretos antes de que entren al historial\u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502  Capa 2: Escaneo de PR \/ Merge Request (gitleaks)   \u2502\n\u2502  \u2192 Bloquea PRs que contengan secretos               \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502  Capa 3: Push Protection (GitHub \/ GitLab)          \u2502\n\u2502  \u2192 Bloqueo a nivel de plataforma de patrones conocidos\u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502  Capa 4: Escaneo Post-merge (truffleHog programado) \u2502\n\u2502  \u2192 Escaneo profundo semanal con verificaci\u00f3n        \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502  Capa 5: Monitoreo en Tiempo de Ejecuci\u00f3n (vault)   \u2502\n\u2502  \u2192 Detecta anomal\u00edas en el uso de secretos en prod  \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n<h3>Por Qu\u00e9 Ninguna Capa Individual Es Suficiente<\/h3>\n<ul>\n<li><strong>Los hooks pre-commit<\/strong> pueden ser eludidos con <code>git commit --no-verify<\/code> o usando un cliente Git que no soporte hooks.<\/li>\n<li><strong>Los escaneos del pipeline<\/strong> solo se ejecutan en ramas que activan CI \u2014 los pushes directos a ramas no protegidas o los force pushes pueden omitirlos.<\/li>\n<li><strong>Push protection<\/strong> solo detecta patrones de proveedores conocidos \u2014 los secretos internos personalizados no est\u00e1n cubiertos.<\/li>\n<li><strong>Los escaneos post-merge<\/strong> son reactivos \u2014 encuentran secretos despu\u00e9s de que ya est\u00e1n en el repositorio.<\/li>\n<\/ul>\n<p>Cada capa compensa las debilidades de las otras. Juntas, crean un sistema donde un secreto necesitar\u00eda eludir las cinco capas para pasar desapercibido.<\/p>\n<h3>Workflow Combinado: Escaneo de PR y Escaneo Completo Semanal<\/h3>\n<p>Aqu\u00ed tienes un workflow combinado de GitHub Actions que ejecuta gitleaks en cada PR y realiza un escaneo completo del repositorio semanalmente:<\/p>\n<pre><code>name: Secret Scanning (Multi-Layer)\n\non:\n  push:\n    branches: [main, develop]\n  pull_request:\n    branches: [main]\n  schedule:\n    # Escaneo completo del repositorio cada lunes a las 6:00 AM UTC\n    - cron: '0 6 * * 1'\n\njobs:\n  # Capa 2: Escaneo de PR y push\n  gitleaks-pr-scan:\n    name: gitleaks PR Scan\n    runs-on: ubuntu-latest\n    if: github.event_name == 'push' || github.event_name == 'pull_request'\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Run gitleaks (diff scan)\n        uses: gitleaks\/gitleaks-action@v2\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n  # Capa 4: Escaneo profundo semanal con truffleHog\n  trufflehog-full-scan:\n    name: truffleHog Full Repository Scan\n    runs-on: ubuntu-latest\n    if: github.event_name == 'schedule'\n    steps:\n      - name: Checkout code\n        uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Install truffleHog\n        run: pip install trufflehog\n\n      - name: Run truffleHog (full scan with verification)\n        run: |\n          trufflehog git file:\/\/. --fail --json > trufflehog-results.json || true\n          if [ -s trufflehog-results.json ]; then\n            echo \"::error::Secrets detected in repository. See trufflehog-results.json.\"\n            cat trufflehog-results.json | python -m json.tool\n            exit 1\n          fi\n\n      - name: Upload scan results\n        if: always()\n        uses: actions\/upload-artifact@v4\n        with:\n          name: trufflehog-results\n          path: trufflehog-results.json\n          retention-days: 90\n<\/code><\/pre>\n<p>Este workflow asegura que cada cambio de c\u00f3digo sea escaneado en tiempo real, y que todo el historial del repositorio sea auditado semanalmente en busca de secretos que puedan haberse pasado por alto.<\/p>\n<h2>Limpieza<\/h2>\n<p>Despu\u00e9s de completar el laboratorio, elimina los secretos de prueba y reinicia el repositorio:<\/p>\n<pre><code># Eliminar archivos de prueba con secretos\nrm -f .env deploy.py config.yml Dockerfile internal-config.py\n\n# Eliminar configuraciones de prueba (opcional \u2014 cons\u00e9rvelas si desea reutilizarlas)\n# rm -f .gitleaks.toml .pre-commit-config.yaml\n\n# Commitear la limpieza\ngit add -A\ngit commit -m \"Remove test secrets from lab exercises\"\n\n# Si deseas eliminar completamente los secretos del historial de Git,\n# usa git-filter-repo (m\u00e1s exhaustivo que git filter-branch):\npip install git-filter-repo\ngit filter-repo --invert-paths --path deploy.py --path .env --path config.yml --path Dockerfile\n<\/code><\/pre>\n<p><strong>Importante:<\/strong> Simplemente eliminar archivos no los remueve del historial de Git. Cualquier persona con acceso al repositorio a\u00fan puede encontrar los secretos en commits anteriores. Usa <code>git-filter-repo<\/code> para reescribir el historial y eliminar permanentemente los archivos sensibles. Despu\u00e9s de reescribir el historial, haz force-push al remoto y pide a todos los colaboradores que vuelvan a clonar el repositorio.<\/p>\n<h2>Conclusiones Clave<\/h2>\n<ul>\n<li><strong>Los secretos en CI\/CD son el vector de ataque m\u00e1s com\u00fan para el compromiso de pipelines.<\/strong> Una sola credencial filtrada puede dar a un atacante acceso completo a la infraestructura de producci\u00f3n.<\/li>\n<li><strong>Los hooks pre-commit con gitleaks proporcionan el ciclo de retroalimentaci\u00f3n m\u00e1s r\u00e1pido.<\/strong> Los desarrolladores son alertados inmediatamente, antes de que el secreto entre en el historial de Git.<\/li>\n<li><strong>El escaneo en el pipeline es una capa de aplicaci\u00f3n obligatoria.<\/strong> No puede ser eludida por los desarrolladores y asegura que ning\u00fan PR con secretos sea fusionado.<\/li>\n<li><strong>La capacidad de verificaci\u00f3n de truffleHog reduce los falsos positivos dr\u00e1sticamente.<\/strong> \u00dasalo para escaneos profundos programados donde la precisi\u00f3n importa m\u00e1s que la velocidad.<\/li>\n<li><strong>GitHub push protection y el programa de socios agregan defensa a nivel de plataforma<\/strong> que funciona sin ninguna configuraci\u00f3n en tus pipelines CI\/CD.<\/li>\n<li><strong>Las reglas de detecci\u00f3n personalizadas son esenciales<\/strong> para capturar secretos espec\u00edficos de la organizaci\u00f3n que las herramientas est\u00e1ndar pasar\u00e1n por alto. Invierte tiempo en escribir reglas para los formatos de claves internos de tu organizaci\u00f3n.<\/li>\n<li><strong>La defensa en profundidad es la \u00fanica estrategia confiable.<\/strong> Ninguna herramienta o capa individual lo detecta todo. Combina pre-commit, escaneo en el pipeline, push protection, escaneos programados y monitoreo en tiempo de ejecuci\u00f3n para una cobertura integral.<\/li>\n<\/ul>\n<h2>Pr\u00f3ximos Pasos<\/h2>\n<p>Ahora que puedes detectar y prevenir fugas de secretos, el siguiente paso es eliminar los secretos codificados directamente adoptando una gesti\u00f3n adecuada de secretos:<\/p>\n<ul>\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/secrets-management-ci-cd-pipelines-patterns-vault\/\">Gesti\u00f3n de Secretos en Pipelines CI\/CD<\/a> \u2014 Aprende a usar HashiCorp Vault, AWS Secrets Manager y almacenes de secretos nativos de CI\/CD para inyectar secretos en tiempo de ejecuci\u00f3n sin almacenarlos nunca en el c\u00f3digo.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/short-lived-credentials-workload-identity-federation-ci-cd-2\/\">Credenciales de Corta Duraci\u00f3n y Federaci\u00f3n de Identidades de Carga de Trabajo<\/a> \u2014 Elimina los secretos de larga duraci\u00f3n por completo usando federaci\u00f3n de identidades de carga de trabajo basada en OIDC para autenticar pipelines ante proveedores de nube sin credenciales almacenadas.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Descripci\u00f3n General Las fugas de secretos en los pipelines CI\/CD son la causa n\u00famero uno de compromiso de pipelines. Las credenciales expuestas \u2014 claves API, contrase\u00f1as de bases de datos, tokens de acceso a la nube \u2014 proporcionan a los atacantes un camino directo hacia los sistemas de producci\u00f3n. Seg\u00fan el informe State of Secrets &#8230; <a title=\"Lab: Detecci\u00f3n y Prevenci\u00f3n de Fugas de Secretos en Pipelines CI\/CD\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-detecting-preventing-secret-leaks-ci-cd-pipelines-3\/\" aria-label=\"Leer m\u00e1s sobre Lab: Detecci\u00f3n y Prevenci\u00f3n de Fugas de Secretos en Pipelines CI\/CD\">Leer m\u00e1s<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,58],"tags":[],"post_folder":[],"class_list":["post-670","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-pipeline-hardening"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/670","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/comments?post=670"}],"version-history":[{"count":3,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/670\/revisions"}],"predecessor-version":[{"id":680,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/670\/revisions\/680"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=670"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=670"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=670"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=670"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}