4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

株式会社船井総研デジタルAdvent Calendar 2023

Day 4

Amazon InspectorとMicrosoft Defender for Cloudの比較①脆弱な環境準備AWS編

Last updated at Posted at 2023-12-03

今年もアドベントカレンダーの季節がやってきました。
しばらく寝不足の日々が続くことでしょう。
実際今は12月1日の朝2時です。

今回の記事はゴリゴリとコードが出てきます。
私はただのインフラエンジニアなのでコードのことは全く分かりません。
もちろんコード部分の記載の責任は私にありますが、監修してくれた@fsd_maclalaさん、ありがとうございました!

Amazon InspectorとMicrosoft Defender for Cloudの比較を思い付いた背景

昔々、それはまだMicrosoft Defender for CloudがAzure Security Centerと言われていた頃、Azure Security Centerを使うとVMの脆弱性見つけてくれるというのを見たことがあり、たぶんVMに入れてるエージェントとSecurity Centerが持ってる脆弱性情報と突合してくれるんやろなと思ってたんですが、果たしてどこまでの脆弱性みてくれるの?と疑問に思い、ずっと試してみたいと思っていたので今回良い機会なので試してみようと思いました。
比較するのもまぁ面白いかなと思ってAWS Inspectorと機能比較して書いてみます。

試してみるにあたっての問題点

こういうセキュリティ系のサービス、製品って、そもそもセキュリティ面で問題のある環境を準備することから始めないといけないのでまぁまぁ面倒なんですよね。
なのでまずはこの環境準備から入ります。

脆弱性のある環境の準備

なんぼ手順化したとしても面倒なことに変わりはないです。
しかもAWSとAzureを比較するので、同じ環境を2つ準備しないといけません。
めんどいですよね。
そこで今回はこの手間を極力最小化するため今年私が新たに身に付けた技術、TerraformでAWSとAzureに同じ環境を構築します。
Terraform、めっちゃ便利です。
大事なことなのでもう一度言いますね。
Terraform、めっちゃ便利です。

脆弱な環境

OSが古いとか、WebアプリをホストしているVMの場合はWebアプリのコードが甘くてSQLインジェクション受けてしまうとか、色んな脆弱性があると思います。
OSが古くてパッチが当たらないという脆弱性はAWSやAzure上で即座に再現することは難しいです。
なぜならサポート切れのOSはデフォルトではデプロイできなくなっており、わざわざイメージを持ち込む必要があります。
この手間をかけて実施するには面倒すぎるので、今回はSQLインジェクションできてしまう簡単なWebアプリLAMP(Linux Apache MySQL PHP)環境で構築してAWS InspectorとMicrosoft Defender for Cloudで検査します。

本記事と全く関係ないのですが、ついさっきメインで使っているPCのデータドライブがお亡くなりになり、戸惑いと怒りが私の中に広がっております。
どちらかというと怒りの方が強いので、この怒りをエネルギー源に本記事を頑張って書いていこうと思います。
データそのものはOne Driveに全て保存しているので無事でした。
One Drive万歳!

脆弱性のある環境の構成図

はい。
いつもの通り構成図描いていきます。
いつもPowerPointで描いているんですが、社内から「PowerPointイケてなくない?」という厳しいご指摘を頂いており、代わりにVisual StudioのDrawioという便利なアプリケーションを教えてもらいましたのでこれで描いていきます。

まずはAWSから。
構成図_AWS.png
はい。
簡単な構成ですね。
NAT GatewayとInternet Gatewayは運用次第ですがデータベースサーバ、今回はMy SQLのインストールされたサーバはNAT Gateway経由でインターネット接続させます。
My SQLもApacheもOSはAmazonlinux2023で、SSMでAWSコンソールからログインします。
DBなんでインターネットに直接接続させることは良くないかなーと考えての構成ですが、そもそも脆弱性のある環境構築なんでここで気を遣って構成するのもようわかりませんが・・・
もちろんSecurity GroupでMy SQLのEC2もApacheのEC2もアクセス制限かけます。
これも脆弱性のある環境構築なんでセキュリティの観点から考えてしまうと実装の意味がわからなくなってしまいますが、世にはEconomic DDoSという攻撃もあるので自身のお財布を考慮して(今回も自前の環境なので自腹です)構成します。

