Article original : How to Securely Deploy APIs to Amazon Lambda – A Practical Guide
Les cyberattaques contre les API (Application Programming Interfaces) sont en augmentation. Ces attaques proviennent de problèmes liés à l'authentification, à l'autorisation, à l'exposition inutile de données, au manque de limites de requêtes, à la consommation de ressources et à l'utilisation d'API tierces vulnérables.
Des failles dans les API peuvent survenir avant que les requêtes n'atteignent les API, au sein du code hébergeant les API, et même le long du chemin de communication des API avec les services en aval, les dépendances ou d'autres microservices.
Les attaquants exploitent les failles des API pour accéder à des données confidentielles, collecter ou manipuler des données, ou même rendre votre service indisponible via des attaques par déni de service distribué (DDoS).
Dans cet article, vous apprendrez à déployer vos API dans Lambda et à appliquer certaines mesures de sécurité avant la fonction, au sein de la fonction et après la fonction.
Table des matières
Qu'est-ce qu'une API ?
L'accent de cet article est mis sur la sécurité des interfaces de programmation d'applications (API). Une API est une interface qui connecte deux programmes ou applications, leur permettant d'échanger des données et de communiquer.
Une API peut être interne à une organisation ou appartenir à un tiers qui permet à d'autres utilisateurs de consommer leurs données via l'API.
Prérequis
Bien que ce tutoriel soit accessible aux débutants, vous aurez besoin des prérequis suivants pour le suivre sans encombre :
Une connaissance de base du Cloud AWS.
Un compte AWS avec un accès administrateur.
AWS CLI. Vous pouvez trouver le guide d'installation ici. Suivez les instructions pour votre système d'exploitation.
Python. Vous pouvez visiter le site de documentation officielle de Python pour savoir comment télécharger et installer Python pour votre système d'exploitation spécifique.
Pipenv ou tout outil de création d'environnement virtuel Python. Vous pouvez trouver le guide d'installation de Pipenv ici.
Une connaissance de base de Git.
Un client API, comme Postman ou Thunderclient.
Objectif du projet
À la fin de ce projet, vous devriez être capable de déployer des API dans Lambda de manière sécurisée, en tirant parti des services de sécurité natifs du cloud AWS.
Architecture globale du projet
Voici l'architecture du flux de travail du projet :

Comme le montre le diagramme architectural, lorsqu'un utilisateur envoie une requête (un objet JSON composé du nom de l'utilisateur) à une API hébergée dans Lambda, l'utilisateur est d'abord authentifié par un service d'authentification appelé Amazon Cognito.
La requête passe par un Web Application Firewall, puis par une API Gateway. API Gateway effectuera une vérification pour voir si l'utilisateur est autorisé à accéder à l'API en utilisant le jeton (token) que l'utilisateur envoie avec la requête après l'authentification. API Gateway laisse ensuite passer le trafic vers l'API si l'utilisateur est autorisé.
La requête de l'utilisateur arrivera d'abord à une fonction Lambda externe, qui enregistrera ensuite le nom de l'utilisateur sous forme de message dans un sujet (topic) Simple Notification Service (SNS). Cela invoquera ensuite une Lambda interne pour s'exécuter et enregistrer la sortie dans les journaux Amazon CloudWatch. Le sujet SNS sera accédé par la Lambda externe à l'aide de l'identifiant unique du SNS stocké dans Amazon Secrets Manager.
Configuration AWS
Vous devrez configurer un environnement AWS pour commencer. Cela nécessite la création d'un compte si vous n'en avez pas déjà un.
Après la création du compte, un utilisateur racine (root user) est automatiquement créé, avec tous les privilèges attachés. La meilleure pratique de sécurité consiste à créer un autre utilisateur avec des privilèges d'administrateur et à utiliser cet utilisateur pour les tâches suivantes.
Ensuite, créez une clé d'accès pour cet utilisateur, qui se compose généralement de deux parties (ID de clé d'accès et Clé d'accès secrète) en naviguant vers :
IAM —→ Users —→ Create Access key
Suivez les instructions et choisissez l'option Command Line Interface. Cochez la case Confirmation et procédez à la création de la clé. Téléchargez le fichier CSV fourni, ou copiez manuellement l' Access Key ID et la Secret Access Key. Enregistrez-les en toute sécurité.






