APIの利活用は業界によっては当たり前のように、別のところではまだこれから、というところもあります。
これからAPI開発を行うに際し、気をつけたいセキュリティについて記載します。
APIセキュリティはまだ馴染みが薄いと思いますが、OWASPではAPIに特化したOWASP API TOP10というものを出しています。こちらを参照くださいー>https://owasp.org/www-project-api-security/
APIの脆弱性って、要するにプログラムに外部からアクセスし動作不良なってしまう欠陥があるようなセキュリティホールのことか?というと、必ずしもそうではありません。設計の不備や設定の不備と言ったらい良いでしょうか?具体的にどうのようなものかをデモ用プログラムを使い説明したいと思います。
デモ用プログラムはOWASPにて紹介されているcrAPIを使います。参照先:https://owasp.org/www-project-crapi/
ソースコードはこちらになりますー>https://github.com/OWASP/crAPI
最近はコンテナイメージでの提供が増えたこともあり導入手順は簡単になっています。
環境はUbuntu22.04LTSを使います。
Podmanと、podman-composeをインストールします。pip3はpodman-composeをインストールするときに使用しますのでこちらもインストールする必要があります。
$sudo apt -y install podman
動作確認をします。
$ podman pull ubuntu
Resolved "ubuntu" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
Trying to pull docker.io/library/ubuntu:latest...
Getting image source signatures
Copying blob de44b265507a done
Copying config b1d9df8ab8 done
Writing manifest to image destination
Storing signatures
b1d9df8ab81559494794e522b380878cf9ba82d4c1fb67293bcf931c3aa69ae4
$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ podman run ubuntu /bin/echo "Welcome to the Podman World"
Welcome to the Podman World
コンテナのUbuntuを通して、正常に表示されました。
次はコンテナのUbuntuにアクセスします。
$ podman run -it ubuntu /bin/bash
OSを確認します。
root@902174651a7d:/# uname -a
Linux 902174651a7d 6.8.0-49-generic #49~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Nov 6 17:42:15 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
コンテナから抜けます。
root@902174651a7d:/# exit
exit
$
これでPodmanは正常に動作していることが確認できました。
次にpodman-composerをインストールしますがその前にpip3をインストールします。
$ sudo apt install python3-pip
途中「Do you want to continue? [Y/n]」と聞いてきますので「y」を押し進みます。
エラーなく終了した場合は、podman-composeのインストールに進みます。
$ pip3 install podman-compose
Downloading podman_compose-1.2.0-py2.py3-none-any.whl (39 kB)
Requirement already satisfied: pyyaml in /usr/lib/python3/dist-packages (from podman-compose) (5.4.1)
Collecting python-dotenv
Downloading python_dotenv-1.0.1-py3-none-any.whl (19 kB)
Installing collected packages: python-dotenv, podman-compose
WARNING: The script dotenv is installed in '/home/tsano/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: The script podman-compose is installed in '/home/tsano/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed podman-compose-1.2.0 python-dotenv-1.0.1
「Successfully installed podman-compose〜」が表示されていればインストールは完了です。
研修素材の「crAPI」をダウンロードします。
ブラウザでhttps://github.com/OWASP/crAPI を確認します。
Quickstart Guideを参照し、以下のように入力します。
$ curl -o docker-compose.yml https://raw.githubusercontent.com/OWASP/crAPI/main/deploy/docker/docker-compose.yml
$ podman-compose pull
$ podman-compose -f docker-compose.yml --compatibility up -d
DockerでなくPodmanを使うことから、実行前に修正する必要があります。以下の一文をファイル最後に追記します。
$ sudo vi /etc/containers/registries.conf
unqualified-search-registries=["docker.io"]
$podman-compose -f ./docker-compose.yml up -d
$ vi docker-compose.yml
postgresdb:
container_name: postgresdb
image: 'postgres:14'
postgresdb:
container_name: postgresdb
image: 'docker.io/library/postgres:14'
mongodb:
container_name: mongodb
image: 'mongo:4.4'
mongodb:
container_name: mongodb
image: 'docker.io/library/mongo:4.4'
$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c876ee409338 docker.io/library/postgres:14 postgres -c max_c... 29 seconds ago Up 29 seconds ago (healthy) postgresdb
15b93f417bab docker.io/library/mongo:4.4 mongod 28 seconds ago Up 28 seconds ago (healthy) mongodb
1338afdd1111 docker.io/crapi/mailhog:latest 27 seconds ago Up 27 seconds ago (healthy) 127.0.0.1:8025->8025/tcp mailhog
7d67c2fe6742 docker.io/crapi/gateway-service:latest /app/server 26 seconds ago Up 26 seconds ago (starting) api.mypremiumdealership.com
8b6d4291aca2 docker.io/crapi/crapi-identity:latest /entrypoint.sh 26 seconds ago Up 26 seconds ago (healthy) crapi-identity
1de5661837ca docker.io/crapi/crapi-community:latest /bin/sh -c /app/m... 25 seconds ago Up 25 seconds ago (healthy) crapi-community
40c8b17bf1e4 docker.io/crapi/crapi-workshop:latest /bin/sh -c /app/r... 24 seconds ago Up 23 seconds ago (healthy) crapi-workshop
05127e642cee docker.io/crapi/crapi-web:latest /bin/sh -c /etc/n... 22 seconds ago Up 22 seconds ago (healthy) 127.0.0.1:8888->80/tcp, 127.0.0.1:8443->443/tcp crapi-web
docker-compose.yamlファイルを修正します。
ダウンロードしたままでは、同じサーバー上でのアクセスは許容されていますが、ネットワーク越しのアクセスができません。そこで以下の2つのサーバー設定中の3箇所を修正します。
crapi-web:
container_name: crapi-web
image: crapi/crapi-web:${VERSION:-latest}
ports:
- "${LISTEN_IP:-127.0.0.1}:8888:80"
- "${LISTEN_IP:-127.0.0.1}:8443:443"
crapi-web:
container_name: crapi-web
image: crapi/crapi-web:${VERSION:-latest}
ports:
- "${LISTEN_IP:-0.0.0.0}:8888:80"
- "${LISTEN_IP:-0.0.0.0}:8443:443"
mailhog:
user: root
container_name: mailhog
image: crapi/mailhog:${VERSION:-latest}
environment:
MH_MONGO_URI: admin:crapisecretpassword@mongodb:27017
MH_STORAGE: mongodb
ports:
# - "127.0.0.1:1025:1025" # smtp server
- "${LISTEN_IP:-0.0.0.0}:8025:8025" # Mail ui
mailhog:
user: root
container_name: mailhog
image: crapi/mailhog:${VERSION:-latest}
environment:
MH_MONGO_URI: admin:crapisecretpassword@mongodb:27017
MH_STORAGE: mongodb
ports:
# - "127.0.0.1:1025:1025" # smtp server
- "${LISTEN_IP:-0.0.0.0}:8025:8025" # Mail ui
起動させます。
$ podman-compose -f ./docker-compose.yml up -d
ブラウザでアクセスします。
この画面がでればひとまず動作しています。
Dont have an Account? Sign Upをクリックします。
必要な情報を入力します。
メールアドレスは適当なもので大丈夫です。
そういえばメールサーバーの設定していないなーと思われますか?
設定する箇所はありますが、すでにローカルで動くダミーメールサーバー(Mailhog)への設定がされています。もちろん、実在するメールサーバーに設定を変更することもできます。
Signupを押すことで登録が完了します。
メールを確認する必要があります。
メールサーバーへの接続は、
http://192.168.200.150:8025/
となります。
実在しないドメインのメールでも受信します。
該当するメールを開きます。
メールを開くと以下のような文面になっています。
このうち、VINとPincodeをメモしておきます。
ログイン後の設定追加で使用します。
VIN: 4UERX64ZFXK054388
Pincode: 4813
いよいよログインします。ブラウザにて以下を使いアクセスします。
http://192.168.200.150:8888/
ログイン直後はこのような画面になります。
右上の「Add a Vehicle」をクリックします。
右上の「Add a Vehicle」をクリックします。
すると以下のような入力画面となります。
先ほどメモに控えた情報を入力します。
無事登録されると以下のようなダッシュボードが構成されます。
ここからはブラウザの開発者モードを開いておきます。
ここで確認できるのは、緯度経度の情報です。車の位置を表していると考えられます。
"vehicleLocation"{"id":3,"latitude":"37.746880","longitude":"-84.301460"},"fullName":"Test Test","email":"test01@ttt.com"}
自分はログインしているのでこの情報が取得できるのは当たり前、疑う余地はありません。
上の図ではURLを目にすることができます。
payload: Response { type: "basic", url: "http://192.168.200.150:8888/identity/api/v2/vehicle/608ff343-b47d-4e6e-84e8-244688c9a7db/location", redirected: false, … }
このURLの中には「608ff343-b47d-4e6e-84e8-244688c9a7db」というどこかで目にした文字列があります。車を登録したあたりでUUIDとして目にすることができました。
気分を変えるべく、掲示板を見てみます。
Robotさんという人の書き込みを見てみます。
開発者ツールの画面では、名前以外にビークルIDやメールアドレスが目に入ります。
ここでビークルIDを使って以下のような問い合わせをしたらどうなるか試してみます。
$ curl -H "Authorization: Bearer (省略)" http://192.168.200.150:8888/identity/api/v2/vehicle/4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5/location
{"carId":"4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5","vehicleLocation":{"id":3,"latitude":"37.746880","longitude":"-84.301460"},"fullName":"Robot","email":"robot001@example.com"}
応答を見ると”latitude","longitude"より緯度経度の情報が、"email"よりメールアドレスとわかります。つまり、Robotさんのメールアドレス、車の所在地を知ることができました。
個人情報は自身だけが見えれば良いのですが他人の情報も見えてしまいました。車の所在地が漏れると何が悪いか?そこにいけば写真に掲載されている車があるので、夜な夜な現地にいって盗難、というリスクが高まります。メールアドレスはウイルス付き、不正サイト誘導などの害のあるSPAMの送信先に加えられてしまいます。
もし、ここれがクレジットカード番号やマイナカード番号だったらどうでしょうか?
ここでOWASP API TOP10を見てみます。
オブジェクトの一部のプロパティについて認証されない不備がありますので、OWASP APIの3「Broken Object Property Level Authorization」に該当します。
よく目にする脆弱性というよりは、設計不備というべきなんでしょうね。セキュリティ全般で使う脆弱性とは全く違います。APIを使った開発では、フロントエンド側とバックエンド側の齟齬があった場合にこのような漏れが起きてしまうと考えられます。APIを使い回している場合は、設定不備がさらに拡大していきますので注意が必要です。
次回は、どのような方法で検知するかを考えています。