Security Tools

Secrets Detection for Engineering Teams

Secrets detection that finds leaked credentials first. Git history, CI logs, and container layers, with pattern and entropy analytics plus a rotation-first runbook.

A dark repository tree with cyan commit nodes and one exposed red key glowing in the history, representing secrets detection

Secrets detection is the least glamorous control in security and one of the highest-yield. Credentials leak constantly through entirely ordinary engineering work: a key committed while debugging, a token echoed into CI output, an environment file baked into a container layer, an authorization header captured in an error trace. None of these require an attacker to do anything. The credential is simply sitting somewhere readable, and whoever finds it first wins. This guide covers finding them first.

Leaked credentials map to MITRE ATT&CK T1552.001 — Unsecured Credentials: Credentials In Files and T1552.004 — Private Keys, and they lead directly to T1078.004 — Valid Accounts: Cloud Accounts. This is the quiet path into most cloud estates, and it rarely produces a dramatic detection because nothing was broken into.

What is secrets detection?

Secrets detection is the practice of finding credentials in places they were never meant to live, before someone else does. The credential classes that matter are broader than “API keys”:

Secret typeWhere it leaksBlast radiusRotation difficulty
Cloud access keysGit, CI logs, image layersOften the entire accountLow
Provider API keysGit, client bundles, error tracesThe provider’s data and spendLow
Database credentialsConfig files, image layersAll stored dataMedium
Private keys (TLS, SSH)Git, backups, image layersImpersonation and interceptionHigh
Signing keysCI config, artifact pipelinesEvery artifact you ever signedVery high
OAuth client secretsClient-side bundles, gitThe app’s whole identityMedium
Webhook signing secretsGit, documentationForged inbound eventsLow
Internal service tokensSource, container envLateral movement across servicesMedium

The two rows worth dwelling on are signing keys and private keys, because their rotation cost is what makes teams hesitate, and hesitation is exactly what an attacker needs. A leaked signing key potentially invalidates the trust of every artifact you have ever published, which is why it belongs in a hardware security module or a managed signing service rather than anywhere a detection rule could find it.

Where do secrets actually leak?

Four surfaces account for the overwhelming majority, and they need different tooling.

Git history is the best known and still the largest. The failure mode is subtle: a developer commits a key, notices, and removes it in the next commit. The working tree is clean, so working-tree scanners report nothing, while the secret remains trivially readable in the earlier commit forever. Any scanner that does not walk full history is providing false assurance.

CI job logs are the most underestimated. Build systems echo commands, print environment state on failure, and emit verbose diagnostics precisely when something is going wrong and someone turned on debug output. Job logs are frequently readable by everyone in the organisation and retained for months. Masking helps only for values the platform already knows are secret, which excludes anything derived, decoded, or constructed at runtime.

Container image layers leak because layers are immutable and additive. Copying a .env file in one layer and deleting it in a later one leaves it fully present in the earlier layer, and anyone who pulls the image can read it. This is the exact same logical error as the git case, reappearing in a different system, and it is just as common.

Error traces and APM payloads leak because exception handlers serialise context. A failed HTTP call helpfully includes the request headers, and the request headers include the authorization header. That trace then flows to an observability vendor and sits in a system with a different access model than the one holding the credential.

Two smaller surfaces are worth naming because they are rarely scanned at all. Client-side bundles ship whatever the build embedded, so any key referenced in front-end code is public the moment it deploys, regardless of minification or obfuscation. And infrastructure state files frequently store the plaintext values of every resource they manage, including database passwords and generated keys, which makes a state file committed to a repository one of the highest-value single artifacts an attacker can find. Neither appears in a default scanner configuration.

What telemetry and coverage do you need?

RequirementWhy it mattersCommon failure
Full git history scanningWorking-tree scans miss deleted-then-committed secretsScanner runs on HEAD only
Pre-receive or pre-commit hookPrevents the push rather than reporting itDetection only, no prevention
CI job log scanningSecond-largest leak surfaceNever scanned at all
Container layer scanningLayers retain deleted filesOnly the final image inspected
Live verification of candidatesSeparates real from expiredEvery match treated as critical
Credential-use telemetryDetects exposure becoming compromiseCloud audit logs not correlated
Rotation automationResponse speed is the only variableManual rotation taking days

The fifth row is what separates a usable programme from an ignored one. A scanner that reports every high-entropy string produces thousands of findings, and a team that receives thousands of findings stops reading them. Verifying whether a candidate credential is actually live collapses that list by an order of magnitude and turns the remainder into genuine incidents.

How to detect leaked secrets

Three complementary analytics: one preventing the leak at CI, one finding material at rest, and one catching the moment exposure becomes compromise.