Ouvrez votre terminal et exécutez les commandes suivantes à l'aide de l'AWS CLI :
aws configure
La commande ci-dessus affichera des invites pour fournir les composants de la Access Key créée précédemment et votre région par défaut (la région AWS hébergeant le service avec lequel vous avez l'intention d'interagir).
Cloner le projet
Dans l'étape suivante, vous clonerez le dépôt GitHub contenant les actifs et les ressources utilisés dans la mise en œuvre du projet.
Visitez l' URL du projet et clonez le dépôt localement.
git clone <repository_clone_url>
Configurer Simple Notification Service
Amazon Simple Notification Service (SNS) connecte les composants du système, permettant une communication et une messagerie asynchrones entre eux.
Recherchez SNS sur la console, cliquez dessus et créez un sujet (topic) auquel vos API enverront des messages. Après avoir créé avec succès un sujet, naviguez vers celui-ci, et dans les détails du sujet, vous trouverez l' ARN du sujet. Un ARN est un Amazon Resource Name, et c'est une chaîne unique attachée à une ressource que vous avez créée sur AWS pour aider à identifier la ressource. Copiez l' ARN du sujet.





Configurer Secrets Manager
Amazon Secrets Manager est utilisé pour stocker, gérer et récupérer des informations sensibles telles que des clés, des identifiants, des jetons, etc. Vous y stockerez l' Topic ARN créé précédemment. Avec cette approche, vous démontrerez comment votre API peut accéder en toute sécurité aux données et informations dont elle a besoin pour fonctionner.
Allez dans Secrets Manager sur la console AWS et créez un secret. Fournissez les détails du secret et ajoutez un nouveau secret nommé TOPIC_ARN comme clé et l'ARN réel du sujet SNS comme valeur.





Ensuite, vous créerez des fonctions Lambda pour servir vos API et consommer la sortie des API. Il y a trois fonctions Lambda à configurer. Deux des fonctions hébergeront des API, chacune ne pouvant être accédée que par des utilisateurs spécifiques. Celles-ci seront appelées ExternalLambda. La troisième Lambda consommera la sortie des fonctions Lambda externes via SNS.
Configurer la Lambda interne
AWS Lambda est un service serverless sur AWS que les utilisateurs peuvent exploiter pour exécuter des fonctions d'application ou du code en cas de besoin. Vous êtes facturé pour votre fonction Lambda en fonction du nombre d'invocations de la fonction, de la durée de chaque invocation et de la quantité de mémoire allouée à la fonction. Lambda peut être provisionné pour utiliser n'importe quel runtime, tel que Python ou NodeJS. Dans cette démonstration, nous nous concentrerons sur le runtime NodeJS.
Maintenant que vous savez ce qu'est Lambda et ce qu'il fait, vous pouvez en créer une. Appelons la première fonction Lambda InternalLambda. Sur la console AWS, recherchez Lambda, et sur le tableau de bord Lambda, cliquez sur Create a function et fournissez les détails. Nous utiliserons Node.js – JavaScript côté serveur comme runtime de choix.


Pour les détails des Permissions, laissez Lambda créer un IAM Role par défaut. Ce rôle par défaut est nommé en fonction de votre fonction, et les permissions attachées au rôle permettent à votre fonction Lambda d'envoyer des journaux à CloudWatch, un autre service AWS utilisé pour la surveillance et l'observabilité.


Comme vous pouvez le voir dans la dernière image ci-dessus, la fonction Lambda que vous avez créée a besoin d'un déclencheur (trigger) et parfois d'une destination. Pour votre InternalLambda, le déclencheur est le sujet SNS que nous avons configuré précédemment. Cette Lambda lira les messages qui lui ont été publiés, puis vous pourrez accéder au message depuis votre client ou même les journaux CloudWatch.
Pour y parvenir, cliquez sur le bouton Add trigger et fournissez les détails.



Ensuite, vous fournirez le code que vous souhaitez invoquer via Lambda. Trouvez le code dans le dépôt GitHub que vous avez cloné précédemment. Collez le code dans l'espace de code de la fonction Lambda et cliquez sur Deploy pour déployer la fonction.
secure-lambda/InternalLambda/index.js
export const handler = async (event) => {
try {
console.log('Request successfully received from SNS');
let name = event['Records'][0]['Sns']['Message'];
let response = {
statusCode: 200,
body: JSON.stringify(`Hello ${name}. Greetings from InternalLambda!`),
};
console.log('Response: ', response);
return response;
} catch (err) {
let response = {
statusCode: 500,
body: JSON.stringify('An error occurred while processing your request.'),
};
console.error('Error processing event', err);
return response;
}
};
La fonction définie dans le fichier index.js ci-dessus prend simplement l'objet event qui lui est envoyé par SNS et en extrait l'attribut Message. Nous utilisons console.log ici pour visualiser les sorties de la fonction et nous assurer qu'elle se comporte comme prévu. Ne l'utilisez simplement pas dans une application prête pour la production.