Azureも条件をそろえるために同じ考え方で構成します。
構成図_Azure.png
若干手抜き感は否めないですが、こんなもんで良いでしょう。
Azureの環境構築も行いますが、本記事では長くなってしまいますのでAWSの環境構築に留めAzureの環境構築は別記事で掲載します。

AWSもAzureも自身のお財布を考慮して安いリージョンに作成します。

AWS環境構築

はい。
ここからTerraformです。
コードが並びます。
Terraformの説明は偉大な先達たちの記事がたくさんあるので参考にしてください。
My SQLの権限のあるアカウント作成などはコード化できておりませんので、その辺りは手動で操作します。

まずは変数設定から。

variables.tf
# ---------------------------
# Variables - 変数設定
# ---------------------------

# region
# ap-northeast-1 東京リージョン
# ap-south-1 ムンバイリージョン
variable "region" {
  default = "ap-south-1"
}

# 環境種別(本番:prd,ステージング:stg,開発:dev)
variable "env_type" {
  default = "dev"
}

# システム名
variable "sys_name" {
  default = "hackable"
}

# availability_zone
variable "availability_zone" {
  type = object({
    a = string
    c = string
    b = string #ムンバイリージョンで利用
  })
  default = {
    a = "ap-south-1a" # ムンバイ(インド)のアベイラビリティゾーン
    b = "ap-south-1b" # ムンバイ(インド)のアベイラビリティゾーン
    c = "ap-south-1c" # ムンバイ(インド)のアベイラビリティゾーン
  }
}

# vpc address
variable "vpc_address_pre" {
  default = "10.0."
}

variable "vpc_address_suf" {
  default = "0.0/23"
}

# private subnet suffix address
variable "private_address_suf01" {
  default = "1.0/24"
}

# public subnet suffix address01
variable "public_address_suf01" {
  default = "0.0/24"
}

# EC2のインスタンスタイプ
variable "ec2_instance_type" {
    default = "t2.micro"
}

# VPC Endpoint
variable "vpc_endpoints" {
  type    = list(any)
  default = ["ssm", "ssmmessages", "ec2messages"]
}

続いてterraformのバージョンやプロバイダバージョンを指定するmain.tfです。

main.tf
# ---------------------------
# main
# ---------------------------

terraform {
  required_version = ">= 1.4" # Terraformのバージョンを1.4以上に指定
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "= 5.22.0" # AWSプロバイダのバージョンを5.22.0に固定
    }
  }
}

# プロバイダー設定
provider "aws" {
  region = var.region
}

ここはまぁこんなもんですかね。
続いてVPC設定。

vpc.tf
# ---------------------------
# VPC
# ---------------------------

# VPC
resource "aws_vpc" "vpc" {
  cidr_block           = "${var.vpc_address_pre}${var.vpc_address_suf}"
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = "${var.env_type}-${var.sys_name}-vpc"
  }
}

# ---------------------------
# Subnet
# ---------------------------

resource "aws_subnet" "sn_private_1a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "${var.vpc_address_pre}${var.private_address_suf01}"
  availability_zone = var.availability_zone.a

  tags = {
    Name = "${var.env_type}-${var.sys_name}-sn-private-1a"
  }
}

resource "aws_subnet" "sn_public_1a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "${var.vpc_address_pre}${var.public_address_suf01}"
  availability_zone = var.availability_zone.a

  tags = {
    Name = "${var.env_type}-${var.sys_name}-sn-public-1a"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "igw_vpc_1a" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.env_type}-${var.sys_name}-igw-vpc1a"
  }
}

# EIP
resource "aws_eip" "eip_nat_1a" {
  tags = {
    Name = "${var.env_type}-${var.sys_name}-eip-nat1a"
  }
}