Secret material written to CI job output

Sigma Secret Material Written to CI Job Output
title: Secret Material Written to CI Job Output
id: 5d9a1c37-2e64-4b08-a7f1-6c3b8e2d94a0
status: experimental
description: >
  Detects credential-shaped strings appearing in CI job output. Covers debug mode
  enabled mid-pipeline, environment dumps on failure, and verbose tooling that
  echoes constructed credentials the platform masker does not recognise.
references:
  - https://attack.mitre.org/techniques/T1552/001/
author: Colson
date: 2026/07/28
logsource:
  category: application
  product: cicd
detection:
  provider_patterns:
    log_line|re:
      - 'AKIA[0-9A-Z]{16}'
      - 'ASIA[0-9A-Z]{16}'
      - 'ghp_[A-Za-z0-9]{36}'
      - 'github_pat_[A-Za-z0-9_]{82}'
      - 'xox[baprs]-[A-Za-z0-9-]{10,}'
      - 'sk-[A-Za-z0-9]{32,}'
      - '-----BEGIN (RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----'
      - 'eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.'
  env_dump:
    log_line|re:
      - '(?i)(SECRET|TOKEN|PASSWORD|APIKEY|API_KEY|PRIVATE_KEY)\s*[=:]\s*\S{8,}'
  masked:
    log_line|contains:
      - '***'
      - '[MASKED]'
      - '[REDACTED]'
  condition: (provider_patterns or env_dump) and not masked
fields:
  - repo
  - pipeline_id
  - job_id
  - step_name
  - actor
  - log_line
falsepositives:
  - Example or documentation keys in test fixtures
  - Deliberately revoked keys used in negative test cases
  - Placeholder values in template configuration
level: critical

Note the masked exclusion. Platform maskers do catch registered secrets, and alerting on already-masked output wastes the rule’s credibility. What this catches is the residue: values the masker never knew about because they were decoded, concatenated, or fetched at runtime.

Provider-pattern key material at rest

For repositories, image layers, and archives, a YARA ruleset gives you a portable scanner that runs anywhere, including across artifacts pulled during incident response.

YARA Provider-Pattern Key Material in Repository or Image Layers
rule Leaked_Credential_Material
{
    meta:
        description = "Provider-pattern credential material in repos or image layers"
        author      = "Colson"
        date        = "2026-07-28"
        reference   = "https://attack.mitre.org/techniques/T1552/001/"
        severity    = "high"

    strings:
        $aws_akia    = /AKIA[0-9A-Z]{16}/
        $aws_asia    = /ASIA[0-9A-Z]{16}/
        $gh_pat      = /ghp_[A-Za-z0-9]{36}/
        $gh_fine     = /github_pat_[A-Za-z0-9_]{82}/
        $slack       = /xox[baprs]-[A-Za-z0-9-]{10,}/
        $pem_rsa     = "-----BEGIN RSA PRIVATE KEY-----"
        $pem_ec      = "-----BEGIN EC PRIVATE KEY-----"
        $pem_ssh     = "-----BEGIN OPENSSH PRIVATE KEY-----"
        $env_assign  = /(?i)(SECRET|TOKEN|PASSWORD|API_KEY)[ ]*=[ ]*[A-Za-z0-9\/+_-]{20,}/

        $fp_example  = "EXAMPLE" nocase
        $fp_sample   = "your-api-key-here" nocase
        $fp_test     = "AKIAIOSFODNN7EXAMPLE"

    condition:
        filesize < 20MB
        and any of ($aws_*, $gh_*, $slack, $pem_*, $env_assign)
        and not any of ($fp_*)
}

AKIAIOSFODNN7EXAMPLE is the documented example key that appears in a great deal of tutorial material, and excluding it explicitly removes a meaningful share of false positives on its own. Extend the exclusion set with your own placeholder conventions as you discover them.

Credential use from an unexpected principal or network

Exposure is a risk; use is an incident. This analytic catches the transition.

SPL Credential Use From an Unexpected Principal or Network
index=cloud sourcetype=aws:cloudtrail earliest=-24h
| search userIdentity.type IN ("IAMUser","AssumedRole")
| eval principal = coalesce('userIdentity.userName', 'userIdentity.arn')
| lookup principal_baseline principal
     OUTPUT usual_asn, usual_regions, usual_events, usual_ua, first_seen
