Blog
Absicherung von S3-Downloads mit ALB und Cognito-Authentifizierung

Früher war es schwer, einen Endpunkt zu sichern. Heutzutage, mit der Cloud, ist es ganz einfach. Sie müssen nur wissen, wie! Angenommen, Sie haben Dateien auf S3, die Sie gerne weitergeben möchten. Sie könnten das Objekt öffentlich zugänglich machen. So können Ihre Nutzer die Datei ganz einfach über ihren Browser herunterladen. Wenn Sie sie skalieren müssen, können Sie CloudFront hinzufügen. Dies würde die Inhalte näher an Ihren Nutzern zwischenspeichern und dafür sorgen, dass Ihre Nutzer die beste Leistung erhalten. Was aber, wenn Sie kontrollieren möchten, wer die Datei herunterladen kann? Hierfür benötigen Sie eine Authentifizierung und Autorisierung.
Authentifizierung vs. Autorisierung
Bei der Authentifizierung geht es darum, festzustellen, wer Sie sind. Zunächst müssen wir sicherstellen, dass wir wissen, wer der Benutzer ist. Sobald wir wissen, wer der Benutzer ist, können wir prüfen, ob der Benutzer auf den Inhalt zugreifen darf. Letzteres ist die Autorisierung. AWS bietet einen Service namens Cognito, mit dem Sie einen Pool von Benutzern verwalten können. Diese Benutzer können aus anderen Quellen wie Google, Facebook und Ihrem eigenen Identitätsanbieter stammen. Wenn Sie keine Identitätsanbieter verwenden möchten, können Sie Benutzer auch direkt im Benutzerpool anlegen.
Sie können auch Gruppen erstellen und auf der Grundlage dieser Gruppen die Berechtigungen verwalten. Sie könnten zum Beispiel eine Gruppe namens Entwickler erstellen. Alle Benutzer in dieser Gruppe sollten berechtigt sein, den auf S3 gehosteten Build-Bericht abzurufen.
Erstellen des Endpunkts
Mit dem Cognito User Pool benötigen wir eine Möglichkeit, den Benutzer während der Anfrage zu validieren. Ich verwende einen Application Load Balancer, um eine Lambda-Funktion aufzurufen. Diese Funktion kann dann prüfen, ob der Benutzer auf den Bericht zugreifen kann. Die Logik ist ganz einfach: Wenn der Benutzer zur Gruppe der Entwickler gehört, kann er den Bericht lesen.
In diesem Fall können wir die native Cognito-Integration des Application Load Balancer verwenden. Dies hat folgende Auswirkungen:
Wenn der Benutzer nicht authentifiziert ist, navigieren Sie zu der von Cognito gehosteten Benutzeroberfläche. Wenn Sie Ihren Identitätsanbieter verwenden, werden Sie auf die Anmeldeseite Ihres Unternehmens weitergeleitet. Wenn Sie die Benutzer aus dem Benutzerpool hosten, wird dem Benutzer ein Anmeldeformular angezeigt. Nachdem sich der Benutzer über eine Weiterleitung angemeldet hat, ist der Benutzer nun authentifiziert. Der Load Balancer wird nun die Zielgruppe mit der Anfrage aufrufen.
Wir wollen auch prüfen, ob der Benutzer in der Entwicklergruppe ist. Wir werden eine Lambda-Funktion verwenden, um dies zu überprüfen. Der folgende Code würde den Zweck erfüllen:
import base64
import json
from typing import List
def decode(data: str) -> dict:
return json.loads(base64.b64decode(data.split('.')[1]).decode('utf-8'))
def resolve_groups(groups: str) -> List[str]:
return list(map(lambda group: group.strip(), groups[1:-1].split(',')))
def handler(event, context) -> dict:
code = 403
description = "403 Access Denied"
body = "Access Denied"
user = decode(event["headers"]["x-amzn-oidc-data"])
groups = resolve_groups(user.get('custom:groups', '[]'))
if 'developers' in groups:
code = 200
description = "200 OK"
body = f"Hi {user.get('name')}, you should be able to download the report"
return {
"statusCode": code,
"statusDescription": description,
"isBase64Encoded": False,
"headers": {"Content-Type": "json; charset=utf-8"},
"body": body,
}
Der Listener auf dem Application Load Balancer und der User Pool Client können wie folgt konfiguriert werden:
Listener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: HTTPS
Certificates:
- CertificateArn: arn:aws:acm:eu-west-1:111122223333:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
DefaultActions:
- AuthenticateCognitoConfig:
OnUnauthenticatedRequest: authenticate
Scope: openid
UserPoolArn: arn:aws:cognito-idp:eu-west-1:111122223333:userpool/eu-west-1_xXXXxxxx
UserPoolClientId: !Ref UserPoolClient
Order: 1
Type: authenticate-cognito
- Order: 2
TargetGroupArn:
Ref: LambdaTarget
Type: forward
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
AllowedOAuthFlows:
- code
AllowedOAuthFlowsUserPoolClient: true
AllowedOAuthScopes:
- profile
- phone
- email
- openid
- aws.cognito.signin.user.admin
CallbackURLs:
- https://<MyDomainName>/oauth2/idpresponse
ClientName: MyClient
GenerateSecret: true
LogoutURLs:
- https://<MyDomainName>/logout
SupportedIdentityProviders:
- COGNITO
UserPoolId: eu-west-1_xXXXxxxx
Sie müssen auch die Lambda-Funktion wie folgt einrichten:
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0
Description: Allow all outbound traffic by default
IpProtocol: "-1"
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
Description: Allow HTTPS access
FromPort: 443
IpProtocol: tcp
ToPort: 443
VpcId: !Ref VPCAsParameter
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
ManagedPolicyArns:
- !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: my-bucket
S3Key: path/to/code.zip
Handler: index.handler
Role: !GetAtt LambdaRole.Arn
Runtime: python3.12
VpcConfig:
SecurityGroupIds:
- !GetAtt LambdaSecurityGroup.GroupId
SubnetIds:
- !Ref Subnet1
- !Ref Subnet2
- !Ref Subnet3
LambdaPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt LambdaFunction.Arn
Principal: elasticloadbalancing.amazonaws.com
LambdaTarget:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
DependsOn:
- LambdaPermissions
Properties:
TargetType: lambda
Targets:
- Id: !GetAtt LambdaFunction.Arn
Sie werden aufgefordert, sich anzumelden, wenn Sie zum Load Balancer navigieren. Danach können Sie sehen, dass die Lambda-Funktion aufgerufen wurde. Dies ist ein sehr einfaches Beispiel, aber die Idee ist, dass Sie die Lambda-Funktion mit der Logik erweitern können, die Sie benötigen. Sie könnten zum Beispiel eine vorab signierte URL für den Bericht auf S3 erstellen und den Benutzer direkt zu dieser URL weiterleiten. Dadurch wird der Bericht automatisch heruntergeladen.
Fazit
Die Sicherung des Zugriffs auf Ihre S3-Dateien muss nicht kompliziert sein. Durch den Einsatz von AWS Cognito, einem Application Load Balancer und einer einfachen Lambda-Funktion können Sie genau kontrollieren, wer auf Ihre Dateien zugreifen darf - ohne sie der Öffentlichkeit preiszugeben. Mit dieser Einrichtung wird die Authentifizierung nahtlos gehandhabt, und die Autorisierung ist so einfach wie die Überprüfung von Gruppenmitgliedschaften. Von hier aus können Sie die Funktionalität weiter ausbauen, z. B. vorsignierte URLs für Downloads generieren oder noch detailliertere Berechtigungen hinzufügen. Die Cloud macht es Ihnen leicht - Sie müssen nur wissen, wie!
Foto von Pixabay
Verfasst von

Joris Conijn
Joris is the AWS Practise CTO of the Xebia Cloud service line and has been working with the AWS cloud since 2009 and focussing on building event-driven architectures. While working with the cloud from (almost) the start, he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learning new things and experimenting with them because that is the way to learn and grow.
Unsere Ideen
Weitere Blogs
Contact



