Cloud One Application Security(Preview)でECS-Fargate上のWebアプリケーションのSQLインジェクションを検知してみた
こんにちはコカコーラ大好きカジです。
今日はサーバーレスやDockerコンテナのWebアプリケーション保護するCloud One Application Security(Preview)をECS-Fargate上で試してみました。 ECS-Fargateでのセキュリティ対策についてお問い合わせ頂くことがあるため、注目している製品となります。
Cloud One Application Securityとは?
トレンドマイクロのアプリケーション組み込み型のセキュリティ対策で、今まで直接保護ができなかったサーバーレス環境のAWS Lambdaやマネージドのコンテナ環境のECS-Fargateに使えるセキュリティ対策となります。
上記引用元の図はトレンドマイクロサイトの図
サーバーレス環境のやマネージドのコンテナ環境でのアプリケーションにおけるセキュリティ脅威は、OWASP Serverless Top 10や、OWASP API Security Projectに記載されています。
Cloud One Application Securityは、アプリケーション組込型のセキュリティ対策のため、対象言語やバージョンの確認が必要です。
テストする前にTrend Micro Cloud One™ Application Security Documentationをご確認ください。
動作イメージの図は以下となります。
上記の図は動画トレンドマイクロの「Cloud One Application Securityのご紹介」のYoutube動画の内の図となります。
前提条件
- 現在(2020/08/22時点)はPreview版であり、今後変更される予定があるため、ご注意ください。
- Cloud One トライアル版アカウント登録し、30日無償にてトライアルで簡単にテストしています。
- 動画トレンドマイクロの「Cloud One Application Securityのご紹介」のYoutube動画をECS-Fargate上で試してみました。
検証環境の構成図
私の検証環境は以下の構成でECS-Fargate上で動作させて試してみました。
テスト用Webアプリケーションについて
OWASP Juice Shop Projectを利用しています。 詳細は以下のブログで紹介しています。
OWASP Juice ShopをDockerコンテナで起動しています。詳細は以下のサイトとなります。
Cloud One トライアル版アカウント登録
30日無償にてトライアルが可能です。
https://cloudone.trendmicro.com/でアカウントを作成し、登録します。
Cloud Oneにログインできると以下の画面になります。そこからApplication Securityをクリックします。
Application Security導入前作業
ここで言うSecurity Groupは、Application Security上のSecurity Group作成となります。注:AWSのSecurity Groupとは異なります。 「Create New Group」をクリック
「Group Name」に一意の名前(今回は、OWASP-JUICE-SHOP-PROTECT)を入れてCreate Groupをクリックします。
ダッシュボードの左側ペインにグループ名が作成されるため、そこの歯車マークを押して、GROUP CREDENTIALSをメモします。
- キー:保護するアプリケーショングループに関連付けられた一意のキー
- シークレット:保護するアプリケーショングループに関連付けられた一意のシークレット。
- 詳細説明はこちら
ブロックするか検知のみにするかは以下の画面で設定します。SQLインジェクションの詳細設定をクリックして設定を追加します。
準備作業はこれだけなので、すごく簡単です。
Dockerコンテナの準備
コンテナファイルの環境設定に上記でメモしたキーとシークレットを記入します。
Dokcerfile
FROM node:13-alpine WORKDIR /usr/src/app/ RUN mkdir juice-shop WORKDIR /usr/src/app/juice-shop RUN wget https://github.com/bkimminich/juice-shop/releases/download/v10.3.2/juice-shop-10.3.2_node13_linux_x64.tgz RUN tar -xzvf juice-shop-10.3.2_node13_linux_x64.tgz -C /usr/src/app/juice-shop RUN pwd RUN ls -al WORKDIR /usr/src/app/juice-shop/juice-shop_10.3.2 RUN npm install RUN apk add --update python RUN apk add make gcc g++ RUN npm i trend_app_protect RUN sed -i -e "1i require('trend_app_protect');" app.js ENV TREND_AP_KEY xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxx(メモしたキー値を記入) ENV TREND_AP_SECRET xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx(メモしたシークレット値を記入) ENTRYPOINT ["npm", "start"]
ECRとECS-Fargateの構築
具体的なECRとFargateの構築手順は以下のページを参考にして下さい。
実際に利用したCloudFormationのテンプレートは本ページの下部に記載しています。
SQL Injectionの検知テスト
正常に起動し、ALBのDNS名へブラウザで接続すると、以下の「OWASP Juice shop」の画面が表示されます。
右上のAccountのところをクリックし、Loginをクリックしてユーザー名は、以下パスワードは任意で入力すると管理者権限でログインでき、SQLインジェクションが発生します。 (OWASP Juice shopはハードニングのトレーニング用のツールのため、本来は、SQLインジェクション部分も問題の一部となりネタバレとなります。)
ユーザ名に入力する値の詳細はこちらを参考にしています。結論は、入力値はユーザ名に以下を入力、パスワードは任意でログインとなります。
' or 1=1;--
Cloud Oneの管理画面での確認
1件目はローカルPC上でDockerコンテナを起動して試したものとなり、2件目の検知がECS-Fargate上で検知したものとなります。 どこでも試せるのが良いですね。
時間あたりの発生数表示の画面
最後に
まだ簡単な検知テストのみのため、他にも保護機能もありますので今後、試してしていきたいと思います。
今回の手順ですとnode.jsには導入しやすいのではないかと推測しておりますが、いかがでしょうか?
(私は開発者ではないため自信がないです。最初テストした際に、キーとシークレットの入力間違いで動作しない状態になり、すこしだけ試行錯誤したぐらいでした。)
気になった点としては、Deep Securityのように、エージェントの正常/異常のステータスが管理画面側で見れるわけではないため、動作しているかは検知テストを試してみないとわかりませんでした。
そのため導入する際にはアプリケーションデプロイ後に、アプリケーションの動作テストだけじゃなく、Application Securityの検知テストも一緒に実施して動作確認し運用することが必要と感じました。
構築に使用したCloudFormationのテンプレート
sample-ecr-owasp-juice-shop.yaml
AWSTemplateFormatVersion: "2010-09-09" Description: Create ECR Parameters: ProjectName: Default: owasp-juice-shop Type: String Resources: ECR: Type: AWS::ECR::Repository Properties: RepositoryName: !Sub ${ProjectName}-ecr
sample-fargate-owasp-juice-shop.yaml
AWSTemplateFormatVersion: "2010-09-09" Description: Fargate and ALB Create Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "Project Name Prefix" Parameters: - ProjectName - Label: default: "InternetALB Configuration" Parameters: - InternetALBName - TargetGroupName - Label: default: "Fargate for ECS Configuration" Parameters: - ECSClusterName - ECSTaskName - ECSTaskCPUUnit - ECSTaskMemory - ECSContainerName - ECSImageName - ECSServiceName - ECSTaskDesiredCount - Label: default: "Netowork Configuration" Parameters: - VpcId - ALBSecurityGroupId - ALBSubnetId1 - ALBSubnetId2 - ECSSecurityGroupId - ECSSubnetId1 - ECSSubnetId2 - Label: default: "Scaling Configuration" Parameters: - ServiceScaleEvaluationPeriods - ServiceCpuScaleOutThreshold - ServiceCpuScaleInThreshold - TaskMinContainerCount - TaskMaxContainerCount ParameterLabels: InternetALBName: default: "InternetALBName" TargetGroupName: default: "TargetGroupName" ECSClusterName: default: "ECSClusterName" ECSTaskName: default: "ECSTaskName" ECSTaskCPUUnit: default: "ECSTaskCPUUnit" ECSTaskMemory: default: "ECSTaskMemory" ECSContainerName: default: "ECSContainerName" ECSImageName: default: "ECSImageName" ECSServiceName: default: "ECSServiceName" ECSTaskDesiredCount: default: "ECSTaskDesiredCount" # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: ProjectName: Default: owasp-juice-shop Type: String #VPCID VpcId: Description: "VPC ID" Type: AWS::EC2::VPC::Id #ALBSecurity Group ALBSecurityGroupId: Type: AWS::EC2::SecurityGroup::Id #ALBSubnet1 ALBSubnetId1: Description: "ALB Subnet 1st" Type: AWS::EC2::Subnet::Id #ALBSubnet2 ALBSubnetId2: Description: "ALB Subnet 2st" Type: AWS::EC2::Subnet::Id #ECSSecurity Group ECSSecurityGroupId: Type: AWS::EC2::SecurityGroup::Id #ECSSubnet1 ECSSubnetId1: Description: "ECS Subnet 1st" Type: AWS::EC2::Subnet::Id #ECSSubnet2 ECSSubnetId2: Description: "ECS Subnet 2st" Type: AWS::EC2::Subnet::Id #InternetALB InternetALBName: Type: String Default: "alb" #TargetGroupName TargetGroupName: Type: String Default: "tg" #ECSClusterName ECSClusterName: Type: String Default: "cluster" #ECSTaskName ECSTaskName: Type: String Default: "task" #ECSTaskCPUUnit ECSTaskCPUUnit: AllowedValues: [256, 512, 1024, 2048, 4096] Type: String Default: "256" #ECSTaskMemory ECSTaskMemory: AllowedValues: [256, 512, 1024, 2048, 4096] Type: String Default: "512" #ECSContainerName ECSContainerName: Type: String Default: "container" #ECSImageName ECSImageName: Type: String Default: "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/owasp-juice-shop-ecr" #ECSServiceName ECSServiceName: Type: String Default: "service" #ECSTaskDesiredCount ECSTaskDesiredCount: Type: Number Default: 1 # Scaling params ServiceScaleEvaluationPeriods: Description: The number of periods over which data is compared to the specified threshold Type: Number Default: 1 MinValue: 1 ServiceCpuScaleOutThreshold: Type: Number Description: Average CPU value to trigger auto scaling out Default: 50 MinValue: 0 MaxValue: 100 ConstraintDescription: Value must be between 0 and 100 ServiceCpuScaleInThreshold: Type: Number Description: Average CPU value to trigger auto scaling in Default: 25 MinValue: 0 MaxValue: 100 ConstraintDescription: Value must be between 0 and 100 TaskMinContainerCount: Type: Number Description: Minimum number of containers to run for the service Default: 1 MinValue: 1 ConstraintDescription: Value must be at least one TaskMaxContainerCount: Type: Number Description: Maximum number of containers to run for the service when auto scaling out Default: 1 MinValue: 1 ConstraintDescription: Value must be at least one Resources: # ------------------------------------------------------------# # Target Group # ------------------------------------------------------------# TargetGroup: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" Properties: VpcId: !Ref VpcId Name: !Sub "${ProjectName}-${TargetGroupName}" Protocol: HTTP Port: 80 TargetType: ip # ------------------------------------------------------------# # Internet ALB # ------------------------------------------------------------# InternetALB: Type: "AWS::ElasticLoadBalancingV2::LoadBalancer" Properties: Name: !Sub "${ProjectName}-${InternetALBName}" Tags: - Key: Name Value: !Sub "${ProjectName}-${InternetALBName}" Scheme: "internet-facing" LoadBalancerAttributes: - Key: "deletion_protection.enabled" Value: false - Key: "idle_timeout.timeout_seconds" Value: 60 - Key: "access_logs.s3.enabled" Value: true - Key: "access_logs.s3.bucket" Value: !Sub "alb-log-${AWS::AccountId}" SecurityGroups: - !Ref ALBSecurityGroupId Subnets: - !Ref ALBSubnetId1 - !Ref ALBSubnetId2 ALBListener: Type: "AWS::ElasticLoadBalancingV2::Listener" Properties: DefaultActions: - TargetGroupArn: !Ref TargetGroup Type: forward LoadBalancerArn: !Ref InternetALB Port: 80 Protocol: HTTP # ------------------------------------------------------------# # ECS Cluster # ------------------------------------------------------------# ECSCluster: Type: "AWS::ECS::Cluster" Properties: ClusterName: !Sub "${ProjectName}-${ECSClusterName}" # ------------------------------------------------------------# # ECS LogGroup # ------------------------------------------------------------# ECSLogGroup: Type: "AWS::Logs::LogGroup" Properties: LogGroupName: !Sub "/ecs/logs/${ProjectName}-ecs-group" # ------------------------------------------------------------# # ECS Task Execution Role # ------------------------------------------------------------# ECSTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${ProjectName}-ECSTaskExecutionRolePolicy" Path: / AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: ecs-tasks.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy # ------------------------------------------------------------# # ECS TaskDefinition # ------------------------------------------------------------# ECSTaskDefinition: Type: "AWS::ECS::TaskDefinition" Properties: Cpu: !Ref ECSTaskCPUUnit ExecutionRoleArn: !Ref ECSTaskExecutionRole Family: !Sub "${ProjectName}-${ECSTaskName}" Memory: !Ref ECSTaskMemory NetworkMode: awsvpc RequiresCompatibilities: - FARGATE #ContainerDefinitions ContainerDefinitions: - Name: !Sub "${ProjectName}-${ECSContainerName}" Image: !Ref ECSImageName LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref ECSLogGroup awslogs-region: !Ref "AWS::Region" awslogs-stream-prefix: !Ref ProjectName MemoryReservation: 128 PortMappings: - HostPort: 3000 Protocol: tcp ContainerPort: 3000 # ------------------------------------------------------------# # ECS Service # ------------------------------------------------------------# ECSService: Type: AWS::ECS::Service DependsOn: ALBListener Properties: Cluster: !Ref ECSCluster DesiredCount: !Ref ECSTaskDesiredCount LaunchType: FARGATE LoadBalancers: - TargetGroupArn: !Ref TargetGroup ContainerPort: 3000 ContainerName: !Sub "${ProjectName}-${ECSContainerName}" NetworkConfiguration: AwsvpcConfiguration: AssignPublicIp: ENABLED SecurityGroups: - !Ref ECSSecurityGroupId Subnets: - !Ref ECSSubnetId1 - !Ref ECSSubnetId2 ServiceName: !Sub "${ProjectName}-${ECSServiceName}" TaskDefinition: !Ref ECSTaskDefinition PlatformVersion: 1.4.0 # ------------------------------------------------------------# # Auto Scaling Service # ------------------------------------------------------------# ServiceAutoScalingRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: application-autoscaling.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: !Sub "${ProjectName}-${ECSContainerName}-autoscaling" PolicyDocument: Statement: - Effect: Allow Action: - application-autoscaling:* - cloudwatch:DescribeAlarms - cloudwatch:PutMetricAlarm - ecs:DescribeServices - ecs:UpdateService Resource: "*" ServiceScalingTarget: Type: AWS::ApplicationAutoScaling::ScalableTarget Properties: MinCapacity: !Ref TaskMinContainerCount MaxCapacity: !Ref TaskMaxContainerCount ResourceId: !Sub - service/${EcsClusterName}/${EcsDefaultServiceName} - EcsClusterName: !Ref ECSCluster EcsDefaultServiceName: !Sub "${ProjectName}-${ECSServiceName}" RoleARN: !GetAtt ServiceAutoScalingRole.Arn ScalableDimension: ecs:service:DesiredCount ServiceNamespace: ecs DependsOn: - ECSService - ServiceAutoScalingRole ServiceScaleOutPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy Properties: PolicyName: !Sub "${ProjectName}-${ECSServiceName}-ScaleOutPolicy" PolicyType: StepScaling ScalingTargetId: !Ref ServiceScalingTarget StepScalingPolicyConfiguration: AdjustmentType: ChangeInCapacity Cooldown: 60 MetricAggregationType: Average StepAdjustments: - ScalingAdjustment: 1 MetricIntervalLowerBound: 0 DependsOn: ServiceScalingTarget ServiceScaleInPolicy: Type: AWS::ApplicationAutoScaling::ScalingPolicy Properties: PolicyName: !Sub "${ProjectName}-${ECSServiceName}-ScaleInPolicy" PolicyType: StepScaling ScalingTargetId: !Ref ServiceScalingTarget StepScalingPolicyConfiguration: AdjustmentType: ChangeInCapacity Cooldown: 60 MetricAggregationType: Average StepAdjustments: - ScalingAdjustment: -1 MetricIntervalUpperBound: 0 DependsOn: ServiceScalingTarget ServiceScaleOutAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "${ProjectName}-${ECSServiceName}-ScaleOutAlarm" EvaluationPeriods: !Ref ServiceScaleEvaluationPeriods Statistic: Average TreatMissingData: notBreaching Threshold: !Ref ServiceCpuScaleOutThreshold AlarmDescription: Alarm to add capacity if CPU is high Period: 60 AlarmActions: - !Ref ServiceScaleOutPolicy Namespace: AWS/ECS Dimensions: - Name: ClusterName Value: !Ref ECSCluster - Name: ServiceName Value: !Sub "${ProjectName}-${ECSServiceName}" ComparisonOperator: GreaterThanThreshold MetricName: CPUUtilization DependsOn: - ECSService - ServiceScaleOutPolicy ServiceScaleInAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: !Sub "${ProjectName}-${ECSServiceName}-ScaleInAlarm" EvaluationPeriods: !Ref ServiceScaleEvaluationPeriods Statistic: Average TreatMissingData: notBreaching Threshold: !Ref ServiceCpuScaleInThreshold AlarmDescription: Alarm to reduce capacity if container CPU is low Period: 300 AlarmActions: - !Ref ServiceScaleInPolicy Namespace: AWS/ECS Dimensions: - Name: ClusterName Value: !Ref ECSCluster - Name: ServiceName Value: !Sub "${ProjectName}-${ECSServiceName}" ComparisonOperator: LessThanThreshold MetricName: CPUUtilization DependsOn: - ECSService - ServiceScaleInPolicy