Configurer la Lambda externe
Vous allez créer deux fonctions Lambda externes : 1 et 2. Ces deux fonctions recevront les requêtes des utilisateurs, les traiteront et publieront des messages sur votre sujet SNS.
Sur la console Lambda, créez une autre fonction et nommez-la ExternalLambda1. Laissez Lambda créer un rôle IAM par défaut, comme précédemment.


Collez l'extrait de code ci-dessous dans l'espace de code ExternalLambda1 :
secure-lambda/ExternalLambda1/insex.js
import {
GetSecretValueCommand,
SecretsManagerClient,
} from "@aws-sdk/client-secrets-manager";
import { SNSClient,
PublishCommand
} from "@aws-sdk/client-sns";
const secretsManagerClient = new SecretsManagerClient();
const snsClient = new SNSClient({});
// Fetch topicArn from AWS Secrets Manager
async function getSecretValue(secretName) {
try {
const data = await secretsManagerClient.send(
new GetSecretValueCommand({
SecretId: secretName,
}),
);
if (data.SecretString) {
return JSON.parse(data.SecretString);
} else {
let buff = Buffer.from(data.SecretBinary, 'base64');
return JSON.parse(buff.toString("utf-8"));
}
} catch (err) {
console.error('Error retrieving secret', err); // added for debugging
throw err;
}
}
export const handler = async (event) => {
let name = event['name'];
console.log(`Request successfully received from ${name}`);
// Retrieve SNS Topic ARN from Secrets Manager
let topicArn;
let response;
try {
const secret = await getSecretValue('LambdaSNSTopicARN');
topicArn = secret.TOPIC_ARN;
} catch (err) {
response = {
statusCode: 500,
body: JSON.stringify('An error occured, try again later.'),
};
console.error('Failed to load SNS Topic ARN from Secrets Manager', err);
return response;
}
// Publish to SNS topic
try {
const snsResponse = await snsClient.send(
new PublishCommand({
Message: name,
TopicArn: topicArn,
})
);
console.log("Message published successfully:", snsResponse.MessageId);
response = {
statusCode: 200,
body: JSON.stringify(`Hello ${name}. Greetings from ExternalLambda1! Message forwarded to InternalLambda.`),
};
return response;
} catch (err) {
response = {
statusCode: 500,
body: JSON.stringify(`Sorry ${name}.An error occurred while processing your request.`),
};
console.error("Failed to publish message:", err);
return response;
}
};
Le code ci-dessus utilise le SDK AWS pour récupérer l'ARN du sujet SNS créé précédemment à partir de Secrets Manager. Il publie ensuite un message sur le sujet.
Le SDK est déjà installé dans la fonction Lambda. En dehors de Lambda, le SDK doit être explicitement installé. La fonction reçoit son event du client via API Gateway, que nous configurerons plus tard.
Le sujet SNS que vous avez créé précédemment sera la destination de cette fonction. Pour que Lambda puisse publier un sujet sur SNS, il a besoin de la permission nécessaire attachée à son rôle IAM. AWS peut s'en charger automatiquement lors de votre configuration, comme indiqué ci-dessous.
Pour le déclencheur, vous utiliserez un autre service connu sous le nom d' API Gateway. Plus d'informations à ce sujet plus tard.


Suivez les mêmes étapes pour provisionner une autre Lambda connue sous le nom d' ExternalLambda2.

Le résultat de la configuration de la Lambda externe est présenté ci-dessous :