# Nat Gateway
resource "aws_nat_gateway" "nat_1a" {
  allocation_id = aws_eip.eip_nat_1a.id
  subnet_id     = aws_subnet.sn_public_1a.id
  tags = {
    Name = "${var.env_type}-${var.sys_name}-nat-1a"
  }
}

# ---------------------------
# Route table
# ---------------------------
# Route table作成
resource "aws_route_table" "rt_sn_public_1a" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw_vpc_1a.id
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-rt-sn-public-1a"
  }
}

resource "aws_route_table" "rt_sn_private_1a" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat_1a.id
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-rt-sn-private-1a"
  }
}

# SubnetとRoute tableの関連付け
resource "aws_route_table_association" "associate_rt_sn_public_1a" {
  subnet_id      = aws_subnet.sn_public_1a.id
  route_table_id = aws_route_table.rt_sn_public_1a.id
}

resource "aws_route_table_association" "associate_rt_sn_private_1a" {
  subnet_id      = aws_subnet.sn_private_1a.id
  route_table_id = aws_route_table.rt_sn_private_1a.id
}

まぁここも特に言及するところは無いですかね。
何かご質問があればお手数ですがコメントください。

つづいてec2の構成を司るec2.tfです。

ec2.tf
# ---------------------------
# EC2
# ---------------------------

# Amazon Linux 2023 の最新版AMIを取得
data "aws_ssm_parameter" "al2023-ami-kernel-default-x86_64" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}

# ---------------------------
# 必要台数のEC2を作成
# ---------------------------

 resource "aws_instance" "ec2_app01" {
   ami                         = data.aws_ssm_parameter.al2023-ami-kernel-default-x86_64.value
   instance_type               = var.ec2_instance_type
   availability_zone           = var.availability_zone.a
   vpc_security_group_ids      = [aws_security_group.sg_ec2_app01.id]
   subnet_id                   = aws_subnet.sn_public_1a.id
   private_ip                  = "${var.vpc_address_pre}0.11"
   associate_public_ip_address = "true" #Elastic IP Addressを付与する
   iam_instance_profile        = aws_iam_instance_profile.instance_prof.name
   user_data                   = <<-EOF
                                 #!/bin/bash
                                 sudo hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-app01
                                 sudo dnf update -y
                                 sudo dnf install -y httpd php php-mysqli
                                 sudo systemctl start httpd
                                 sudo systemctl enable httpd
                                 sudo usermod -a -G apache ec2-user
                                 sudo usermod -a -G apache ssm-user
                                 sudo chown -R ec2-user:apache /var/www
                                 sudo chown -R ssm-user:apache /var/www
                                 sudo chmod 2775 /var/www
                                 find /var/www -type d -exec sudo chmod 2775 {} \;
                                 find /var/www -type f -exec sudo chmod 0664 {} \;
                                 echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
                                 EOF

   root_block_device {
     volume_size = 30   # GB
     volume_type = "gp3" # 汎用SSD
     encrypted   = false
     tags        = {
       Snapshot = "false"      
     }
   }
   tags = {
     Name = "${var.env_type}-${var.sys_name}-ec2-app01"
   }
 }

resource "aws_instance" "ec2_db01" {
  ami                         = data.aws_ssm_parameter.al2023-ami-kernel-default-x86_64.value
  instance_type               = var.ec2_instance_type
  availability_zone           = var.availability_zone.a
  vpc_security_group_ids      = [aws_security_group.sg_ec2_db01.id]
  subnet_id                   = aws_subnet.sn_private_1a.id
  private_ip                  = "${var.vpc_address_pre}1.11"
  associate_public_ip_address = "false" #Elastic IP Addressを付与しない
  iam_instance_profile        = aws_iam_instance_profile.instance_prof.name
  user_data                   = <<-EOF
                                #!/bin/bash
                                sudo hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-db01
                                sudo dnf update -y
                                sudo dnf -y localinstall  https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm
                                sudo dnf install -y php mysql-server php-mysqlnd
                                sudo systemctl start mysqld
                                sudo systemctl enable mysqld
                                EOF

  root_block_device {
    volume_size = 30   # GB
    volume_type = "gp3" # 汎用SSD
    encrypted   = false
    tags        = {
      Snapshot = "false"      
    }
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-ec2-db01"
  }
}