| eval anomaly = 0
| eval anomaly = anomaly + if(src_asn != usual_asn, 3, 0)
| eval anomaly = anomaly + if(NOT match(awsRegion, usual_regions), 2, 0)
| eval anomaly = anomaly + if(NOT match(eventName, usual_events), 2, 0)
| eval anomaly = anomaly + if(match(userAgent, "(?i)(curl|python|boto|go-http)"), 1, 0)
| eval anomaly = anomaly + if(eventName IN ("CreateUser","CreateAccessKey",
                              "AttachUserPolicy","PutUserPolicy",
                              "CreateLoginProfile","GetSecretValue"), 4, 0)
| where anomaly >= 5
| stats min(_time) AS first_anomaly, max(_time) AS last_anomaly,
        values(eventName) AS actions, values(src_ip) AS source_ips,
        values(awsRegion) AS regions, max(anomaly) AS peak_score
        by principal
| sort - peak_score

The CreateAccessKey and AttachUserPolicy weighting is deliberate. An attacker who finds a leaked key rarely uses it directly for long; they use it to mint durable, less-monitored access. Those two events immediately following an unusual-origin authentication are close to definitive and connect directly to the patterns in CloudTrail monitoring that matters.

Which false positives will you actually see?

False positiveWhich rule it hitsWhy it happensResolution
Documentation example keysYARA, SigmaTutorials use real-format placeholdersExclude known example values explicitly
Test fixtures with fake credsYARA, SigmaNegative test cases need key-shaped inputPath-exclude fixture directories
Base64 blobs and hashesEntropy scanningHigh entropy, not a credentialRequire pattern or verification, not entropy alone
Already-revoked keysAll scanningHistory full of old rotated keysLive verification resolves this entirely
Minified JS bundlesYARARandom-looking identifiersExclude build output paths
Platform-masked outputSigma CI ruleMasker already handled itThe masked exclusion covers it
Automation from a new regionCredential useLegitimate infrastructure changeBaseline per principal, re-baseline after changes
Lock and checksum filesYARALong random-looking hashesExclude by filename pattern

Rows three and four together explain most of the noise in an untuned programme, and both are resolved by the same practice. Entropy without verification generates the volume; verification removes it. Build verification before you widen coverage, not after.

How do you respond to a leaked secret?

Order matters more here than in almost any other incident type, because the clock started when the secret was pushed rather than when you found it.

  1. Rotate immediately. Before investigating scope, before notifying, before writing a timeline. If the secret is live, every minute of analysis is a minute of attacker opportunity.
  2. Revoke the old credential explicitly. Issuing a replacement does not disable the original on many providers, and an unrevoked old key remains fully valid.
  3. Determine exposure duration. From the introducing commit or build to revocation. Public exposure should be treated as compromised from the first moment.
  4. Pull all usage of the credential across its lifetime. Cloud audit logs, provider access logs, application logs. This establishes whether exposure became use.
  5. Look for durable access established with it. New keys, new users, new policies, new OAuth grants. This determines whether rotation actually ended the incident.
  6. Assess the blast radius by permission, not by intent. What the credential could reach, not what it was created for.
  7. Purge the material where you can, after rotation. History rewriting, log purging, image rebuilds. This is cleanup, not remediation.
  8. Fix the introducing path. A pre-receive hook, a masking gap, a .dockerignore entry. A leak that recurs is a process defect rather than an individual mistake.

Step 1 before step 3 is the part that feels wrong to incident responders trained to preserve evidence and scope before acting. For live credentials it is correct, because the credential’s continued validity is itself the ongoing harm and the logs you need to scope it are not going anywhere.

How to test your secrets detection

In a repository and pipeline you own, using credentials you have already revoked:

  1. Commit a revoked provider key, then delete it in a follow-up commit. Confirm the history scan still finds it, since this is the exact failure mode that matters.
  2. Attempt to push a live-format test credential. Confirm the pre-receive hook blocks the push rather than merely reporting it after the fact.
  3. Echo a constructed credential in a CI step, built by concatenation so the platform masker does not recognise it. Confirm the Sigma rule fires.
  4. Build an image that copies a secret file in one layer and deletes it in a later one. Confirm the layer scan finds it in the earlier layer.
  5. Include a documentation example key. Confirm it does not alert.
  6. Use a revoked key from an unusual network and confirm the credential-use analytic scores it.

Items 1 and 4 are the same logical error in two systems, and a programme that catches one but not the other is extremely common.

How to prevent secrets from leaking

Four further controls carry real weight:

  • Scope every credential minimally. A leaked key limited to reading one bucket is an inconvenience; an administrative key is an outage and a disclosure.
  • Never put a secret in a client bundle. Anything shipped to a browser or mobile app is public by definition, regardless of obfuscation.
  • Strip authorization headers before telemetry export. Do it at the SDK boundary rather than trusting each exception handler.
  • Keep signing keys in an HSM or managed signing service. Their rotation cost makes exposure disproportionately expensive, which links directly to supply chain attack detection.

