今年もアドベントカレンダーの季節がやってきました。
しばらく寝不足の日々が続くことでしょう。
実際今は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から。
はい。
簡単な構成ですね。
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の環境構築も行いますが、本記事では長くなってしまいますのでAWSの環境構築に留めAzureの環境構築は別記事で掲載します。
AWSもAzureも自身のお財布を考慮して安いリージョンに作成します。
AWS環境構築
はい。
ここからTerraformです。
コードが並びます。
Terraformの説明は偉大な先達たちの記事がたくさんあるので参考にしてください。
My SQLの権限のあるアカウント作成などはコード化できておりませんので、その辺りは手動で操作します。
まずは変数設定から。
# ---------------------------
# 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
# ---------------------------
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
# ---------------------------
# 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
# ---------------------------
# 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
# ---------------------------
# 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
# ---------------------------
# 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
# ---------------------------
# ---------------------------
# 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
# ---------------------------
# 作成した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の前に何が作成されるのか一覧を聞いてきてる内容はちゃんと確認してください。
平日なんで始業に間に合うか不安になってきました。
とか言うてる間に環境ができました!
最後にNAT GatewayのElastic IP Addressを出力しています。
これは後程確認しますので、画面はこのまま置いておきます。
はい。
ちゃんとムンバイリージョンにec2が2台できていますね。
Terraformは素晴らしいですね!
ちゃんとec2-app01にはパブリックIPアドレスが付与されていて、ec2-db01にはElastic IP Addressが付与されていませんね。
計画通りです。
ではもう一度、
Terraformは素晴らしいですね!
Terraformで構築した環境の動作確認
コードが多いせいかめっちゃ長い記事になっちゃいましたし、始業まで2時間くらいなんでちゃっちゃと進めましょう。
まずはapp01のssmから。
はい。
ちゃんと[接続]ボタンが輝いて(enableになって)いますね!
ここでもう一度、
Terraformは素晴らしいですね!
当たり前にログインできますね。
とりあえず
curl httpbin.org/ip
を叩いてこのec2がどのグローバルIPアドレス(Elastic IP Address)でインターネットに接続しているか確認します。
はい。
3.110.214.185
ですね。
インドのグローバルIPアドレスなんで、なんか物珍しい感じがしますね。
先ほどのAWSコンソール画面のapp01のパブリックIPアドレスの値と完全合致です。
素晴らしいですね。
続いてdb01ですが
同じようにcurlコマンドで自身のElastic IP Addressを確認すると
3.6.167.164
です。
ここで先ほど出しっぱなしにしたコマンドプロンプト画面のoutputの値と見比べてみると
nat_gateway_global_ips = [
"3.6.167.164",
]
となっており、こちらも完全合致、つまりdb01はNAT Gatewayからインターネット接続していることが確認できます。
伏線が回収できました。
完璧ですね。
ではヒアドキュメントの通りapp01がapache入ってるのかというと
こちらも完璧ですね。
httpsじゃなくてhttpでアクセスしてみてくださいね。
脆弱性のある環境の実装
はい。
ここからが本番ですね。
いやーめんどくさい。
それでもterraformのおかげでかなり作業は簡便になっています。
AWSの課金も気になるので、さっさと進めます。
Webサーバ(app01)の設定
app01のapacheの機能でwebとして公開されている領域、defaultだと
/var/www/html
ですね。
ここに以下のファイルを配置してください。
配置とか偉そうに言いながら私はnanoでコピペしてます。
今回ssm経由で設定するので、
sudo nano /var/www/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というファイルを作成しつつ開き、以下をコピーしてください。
<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
を叩いてください。
結果が
と、こんな感じで返ってきます。
この赤枠で囲ったところがMy SQLのデフォルトのパスワードです。
この辺もuser_dataのヒアドキュメントでawkとか駆使して自動化できるんでしょうが、面倒なのと私のプログラミング力では時間がかかっちゃうのでやってないです。
中途半端ですみません。
このパスワードをメモしておいて、以下のコマンドをdb01で叩きます。
mysql_secure_installation
はい。
さっきの赤枠内のrootパスワード聞いてきましたね。
rootパワードはメモするまでもなく同じ画面上に表示されてますね。
このパスワードを入力して、新しいパスワードに変更しましょう。
変更後のパスワードは忘れないようにしてください。
パスワード変更後もやいのやいの色々聞いてきます。
My SQLがデフォルトで持ってるDBやらテーブルやら削除しますか?anonymousログイン止めときましょか?的なことなので、基本的に全部yで良いですね。
変更後のパスワードが弱いとそこも聞いてきますので、細かくは読者の皆様の環境に合わせて決めてください。
最後に
All done!
と出てくればOKです。
まだ作業は続きます。
朝ごはん食べる時間あるかな。
続いて以下のコマンドをdb01で実行してMySQLにログインします。
mysql -h 127.0.0.1 -u root -p
はい。
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;
全部の行の下に
Query OK
と出ているのを確認します。
最後に今回用に作成したDB内のテーブルのデータ一覧が表示されます。
uid | passwd | |
---|---|---|
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の構文もこのままコピペで動きます。
結果として以下の画面のように
出力されていれば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!だったのが、以下の画面に変化しています。
来た来た、って感じですね。
はい。
さっきDBに作ったテストデータのIDとパスワード、実際の値は先ほども記載しましたが
uid | passwd | |
---|---|---|
user01 | password01 | email01@test01.com |
user02 | password02 | email02@test01.co.jp |
user03 | password03 | email03@test01.us |
こちらの中からどの組み合わせでも良いのでuidの値をユーザIDのテキストボックスに、passwdの値をパスワードのテキストボックスに入力してログインボタンを押すと
はい。
ログインできました。
ここまでは正常系。
SQLインジェクション確認
ではSQLインジェクションの試験です。
ログイン画面に戻って、パスワード入力欄に
'OR 'A' = 'A
ウェーイ!!!
DBの中身が全部見えました!
めちゃくちゃ情報漏洩してますね!
SQLインジェクション成功です!
現在8時14分。
朝ごはん食べてきます。
本日はここまで。