IAM est la première ligne de défense de votre infrastructure AWS. Ces 10 bonnes pratiques, issues d'audits réels, vous permettront de réduire drastiquement votre surface d'attaque.
Pourquoi IAM est la surface d'attaque numéro 1
Selon Gartner, 80 % des incidents de sécurité cloud impliquent une mauvaise configuration IAM. Ce chiffre est troublant mais logique : IAM est le système nerveux de votre infrastructure AWS. Une clé d'accès exposée, un rôle avec des permissions trop larges, ou un compte root non protégé peuvent compromettre l'intégralité de votre organisation AWS en quelques minutes. Les attaques réelles que nous avons traitées lors d'audits suivent presque toujours le même schéma : clé IAM trouvée dans un dépôt GitHub public → accès complet à la production → exfiltration de données ou cryptominage massif.
Ce guide détaille les pratiques que nous appliquons systématiquement lors de nos audits et déploiements. Elles ne sont pas théoriques — elles viennent d'incidents réels et de leur remédiation.
1. Compte root : MFA obligatoire et procédure break-glass
Le compte root AWS est la seule identité qui ne peut pas être restreinte par des SCPs. C'est aussi la plus dangereuse. La règle est simple : le compte root ne doit jamais être utilisé pour les opérations quotidiennes, et son accès doit être verrouillé derrière une procédure formelle.
# SCP pour bloquer l'utilisation du compte root dans toute l'organisation
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRootAccountActions",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:PrincipalArn": [
"arn:aws:iam::*:root"
]
}
}
}
]
}
# Checklist compte root :
# [ ] MFA hardware (YubiKey) activé
# [ ] Email root = boîte partagée sécurisée, pas un email personnel
# [ ] Mot de passe root stocké dans un coffre-fort (HashiCorp Vault, 1Password Teams)
# [ ] Procédure break-glass documentée et testée trimestriellement
# [ ] Alerte CloudTrail sur toute utilisation du compte root
2. Principe du moindre privilège — jamais de wildcard en production
Le principe est simple à énoncer, difficile à appliquer : chaque identité doit avoir uniquement les permissions dont elle a besoin, ni plus. En pratique, cela signifie commencer par tout refuser et ajouter les permissions de façon incrémentale. La wildcard "Action": "*" ou "Resource": "*" est un anti-pattern absolu en production.
# MAUVAIS : politique trop permissive (souvent vue en audit)
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
# BON : politique S3 lecture seule spécifique avec conditions
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadSpecificBucketWithConditions",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::mon-bucket-production",
"arn:aws:s3:::mon-bucket-production/*"
],
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
},
"IpAddress": {
"aws:SourceIp": [
"10.0.0.0/8",
"192.168.0.0/16"
]
}
}
}
]
}
3. Rôles IAM plutôt que clés d'accès longue durée
Les clés d'accès IAM (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) sont la principale source de compromission. Elles ne se révoquent pas automatiquement, sont souvent committées par erreur dans le code, et circulent dans des logs et des variables d'environnement. La règle : supprimer toutes les clés d'accès longue durée et les remplacer par des rôles.
- EC2 : Instance Profile attaché à l'instance — les credentials sont rotés automatiquement toutes les heures par IMDS
- Lambda : Execution Role avec permissions minimales — pas de clés dans les variables d'environnement
- ECS/Fargate : Task Role — isolé par tâche, pas par service entier
- GitHub Actions : OIDC federation → assume role → session temporaire (1 heure max, aucune clé stockée)
- Applications on-premise : IAM Roles Anywhere avec certificats X.509 émis par votre PKI
# GitHub Actions : OIDC federation avec AWS (zéro secret stocké)
# 1. Créer le provider OIDC dans AWS
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
# 2. Créer le rôle assumable par GitHub Actions
resource "aws_iam_role" "github_actions" {
name = "GitHubActionsRole"
assume_role_policy = jsonencode({
Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.github.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:mon-org/mon-repo:*"
}
}
}]
})
}
4. Permission Boundaries : délégation sûre aux équipes
Les Permission Boundaries permettent de déléguer la création de rôles IAM à des équipes de développement sans risquer d'escalade de privilèges. Elles définissent le plafond maximal des permissions qu'un administrateur délégué peut accorder, même s'il attache une politique AdministratorAccess à un rôle.
resource "aws_iam_policy" "developer_boundary" {
name = "DeveloperPermissionBoundary"
description = "Limite les permissions maximales des développeurs"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AllowedServices"
Effect = "Allow"
Action = [
"s3:*",
"lambda:*",
"dynamodb:*",
"sqs:*",
"sns:*",
"logs:*",
"xray:*"
]
Resource = "arn:aws:*:eu-west-1:123456789:*"
},
{
Sid = "DenyPrivilegedActions"
Effect = "Deny"
Action = [
"iam:*",
"organizations:*",
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"guardduty:DeleteDetector",
"config:DeleteConfigRule"
]
Resource = "*"
}
]
})
}
# Attacher la boundary lors de la création d'un rôle dev
resource "aws_iam_role" "developer_role" {
name = "DeveloperRole"
permissions_boundary = aws_iam_policy.developer_boundary.arn
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
5. AWS Organizations + SCPs : guardrails au niveau organisationnel
Les Service Control Policies (SCPs) sont des garde-fous qui s'appliquent à tous les comptes d'une OU (Organizational Unit), y compris aux rôles avec AdministratorAccess. Elles constituent la dernière ligne de défense contre les erreurs de configuration critiques.
# SCP : bloquer toutes les régions sauf l'UE (souveraineté des données)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonEURegions",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-west-1",
"eu-west-3",
"eu-central-1",
"eu-north-1"
]
},
"ArnNotLike": {
"aws:PrincipalArn": [
"arn:aws:iam::*:role/AdminBreakGlassRole"
]
}
}
}
]
}
# SCP : empêcher la désactivation des services de sécurité
{
"Sid": "DenyDisableSecurityServices",
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"guardduty:DeleteDetector",
"securityhub:DisableSecurityHub",
"config:DeleteConfigurationRecorder"
],
"Resource": "*"
}
6. AWS IAM Access Analyzer : détection automatique des accès excessifs
IAM Access Analyzer analyse en continu vos politiques et remonte deux types d'alertes : les accès publics non intentionnels (bucket S3 public, Lambda invocable par tous) et les politiques excessivement permissives. Il est gratuit et doit être activé dans chaque compte et chaque région.
# Activer Access Analyzer via Terraform
resource "aws_accessanalyzer_analyzer" "org_analyzer" {
analyzer_name = "organization-analyzer"
type = "ORGANIZATION" # Analyse l'ensemble de l'organisation
tags = {
Environment = "security"
}
}
# Générer une politique de moindre privilège depuis CloudTrail
aws accessanalyzer start-policy-generation --policy-generation-details '{
"principalArn": "arn:aws:iam::123456789:role/MyAppRole"
}' --cloud-trail-details '{
"trailArn": "arn:aws:cloudtrail:eu-west-1:123456789:trail/main-trail",
"startTime": "2025-01-01T00:00:00Z",
"endTime": "2025-06-01T00:00:00Z"
}'
# Retourne une politique IAM avec uniquement les actions réellement utilisées
7. AWS CloudTrail : journalisation complète et alertes temps réel
CloudTrail enregistre toutes les appels API AWS. Un trail multi-région avec chiffrement S3 et alertes EventBridge est le minimum requis pour tout environnement de production.
# EventBridge rule : alerte sur les activités suspectes
resource "aws_cloudwatch_event_rule" "root_activity" {
name = "detect-root-activity"
description = "Alerte sur toute utilisation du compte root"
event_pattern = jsonencode({
detail-type = ["AWS API Call via CloudTrail"]
detail = {
userIdentity = {
type = ["Root"]
}
}
})
}
resource "aws_cloudwatch_event_rule" "console_no_mfa" {
name = "detect-console-login-without-mfa"
description = "Connexion console sans MFA"
event_pattern = jsonencode({
detail-type = ["AWS Console Sign In via CloudTrail"]
detail = {
additionalEventData = {
MFAUsed = ["No"]
}
}
})
}
# Alerte SNS pour les deux règles
resource "aws_cloudwatch_event_target" "security_alert" {
rule = aws_cloudwatch_event_rule.root_activity.name
arn = aws_sns_topic.security_alerts.arn
}
8. AWS IAM Identity Center (SSO) : accès humain fédéré
Pour les accès humains à la console AWS, IAM Identity Center (anciennement AWS SSO) est le standard. Il permet de fédérer votre annuaire d'entreprise (Active Directory, Okta, Google Workspace) avec AWS, de centraliser les permissions, et d'imposer des durées de session courtes.
- Fédération SAML 2.0 : vos utilisateurs se connectent avec leurs credentials d'entreprise — pas de mots de passe AWS séparés
- Durées de session courtes : configurez 1 à 4 heures maximum pour les accès production (pas 8h comme par défaut)
- Permission sets : définissez des profils d'accès réutilisables (ReadOnly, Developer, Admin) et assignez-les par compte et par groupe
- Audit centralisé : toutes les connexions sont loguées dans CloudTrail avec l'identité de l'utilisateur fédéré
9. Exemple Terraform : rôle Lambda avec politique de moindre privilège
# Rôle d'exécution Lambda avec permissions minimales
resource "aws_iam_role" "lambda_processor" {
name = "lambda-order-processor-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy" "lambda_processor_policy" {
name = "order-processor-policy"
role = aws_iam_role.lambda_processor.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "ReadFromOrdersQueue"
Effect = "Allow"
Action = [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
]
Resource = aws_sqs_queue.orders.arn
},
{
Sid = "WriteToOrdersTable"
Effect = "Allow"
Action = [
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:GetItem"
]
Resource = aws_dynamodb_table.orders.arn
},
{
Sid = "CloudWatchLogs"
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "arn:aws:logs:eu-west-1:*:log-group:/aws/lambda/order-processor:*"
}
]
})
}
10. AWS Security Hub : score de conformité continu
Security Hub agrège les findings de GuardDuty, Inspector, IAM Access Analyzer et Config pour produire un score de conformité sur les benchmarks CIS AWS Foundations et AWS Foundational Security Best Practices. Visez un score CIS supérieur à 90 %.
# Activer Security Hub avec les standards de conformité
resource "aws_securityhub_account" "main" {}
resource "aws_securityhub_standards_subscription" "cis" {
depends_on = [aws_securityhub_account.main]
standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.4.0"
}
resource "aws_securityhub_standards_subscription" "aws_best_practices" {
depends_on = [aws_securityhub_account.main]
standards_arn = "arn:aws:securityhub:eu-west-1::standards/aws-foundational-security-best-practices/v/1.0.0"
}
# Vérifier le score depuis la CLI
aws securityhub get-insights --insight-arns "arn:aws:securityhub:::insight/aws/securityhub/0"
Conclusion
La sécurité IAM n'est pas un projet qu'on termine — c'est une hygiène quotidienne. Les actions à priorité maximale : activer MFA sur tous les comptes humains, supprimer toutes les clés d'accès longue durée au profit des rôles, déployer des SCPs pour bloquer les régions non autorisées, et activer IAM Access Analyzer (gratuit) pour détecter les accès excessifs. Ces quatre actions seules éliminent la grande majorité des vecteurs d'attaque IAM documentés. Le reste — Permission Boundaries, Security Hub, CloudTrail alerting — constitue la défense en profondeur qui fait la différence entre un incident contenu et une compromission totale de votre organisation AWS.