Which secrets control should you deploy first?

ControlStopsEffortCoverageDeploy when
Pre-receive secret blockingNew git leaksLowHighAlways, first
Full-history scan with verificationExisting leaksLowHighAlways, second
CI log scanningThe second-biggest surfaceLowMediumAlways
Container layer scanningImage-baked secretsMediumMediumYou ship images
Platform env secrets onlyConfig-file leaksLowHighAlways
Short-lived dynamic credentialsStatic secrets entirelyHighVery highYou have a secrets manager
Workload identity federationCloud keys entirelyHighVery highCloud-native workloads

Pre-receive blocking plus a verified full-history scan is a weekend of work and covers the majority of realistic exposure. Dynamic credentials and workload identity are the endgame that makes the whole category mostly disappear.

Common secrets detection mistakes

  • Scanning the working tree only. The deleted-then-committed secret is the common case.
  • Rewriting history instead of rotating. The copies you do not control are already gone.
  • Skipping live verification. Unverified findings become an ignored backlog.
  • Trusting the CI masker completely. It only masks values it was told about.
  • Ignoring container layers. Same error as git, different system.
  • Leaving old keys active after issuing new ones. Replacement is not revocation.
  • Alerting on entropy alone. Every checksum file becomes a finding.
  • Treating a leak as an individual mistake. The introducing path is the actual defect.

Secrets detection checklist

  1. Scan full git history, not just the working tree, across every repository.
  2. Add pre-receive or pre-commit blocking so leaks are prevented rather than reported.
  3. Build live verification so findings are separated into real and expired.
  4. Scan CI job logs continuously, excluding already-masked output.
  5. Scan container image layers individually, not just the final flattened image.
  6. Strip authorization headers at the telemetry SDK boundary.
  7. Exclude documented example keys, fixtures, lockfiles, and build output from scanning.
  8. Correlate cloud audit logs to detect credential use from unexpected principals or networks.
  9. Weight key-creation and policy-attachment events heavily in that analytic.
  10. Rotate first and revoke explicitly; treat purging as cleanup afterwards.
  11. Automate rotation so response speed is not gated on a manual runbook.
  12. Scope every credential to the minimum permission it needs.
  13. Move to short-lived dynamic credentials or workload identity where the platform supports it.
  14. Keep signing keys in an HSM or managed signing service, never in scannable storage.
  15. Fix the introducing path after every leak, since recurrence is a process defect.

The takeaway

Secrets detection works when it scans history rather than the working tree, verifies findings rather than counting them, and covers CI logs and container layers alongside git. Rotate first and treat purging as cleanup, because the copies you cannot see are already outside your control. Then remove the category structurally with short-lived dynamic credentials and workload identity, so the secret that leaks does not exist to leak. Continue with supply chain attack detection, CloudTrail monitoring patterns that matter, and OAuth misconfiguration defensive review, or browse the full Security Tools pillar.

Training & tools referenced

Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.

  • 1PasswordSecrets management and team credential storagePassword Manager
    Compare plans
  • TryHackMeAuthorized labs to practice credential-exposure analysisSecurity Training
    Start training

Frequently asked questions

What is secrets detection?

Secrets detection is the practice of finding credentials that have leaked into places they were never meant to live — git history, CI job logs, container image layers, error traces, and configuration files — before an attacker finds them. It combines provider-specific pattern matching with entropy analysis and, critically, verification that the found credential is actually live.

How do you find leaked secrets in git history?

Scan the full commit history rather than the working tree, because deleting a secret in a later commit leaves it permanently readable in the earlier one. Use provider-specific patterns for known key formats plus entropy analysis for opaque tokens, then verify each candidate against its provider to separate live credentials from expired noise.

Is deleting the commit enough to fix a leaked secret?

No. Rewriting history does not help once the commit has been pushed, cloned, forked, or indexed by any platform or scanner. Assume any secret that reached a remote is public permanently. The only real remediation is rotating the credential, and history rewriting is at best cleanup performed after rotation.

How fast do attackers find leaked keys?

Fast enough that rotation speed is the only variable you control. Public repository push events are streamed continuously and scanned automatically by both defenders and attackers, so exposure is measured in minutes rather than days. Plan your response around the assumption that a public leak is compromised at the moment of the push.

What is the difference between entropy and pattern detection?

Pattern detection matches known credential formats such as a provider's key prefix, which gives high precision and misses anything unrecognised. Entropy detection flags strings that are statistically random, catching unknown formats at the cost of many false positives from hashes and encoded blobs. Production tooling needs both plus live verification to arbitrate.