Collez le code ci-dessous dans ExternalLambda2. Il remplit la même fonction qu' ExternalLambda1, mais leur sortie diffère. Chacune des deux fonctions Lambda recevra du trafic pour un utilisateur spécifique autorisé à accéder à la fonction.
secure-lambda/ExternalLambda2/index.js
import {
GetSecretValueCommand,
SecretsManagerClient,
} from "@aws-sdk/client-secrets-manager";
import { SNSClient,
PublishCommand
} from "@aws-sdk/client-sns";
const secretsManagerClient = new SecretsManagerClient();
const snsClient = new SNSClient({});
// Fetch topicArn from AWS Secrets Manager
async function getSecretValue(secretName) {
try {
const data = await secretsManagerClient.send(
new GetSecretValueCommand({
SecretId: secretName,
}),
);
if (data.SecretString) {
return JSON.parse(data.SecretString);
} else {
let buff = Buffer.from(data.SecretBinary, 'base64');
return JSON.parse(buff.toString("utf-8"));
}
} catch (err) {
console.error('Error retrieving secret', err);
throw err;
}
}
export const handler = async (event) => {
let name = event['name'];
console.log(`Request successfully received from ${name}`);
// Retrieve SNS Topic ARN from Secrets Manager
let topicArn;
let response;
try {
const secret = await getSecretValue('LambdaSNSTopicARN');
topicArn = secret.TOPIC_ARN;
} catch (err) {
response = {
statusCode: 500,
body: JSON.stringify('An error occured, try again later.'),
};
console.error('Failed to load SNS Topic ARN from Secrets Manager', err);
return response;
}
// Publish to SNS topic
try {
const snsResponse = await snsClient.send(
new PublishCommand({
Message: name,
TopicArn: topicArn,
})
);
console.log("Message published successfully:", snsResponse.MessageId);
response = {
statusCode: 200,
body: JSON.stringify(`Hello ${name}. Greetings from ExternalLambda2! Message forwarded to InternalLambda.`),
};
return response;
} catch (err) {
response = {
statusCode: 500,
body: JSON.stringify(`Sorry ${name}.An error occurred while processing your request.`),
};
console.error("Failed to publish message:", err);
return response;
}
};
Avant de continuer, vous devez modifier les rôles IAM de la Lambda externe. Actuellement, les rôles IAM n'ont que les permissions d'écrire dans CloudWatch et SNS (ajoutées automatiquement). La Lambda externe a également besoin de la permission de récupérer l'ARN du sujet SNS créé précédemment.
L'idée ici est de montrer comment exploiter un gestionnaire de secrets, tel qu'AWS Secrets Manager, pour stocker des informations ou des données sensibles, tout en y accédant de manière sécurisée. Cette approche est plus sûre que de stocker l'ARN en tant que variable d'environnement au sein de Lambda.
Naviguez vers IAM et cliquez sur l'onglet Policies à gauche. Cela vous amène à une liste de politiques. Ensuite, cliquez sur Create policy.

Recherchez secrets manager dans l'éditeur de politique.


Sélectionnez les permissions dont Lambda a besoin pour accéder à Secrets Manager. Dans ce cas, ce serait Read —> GetSecretValue.

Sélectionnez Specific pour les ressources, et cliquez sur Add ARNs. Sur l'onglet suivant, ajoutez les détails du secret Secrets Manager créé précédemment.

L'ARN du secret sera renseigné ici.

Ensuite, donnez un nom à la politique et créez-la.


Ensuite, naviguez vers Roles et recherchez les rôles IAM attribués aux fonctions Lambda externes. Ceux-ci sont nommés en fonction de la Lambda.


Cliquez sur Add permissions pour ajouter une nouvelle permission au rôle IAM sélectionné.




Configurer Web Application Firewall
Un pare-feu est un système placé devant une application, une charge de travail, des API, etc., pour inspecter le trafic, le filtrer et soit autoriser, soit bloquer le trafic en fonction de certaines règles préconfigurées.
Pour ce projet, vous utiliserez le service AWS Web Application Firewall (WAF) pour inspecter les requêtes des utilisateurs avant d'acheminer le trafic vers vos API s'exécutant dans Lambda.
Rendez-vous sur la console AWS et recherchez WAF.

Cliquez sur l'onglet IP sets à gauche. Cela vous permettra de créer une liste d'adresses IP que vous souhaitez autoriser (comme dans ce cas) ou refuser.


Les adresses IP doivent inclure un bloc CIDR. Par exemple, si vous ajoutez une seule adresse IP, elle doit être X.X.X.X/32. Il en va de même pour les plages d'adresses IP telles que X.X.X.X/24.

Ensuite, cliquez sur l'onglet Web ACLs, puis sur Create web ACL.

Choisissez Regional resources comme type de ressource, et entrez votre région. Il est préférable de conserver toutes les ressources que vous créez dans ce projet dans la même région. Donnez un nom à votre Web ACL, puis cliquez sur suivant.

