Taming the Machine Identity Hydra: Practical Steps to Clean Up Ghost Identities
Saw the stats in the latest Hacker News piece about the upcoming webinar—68% of cloud breaches in 2024 were due to compromised service accounts and orphaned keys. It’s a grim reminder that while we obsess over phishing simulations, our IAM environments are often swiss cheese.
The 40:1 non-human-to-human identity ratio sounds insane until you actually audit your environment. Between CI/CD pipelines, SaaS integrations, and temporary OAuth grants, the noise level is deafening. If you aren't actively pruning these, you're basically leaving keys under the doormat.
We’ve started running monthly audits to spot dormant accounts. Here’s a quick Python snippet using boto3 to find AWS IAM users (often service accounts) that haven't used their access keys in over 90 days:
import boto3
from datetime import datetime, timedelta
iam = boto3.client('iam')
cutoff_date = datetime.now() - timedelta(days=90)
users = iam.list_users()['Users']
for user in users:
username = user['UserName']
keys = iam.list_access_keys(UserName=username)['AccessKeyMetadata']
for key in keys:
if key['Status'] == 'Active':
last_used = iam.get_access_key_last_used(AccessKeyId=key['AccessKeyId'])
# Handle cases where key has never been used
last_used_date = last_used.get('AccessKeyLastUsed', {}).get('LastUsedDate')
if not last_used_date or last_used_date.replace(tzinfo=None) < cutoff_date:
print(f"Stale Key: {username} - {key['AccessKeyId']}")
This is just basic hygiene, though. The real nightmare is the shadow IT stuff—API keys hardcoded in repos that former employees generated.
How are you guys handling the lifecycle of non-human identities? Are you relying on native CSP tooling, or have you deployed dedicated secret scanners like TruffleHog or Gitleaks in your pipelines?
Great topic. We've moved away from long-lived credentials entirely for our CI/CD. Using OpenID Connect (OIDC) between GitHub Actions and AWS has cut down the number of static keys we need to manage by about 80%.
For the legacy stuff, we run a nightly TruffleHog scan on our org repos. You'd be surprised how often a dev commits a service_account. to a 'test' branch and forgets it.
The 40:1 ratio doesn't surprise me at all. From a SOC perspective, the hardest part is tuning the alerts. Service accounts have erratic usage patterns by design.
We've started correlating Service Principal sign-ins with IP geolocation and User-Agent strings. If an automation account logs in from a new ASN or a browser-based UA, we nuke the session immediately and rotate the keys.
Python script is handy, but don't forget Azure if you're hybrid. You can use the Microsoft Graph PowerShell SDK to find apps with expired secrets that are still active.
Get-MgServicePrincipal -All | Foreach {
$sp = $_
Get-MgServicePrincipalOwner -ServicePrincipalId $sp.Id | Where-Object { $_.DeletedDateTime -ne $null } |
Foreach { Write-Host "Orphaned App: $($sp.DisplayName)" }
}
It's scary how many 'Zombie' apps are owned by deleted users.
Verified Access Required
To maintain the integrity of our intelligence feeds, only verified partners and security professionals can post replies.
Request Access