ここでのポイントは

apacheをインストールするWeb用のec2であるec2-app01にはElastic IP Addressを付与している

My SQLをインストールするDB用のec2であるec2-db01にはElastic IP Addressを付与していない

ec2-app01はpublic subnetにec2-db01はprivate subnetに構築している

それぞれのec2に必要なミドルウェアはuser_dataのヒアドキュメントでインストールしている

です。
note info使ったら表示がやかましくなっちゃいましたね。
まぁいいでしょう。
2台のec2をこのtfファイルで構築しています。

続いてec2でssmを利用する際にec2に付与するIAMの設定を行うiam.tfです。

iam.tf
# ---------------------------
# IAM
# ---------------------------

# IAM ROLE 定義
resource "aws_iam_role" "ec2_role" {
  name               = "${var.env_type}-${var.sys_name}-iam-role"
  assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
}

# IAMポリシー定義
data "aws_iam_policy_document" "ec2_assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

# IAM ROLEのインスタンスプロフィールの作成
resource "aws_iam_instance_profile" "instance_prof" {
  name = "${var.env_type}-${var.sys_name}-instance-profile-ssm"
  role = aws_iam_role.ec2_role.name
}

# ---------------------------
# FOR SSM
# ---------------------------
# SSM用ポリシーをEC2ロールに設定
resource "aws_iam_role_policy_attachment" "ssm_control" {
  role       = aws_iam_role.ec2_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

ここも特に言及するところは無いですね。
ssmを利用する際に必要なiamを定義しているだけです。
続いてssmを利用する際に必要なvpc endpointの設定です。

vpc_endpoint.tf
# ---------------------------
# VPC Endpoint
# ---------------------------

# SSM用 VPC endpointの作成
resource "aws_vpc_endpoint" "interface" {
  for_each            = toset(var.vpc_endpoints)
  vpc_id              = aws_vpc.vpc.id
  service_name        = "com.amazonaws.${var.region}.${each.value}"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = [aws_subnet.sn_private_1a.id]
  private_dns_enabled = true
  security_group_ids  = [aws_security_group.sg_ssm.id]
  tags                = { "Name" = "${var.env_type}-${var.sys_name}-vpc-endpoint-${each.value}" }
}

ここも特にポイントは無いですね。
敢えて言うならforeach使ってvarriable.tf内のList読み込んでるくらいですかね。

ひょえー
もう6時だ。
途中でPCのディスクが死ななければこんなに時間がかからずに済んだのに。

続いてsecurity group用のtfファイルです。
vpc用のtfファイルにマージしても良いのですが、私は分けて管理しています。

security_group.tf
# ---------------------------
# Security Group
# ---------------------------

# ---------------------------
# EC2用 Security Group作成
# ---------------------------
resource "aws_security_group" "sg_ec2_app01" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2-app01"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2-app01"
  }

  # インバウンドルール

  # For SSM
  ingress {
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # For httpアクセス
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    cidr_blocks = ["ご自分のグローバルIPアドレス"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ec2_db01" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2-db01"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2-db01"
  }

  # インバウンドルール

  # For SSM
  ingress {
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # For EC2-app01 MySQL
  ingress {
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.sg_ec2_app01.id]
  }

  # For EC2-app01 ICMPv4
  ingress {
    from_port       = -1
    to_port         = -1
    protocol        = "icmp"
    security_groups = [aws_security_group.sg_ec2_app01.id]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# ---------------------------
# SSM用 Vpc endpoint Security Group作成
# ---------------------------

resource "aws_security_group" "sg_ssm" {
  name   = "${var.env_type}-${var.sys_name}-sg-ssm"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ssm"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ssmmessages" {
  name   = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ec2messages" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2messages"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2messages"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

ここでのポイントは、app01のec2用のSecurity Groupのインバウンドルールにhttp、つまりtcp80番ポートでご自身のグローバルIPアドレスを指定するくらいです。
これが無いと脆弱性のあるLAMP環境のテストができませんね。
httpsじゃなくてhttpなのもAWS Inspectorで引っかかるかなという期待を込めています。
httpsでも自己証明書だとAWS Inspectorで引っかかるとかだと中々嬉しいですけどね。
今回はまずhttpで試します。

続いてterraform実行後に出力するoutputの指定を行うoutput.tfです。

output.tf
# ---------------------------
# Output
# ---------------------------

# 作成したNAT GatewayのパブリックIPアドレスを出力
output "nat_gateway_global_ips" {
   value = aws_nat_gateway.nat_1a.*.public_ip
 }

はい。
これで動きます。
ここはNAT GatewayのElastic IP Addressを表示させます。
なんでNAT GatewayのElastic IP Addressなの?って感じかもしれませんが伏線なので後程回収します。

これらのファイルをお手元のPC内、任意のディレクトリに配置してください。
続いてterraformのコマンドです。
これは簡単ですね。
作業端末はWindows 11 Proを想定していますので、コマンドプロンプトでこれらのファイルを配置した任意のディレクトリにcdコマンドとかで移動してもらい、

terraform init

を実行後

terraform validate

を実行して、結果が

Success! The configuration is valid.

であれば次に

terraform plan

を実行して問題なければ

terraform apply

を実行し、

enter a value

と聞いてきたら

yes

と打ち込みましょう!

enter a valueの前に何が作成されるのか一覧を聞いてきてる内容はちゃんと確認してください。

平日なんで始業に間に合うか不安になってきました。
とか言うてる間に環境ができました!

image.png

最後にNAT GatewayのElastic IP Addressを出力しています。
これは後程確認しますので、画面はこのまま置いておきます。

AWS コンソールを確認してみましょう。
image.png

はい。
ちゃんとムンバイリージョンにec2が2台できていますね。
Terraformは素晴らしいですね!

ちゃんとec2-app01にはパブリックIPアドレスが付与されていて、ec2-db01にはElastic IP Addressが付与されていませんね。
計画通りです。
ではもう一度、
Terraformは素晴らしいですね!

Terraformで構築した環境の動作確認

コードが多いせいかめっちゃ長い記事になっちゃいましたし、始業まで2時間くらいなんでちゃっちゃと進めましょう。

まずはapp01のssmから。
image.png
はい。
ちゃんと[接続]ボタンが輝いて(enableになって)いますね!
ここでもう一度、
Terraformは素晴らしいですね!
image.png
当たり前にログインできますね。
とりあえず

curl httpbin.org/ip

を叩いてこのec2がどのグローバルIPアドレス(Elastic IP Address)でインターネットに接続しているか確認します。
image.png
はい。
3.110.214.185
ですね。
インドのグローバルIPアドレスなんで、なんか物珍しい感じがしますね。
先ほどのAWSコンソール画面のapp01のパブリックIPアドレスの値と完全合致です。
素晴らしいですね。
続いてdb01ですが
image.png
同じようにcurlコマンドで自身のElastic IP Addressを確認すると
3.6.167.164
です。
ここで先ほど出しっぱなしにしたコマンドプロンプト画面のoutputの値と見比べてみると

nat_gateway_global_ips = [
  "3.6.167.164",
]

となっており、こちらも完全合致、つまりdb01はNAT Gatewayからインターネット接続していることが確認できます。
伏線が回収できました。
完璧ですね。

ではヒアドキュメントの通りapp01がapache入ってるのかというと
image.png
こちらも完璧ですね。
httpsじゃなくてhttpでアクセスしてみてくださいね。

脆弱性のある環境の実装

はい。
ここからが本番ですね。
いやーめんどくさい。
それでもterraformのおかげでかなり作業は簡便になっています。
AWSの課金も気になるので、さっさと進めます。

Webサーバ(app01)の設定

app01のapacheの機能でwebとして公開されている領域、defaultだと
/var/www/html
ですね。
ここに以下のファイルを配置してください。
配置とか偉そうに言いながら私はnanoでコピペしてます。
今回ssm経由で設定するので、

sudo nano /var/www/html/index.html

です。
index.htmlというファイルを作成しつつ開いたら、以下をコピーして貼り付けてください。

index.html
<html>
  <meta charset="UTF-8">
  <head>
    <title>ログイン画面</title>
  </head>
  <body>
    <form action="db.php" method="post">
      <table>
        <tr>
          <td>ユーザID</td>
          <td><input type="text" name="uid"></td>
        </tr>
        <tr>
          <td>パスワード</td>
          <td><input type="text" name="password"></td>
        </tr>
      </table>
      <input type="submit" value="ログイン">
    </form>
  </body>
</html>

この配置もterraformのヒアドキュメントでやれった話なんですけどね。
あんまりec2.tfが長くなりすぎるのも良くないですし、またローカルPC環境からterraformでアップロードして配置ってのもできるんですが、あんまりごちゃごちゃすると障害発生時に切り戻しが面倒になるのでこうしています。
この辺どうするかは読者の皆様にお任せします。

続いて同じ領域に以下のファイルを配置します。

sudo nano /var/www/html/db.php

でdb.phpというファイルを作成しつつ開き、以下をコピーしてください。

db.php
<html>
  <meta charset="UTF-8">
  <head>
    <title>ログイン後の画面</title>
  </head>
  <body>
<?php

$db_host = "10.0.1.11"; /*DBサーバのIPアドレスを入力する*/
$db_user = "db_user"; /*権限のあるDBサーバ内のユーザ名を入力する*/
$db_password = "P@ssw0rd0123"; /*上述のユーザー名のパスワードを入力する*/
$db_name = "system"; /*アクセスしたいDB名を入力する*/

$conn = mysqli_connect($db_host, $db_user, $db_password, $db_name);

$uid = $_POST['uid']; /*ログイン画面のユーザーIDの項目で入力された値を格納する変数*/
$pass = $_POST['password']; /*ログイン画面のパスワードの項目で入力された値を格納する変数*/

$result = mysqli_query($conn, "SELECT email FROM users where uid='$uid' AND passwd='$pass'");

if (mysqli_num_rows($result) == 0 ) { /*ログインID、パスワードの組み合わせが間違っていた場合の処理*/
  echo "ユーザ名または、パスワードに誤りがあります。";
  echo $result;
  exit;
}

while ($row = mysqli_fetch_assoc($result)) { /*ログインに成功した後の処理*/
  print('ようこそ!');
  print('<br>');
  print('あなたの電子メールアドレス:'.$row['email']);
  print('<br>');
}

mysqli_close($conn);
?>
  <a href="index.html">ログイン画面に戻る</a>
  </body>
</html>

はい。
できました。
10.0.1.11の部分は実際のDBサーバのローカルIPアドレスを記載してもらえれば良いんですが、今回はTerraformでこのIPアドレスを指定しているのでTerraformをコピペしてるならこのdb.phpもコピペで動きます。

これでWebサーバ側app01はおしまいです。
続いてDBサーバ側db01の設定です。
時間が無くなってきたな。

DBサーバ(db01)の設定

続いてdb01の設定です。
db01のssmで

sudo cat /var/log/mysqld.log

を叩いてください。
結果が
image.png
と、こんな感じで返ってきます。
image.png
この赤枠で囲ったところがMy SQLのデフォルトのパスワードです。
この辺もuser_dataのヒアドキュメントでawkとか駆使して自動化できるんでしょうが、面倒なのと私のプログラミング力では時間がかかっちゃうのでやってないです。
中途半端ですみません。

このパスワードをメモしておいて、以下のコマンドをdb01で叩きます。

mysql_secure_installation

image.png
はい。
さっきの赤枠内のrootパスワード聞いてきましたね。
rootパワードはメモするまでもなく同じ画面上に表示されてますね。
このパスワードを入力して、新しいパスワードに変更しましょう。
変更後のパスワードは忘れないようにしてください。
パスワード変更後もやいのやいの色々聞いてきます。
My SQLがデフォルトで持ってるDBやらテーブルやら削除しますか?anonymousログイン止めときましょか?的なことなので、基本的に全部yで良いですね。
変更後のパスワードが弱いとそこも聞いてきますので、細かくは読者の皆様の環境に合わせて決めてください。
image.png
最後に

All done!

と出てくればOKです。

まだ作業は続きます。
朝ごはん食べる時間あるかな。

続いて以下のコマンドをdb01で実行してMySQLにログインします。

mysql -h 127.0.0.1 -u root -p

image.png
はい。
My SQLにログインしました。
これからDBとテーブル作成、テストデータ入力を行います。
My SQLにログインした状態で以下のコマンドを入力してください。

CREATE DATABASE `system`;

CREATE TABLE `system`.`users` (`uid` VARCHAR(20) unique NOT NULL , `passwd` VARCHAR(20) NOT NULL , `email` VARCHAR(256) unique NOT NULL ) ENGINE = InnoDB;

INSERT INTO `system`.`users` (`uid`, `passwd`, `email`) VALUES ('user01', 'password01', 'email01@test01.com');

INSERT INTO `system`.`users` (`uid`, `passwd`, `email`) VALUES ('user02', 'password02', 'email02@test02.co.jp');

INSERT INTO `system`.`users` (`uid`, `passwd`, `email`) VALUES ('user03', 'password03', 'email03@test03.us');

SELECT * FROM system.users;

image.png

全部の行の下に

Query OK

と出ているのを確認します。
最後に今回用に作成したDB内のテーブルのデータ一覧が表示されます。

uid passwd email
user01 password01 email01@test01.com
user02 password02 email02@test01.co.jp
user03 password03 email03@test01.us

と出力されていればOKです。
続いてMy SQLにログインした状態で以下のコマンドを入力してください。
app01からMy SQLにログインするユーザを作成します。

create user 'db_user'@'10.0.0.11' identified by 'P@ssw0rd0123';
grant all on *.* to 'db_user'@'10.0.0.11';
select user, host from mysql.user;

10.0.0.11の部分はapp01のローカルIPアドレスを入力してください。
今回はterraformで10.0.0.11をプライベートIPアドレスとして指定しているので、terrafomのファイルをコピペしてるならこのMySQLの構文もこのままコピペで動きます。
結果として以下の画面のように
image.png
出力されていればOKです。
クエリで作成したユーザ確認のため最後のクエリでユーザ一覧を出力しているので、

user host
db_user 10.0.0.11
mysql.infoschema localhost
mysql.session localhost
mysql.sys localhost
root localhost

このように出力されていればOKです。
これで環境の準備は完了です。

LAMP環境にSQLインジェクションできるか確認

ちゃんとSQLインジェクションができてしまうWebアプリができているか確認します。

正常系確認

app01にhttpでアクセスすると、index.htmlを配置したので、初回アクセス時はIt works!だったのが、以下の画面に変化しています。
image.png
来た来た、って感じですね。
image.png
はい。
さっきDBに作ったテストデータのIDとパスワード、実際の値は先ほども記載しましたが

uid passwd email
user01 password01 email01@test01.com
user02 password02 email02@test01.co.jp
user03 password03 email03@test01.us

こちらの中からどの組み合わせでも良いのでuidの値をユーザIDのテキストボックスに、passwdの値をパスワードのテキストボックスに入力してログインボタンを押すと
image.png
はい。
ログインできました。
ここまでは正常系。

SQLインジェクション確認

ではSQLインジェクションの試験です。

ログイン画面に戻って、パスワード入力欄に

'OR 'A' = 'A

と入力します。
image.png
ログインボタンを押すと・・・
image.png

ウェーイ!!!

DBの中身が全部見えました!
めちゃくちゃ情報漏洩してますね!
SQLインジェクション成功です!

現在8時14分。
朝ごはん食べてきます。

本日はここまで。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?