Ajoutez des règles à la Web ACL.


Choisissez un type de règle. Dans ce cas, vous utiliserez IP set, et donnerez un nom à la règle. Choisissez l'IP set créé précédemment.
Sélectionnez Source IP address, et Count comme action. Pour ce projet, vous vous concentrerez sur le comptage des requêtes envoyées à vos API. Mais comme le montre l'image ci-dessous, vous pouvez effectuer d'autres actions, telles qu'autoriser, bloquer, etc.

Votre configuration de règle finale apparaîtra de cette façon.

Faites défiler vers le bas, puis cliquez sur Create web ACL.


Configurer les pools d'utilisateurs Cognito
Amazon Cognito est un service de gestion d'identité utilisé pour créer et gérer des utilisateurs. Vous pouvez l'exploiter pour authentifier et autoriser les utilisateurs à accéder à des applications, des API ou d'autres charges de travail.
Vous créerez des User Pools (pools d'utilisateurs) au sein de Cognito et ajouterez un utilisateur à chaque pool. Vous configurerez la manière dont ces utilisateurs peuvent être authentifiés et autorisés à accéder aux fonctions Lambda externes déjà créées.
Recherchez Cognito sur AWS.

Cliquez sur Get started for free, puis sur Create user pool.

Sélectionnez Single-page application (SPA), donnez au pool d'utilisateurs le nom MyUserPool1, et sélectionnez Email comme option de connexion. Cela signifie que l'attribut principal que les utilisateurs fourniront lors de l'inscription et de la connexion sera leur adresse e-mail. Laissez tout le reste par défaut. Nous garderons les choses aussi simples que possible.



Après avoir créé le pool d'utilisateurs, vous trouverez la page ci-dessous. Vous pouvez afficher la page de connexion et d'inscription pour le pool que vous venez de créer en cliquant sur le bouton View login page.

Vous pouvez ajouter des App clients à votre pool d'utilisateurs. Par défaut, un client nommé MyUserPool1 sera ajouté au pool. Naviguez vers votre pool d'utilisateurs et cliquez sur App clients pour voir les détails de ce client. Notez le Client ID. Vous apporterez également quelques modifications au client d'application en cliquant sur le bouton Edit.

Vous modifierez le champ Authentication flows en cochant les cases Sign in with username and password… et Sign in with server-side administrative credentials…. Ces modifications vous permettront d'authentifier l'utilisateur qui sera ajouté à ce client par programmation, plutôt que via une interface utilisateur. Avec cette approche, nous pouvons récupérer le jeton attribué à l'utilisateur par Cognito et utiliser ce jeton pour autoriser l'accès à Lambda.

Maintenant, ajoutez un utilisateur à ce pool. L'utilisateur a besoin d'une adresse e-mail valide. Vous aurez besoin de l'URL de la page de connexion pour créer l'utilisateur.

Vous devez avoir accès à l'e-mail utilisé pour créer l'utilisateur. Récupérez le code envoyé à l'adresse e-mail et soumettez-le pour confirmer le compte.



Suivez les mêmes étapes et créez un autre pool d'utilisateurs nommé MyUserPool2. Ajoutez un utilisateur avec un e-mail différent à ce pool.
Configurer API Gateway
API Gateway est un service utilisé pour gérer l'accès et acheminer le trafic vers les services backend d'API tels que les API. Il sert de proxy inverse et fournit une couche de sécurité supplémentaire pour les services backend.
Vous configurerez API Gateway pour diriger le trafic vers vos fonctions Lambda.
Naviguez vers API Gateway et cliquez sur Create an API.

Sélectionnez l'option REST API —→ Build.

Sélectionnez New API, fournissez un nom et choisissez Regional comme type de point de terminaison (endpoint) d'API. Le type d'adresse IP peut être IPv4 ou Dualstack. Nous sélectionnerons IPv4 ici. Puis créez.

Une partie importante de la configuration d'API Gateway pour ce projet est l'Authorizer. API Gateway utilise l'Authorizer pour autoriser le trafic des clients vers les services backend.
Vous créerez deux Authorizers. Chacun sera connecté à l'un des pools d'utilisateurs que vous avez configurés précédemment. Sur le côté gauche de l'API Gateway que vous avez configurée, cliquez sur Authorizers —→ Create authorizer.

Fournissez le nom AGAuthorizer1, et sélectionnez Cognito comme type d'Authorizer. Ajoutez le pool d'utilisateurs pour MyUserPool1 créé précédemment. Pour la source du jeton (Token source), utilisez Authorization. Lorsque vous envoyez une requête depuis votre client API, un jeton sera ajouté à l'en-tête de la requête pour l'autorisation. La clé du jeton sera nommée Authorization, tandis que la valeur sera le jeton lui-même.

Créez une autre autorisation pour MyUserPool2 de la même manière.

Les deux Authorizers apparaîtront de cette façon.

Ensuite, vous créerez des ressources et des points de terminaison au sein de l'API Gateway que vous avez définie.
Une resource dans API Gateway est utilisée pour regrouper certains points de terminaison dans un chemin spécifique. Vous définirez deux ressources au sein de l'API Gateway que vous avez créée. Cela créera deux chemins différents, / et .
Sur le tableau de bord API Gateway, naviguez vers votre Gateway, cliquez sur Create resource, définissez votre chemin racine ('/' dans votre cas), et fournissez le nom de la ressource (lambda1).

Créez une autre ressource nommée lambda2.

Maintenant, cliquez sur /lambda1, puis sur Create method pour définir un point de terminaison au sein de cette ressource. Vous utiliserez la méthode POST pour envoyer des requêtes au service backend via ce point de terminaison.

Pour le service backend ou le type d'intégration, sélectionnez Lambda function, et fournissez l'ARN d'ExternalLambda1.

Pour l'autorisation, sélectionnez AWS IAM —→ Cognito user pool authorizers —→ AGAuthorizer1. Laissez les autres configurations, puis créez le point de terminaison.

Répétez la même étape pour créer une méthode POST pour la ressource /lambda2. La method doit être attachée à ExternalLambda2 et AGAuthorizer2.

L'API Gateway que vous avez créée doit être déployée pour devenir accessible. Le déploiement se fait généralement vers une étape (Stage).
Cliquez sur Deploy API, sélectionnez New stage et nommez l'étape development. Puis, déployez.

Après le déploiement vers une étape, une URL d'invocation sera fournie. Elle servira d'URL de base pour les points de terminaison que vous avez définis.

L'étape que vous avez créée nécessite quelques modifications pour une sécurité accrue. Premièrement, vous devez attacher le WAF que vous avez créé précédemment. Deuxièmement, la limite de débit (rate limit) par défaut pour l'API déployée sur cette étape est de 10000. La limite de débit restreint la consommation excessive de ressources et protège votre API contre les abus. Pour ce projet, vous pouvez réduire la limite à 50.


Pour tester la configuration d'API Gateway, cliquez sur le point de terminaison que vous souhaitez tester, puis sur le bouton Test. Ce test initial ne nécessite aucune autorisation, car le test est effectué directement au sein de la Gateway.

Ajoutez des données JSON comme corps de la requête (Request body). La clé sera name, et la valeur sera n'importe quelle chaîne de caractères.

La réponse renvoyée par ExternalLambda1 affiche un code d'état 200 et un corps de réponse contenant exactement le message attendu de la fonction Lambda.

Si vous vous rendez dans les groupes de journaux CloudWatch (Log groups), vous devriez également trouver les groupes de journaux qui ont été automatiquement créés pour les fonctions Lambda. Cliquez sur le groupe de journaux pour ExternalLambda1 et naviguez vers le flux de journaux (Log stream) le plus récent. Vous devriez trouver les journaux de la requête que vous venez de faire depuis API Gateway.



Tester la configuration de bout en bout
Pour tester correctement notre configuration, et depuis Internet, envoyez la même requête depuis votre client API sans aucune information supplémentaire dans l'en-tête de la requête. Cela devrait renvoyer une erreur 401 – Unauthorized. C'est le comportement attendu.

API Gateway attend un jeton d'autorisation pour chaque requête qu'elle reçoit avant d'acheminer le trafic vers le service backend approprié. Elle valide ce jeton via Cognito.
Vous allez simuler une connexion utilisateur pour chaque utilisateur ajouté aux pools d'utilisateurs Cognito afin d'obtenir un jeton pour l'utilisateur. Pour y parvenir, vous utiliserez les deux scripts Python que j'ai fournis ci-dessous :
secure-lambda/auth-scripts/user1.py
import boto3
client = boto3.client("cognito-idp")
response = client.initiate_auth(
AuthFlow="USER_PASSWORD_AUTH", # or ADMIN_USER_PASSWORD_AUTH if using admin creds
AuthParameters={
"USERNAME": "", # user1 email
"PASSWORD": "" # user1 password
},
ClientId="" # Cognito App Client ID
)
id_token = response["AuthenticationResult"]["IdToken"]
access_token = response["AuthenticationResult"]["AccessToken"]
refresh_token = response["AuthenticationResult"]["RefreshToken"]
print("ID Token:", id_token)
En utilisant la bibliothèque Python boto3, vous initierez une requête d'authentification auprès de Cognito. Fournissez l'adresse e-mail et le mot de passe de l'utilisateur dans MyUserPool1. Ajoutez également l'ID client du client d'application.
Pour exécuter le script, créez un environnement isolé à l'aide de Pipenv, uv ou d'une bibliothèque similaire. Installez la dépendance utilisée dans le projet comme défini dans le Pipfile, et exécutez le script avec le shell Pipenv.
pipenv install
pipenv shell
Python secure-lambda/auth-scripts/user1.py
La commande Python renverra un jeton attribué à l'utilisateur. Ensuite, vous utilisez ce jeton pour autoriser un utilisateur à accéder à ExternalLambda1.

Assurez-vous que l'URL de la requête POST est au format : . Vous devriez recevoir une réponse d'API Gateway indiquant le succès.
Maintenant, essayez d'accéder à ExternalLambda2 en utilisant le jeton de User1. Vous devriez recevoir un message Unauthorized. Notez que user1 recevra toujours un message non autorisé lorsqu'il tente d'accéder à ExternalLambda1 sans jeton d'autorisation dans l'en-tête, avec un mauvais jeton, ou lorsqu'il tente d'accéder à ExternalLambda2, pour lequel il n'est pas autorisé.

Répétez le processus avec User2 en utilisant le jeton généré pour l'utilisateur dans MyUserPool2. Tout d'abord, testez l'accès à ExternalLambda2 sans jeton dans l'en-tête de la requête.

Puis testez l'accès avec le jeton.

Ensuite, essayez d'accéder à ExternalLambda1 en utilisant User2.

Vous pouvez également visualiser le résultat de certaines des requêtes effectuées par votre client sur les journaux CloudWatch.


De plus, puisque WAF a été configuré précédemment pour compter les requêtes (bien que, dans un scénario réel, vous souhaitiez accomplir bien plus avec WAF, comme autoriser ou bloquer certains trafics), vous pouvez visualiser les activités capturées par WAF en naviguant vers le service sur AWS, puis en recherchant le WAF que vous avez configuré, et en naviguant vers Traffic overview.


Vous pouvez trouver d'autres détails, tels que les types d'appareils clients et l'origine des requêtes.


Nettoyage
Il est important de nettoyer les ressources créées jusqu'à présent après l'exercice pratique. En raison des dépendances entre les ressources, essayer de supprimer une ressource dont dépend une autre ressource peut entraîner une erreur. Vous devez donc les supprimer dans cet ordre :
Secrets Manager
Cognito – Utilisateurs, Client d'application, puis Pool d'utilisateurs
API Gateway – Points de terminaison/ Méthodes, Ressources, API, Étape
Web Application Firewall – IP Set, Web ACL
Toutes les fonctions Lambda
Rôles IAM Lambda et les politiques qui leur sont attachées
Groupe de journaux CloudWatch pour toutes les fonctions Lambda
Sujet SNS
De plus, vous pouvez désactiver ou supprimer les identifiants créés pour votre utilisateur IAM Admin s'ils ne sont pas utilisés.
Améliorations
Considérez les domaines suivants pour améliorer, appliquer les meilleures pratiques et renforcer davantage la posture de sécurité de vos systèmes.
Utilisation de clés d'API
Consommation d'API tierces
Gestion de l'inventaire des API / documentation
Provisionnement des ressources à l'aide de l'Infrastructure as Code
Conclusion
La sécurité à chaque couche d'un système informatique n'est pas négociable. Dans ce projet, nous avons démontré comment exploiter des solutions natives du cloud pour sécuriser des API hébergées dans un service serverless, en n'autorisant l'accès aux API qu'aux utilisateurs autorisés.
Je suis Agnes Olorundare, et vous pouvez en savoir plus sur moi sur LinkedIn.