上原正吉(EarthLink Network Co., Ltd.)。AI会社・AI社員による業務自動化と、人の逐次介入なしに24時間自動開発を回す仕組みを自社で実践しています。開発ノウハウと具体的な設定方法、運用して分かった改善点を公開しています。

この記事は、連載「自分のサービスは自分で守る ── AI 時代の自衛セキュリティ」の第 6 回です。自分でできる検査は自分で、難しいときは EarthLink Network が検査を代行し、直せない弱点は私たちが修正します。
連載の目次
前回までの 2 回は、動いているサービスに外から問い合わせを送る検査と、コードを読む AI のツール(Strix)の検査を比べました。今回は、コードを読む検査そのもののやり方です。自分たちが書いたソースコードを読んで、書き方に弱点が無いかを調べます。この検査を静的解析と呼びます。
自分のサービスなら、ソースコードは手元にあります。だから、外から試すより先に、まずコードを調べるのがいちばん安くて速いやり方です。無料のツールで、全部のファイルを同じ基準で、数分で調べられます。
ただし、調べ方で、見つかる弱点が変わります。私たちは無料の Semgrep(セムグレップ)で、自社サービス promptflow のコードを調べました。
つまり、一つの調べ方で 0 件でも、安全とは限りません。
もう一つ、APIキー(サービスを使うための合言葉の文字列)やパスワードを、コードに書き残していないかも調べました。無料の Gitleaks(ギットリークス)で Git の履歴まで探すと、過去に書き込まれた AWS(アマゾンのクラウド)のアクセスキーが残っていました。そのキーはもう無効で、使えない状態でした。
この記事は、次の三つの大きな流れで進めます。
静的解析は、ソースコードのファイルを一つずつ読んで、「この書き方は危ない」という場所を探す検査です。見つかると、その場所(ファイル名と行番号)と、なぜ危ないかの説明を出してくれます。
外から試す検査と比べると、得意なことと苦手なことがはっきり分かれます。
どちらか一つで足りる、ということはありません。今回は、コードを読む側を見ていきます。
静的解析のツールは、弱点の見つけ方で、大きく 3 通りに分かれます。見つけ方が違えば、見つかる弱点も違います。
どれも無料から始められます。くわしい無料の範囲は、記事の後半にまとめました。
ここからは、promptflow のコードで試した結果です。調べたのは、この記事で扱う弱点を直す前のコードのコピーです。本番のサービスには触れていません。
Semgrep は Python の部品として配られているので、python3 -m pip install semgrep の 1 行で入ります。まず、アプリのコードのフォルダを、セキュリティ向けのルールセット(p/security-audit)で調べました。

2,016 ファイルを、約 2 分で調べ終わりました。結果は 0 件です。
次に、同じコードを JavaScript 向けのルールセット(p/javascript)で調べました。

今度は 1 件見つかりました。
見つかったのは、ログインのあとに、どの画面へ移動するかを決めている 1 行です。Semgrep の説明を平たく言うと、こうなります。「移動先を、利用者が書き換えられるかもしれない値で決めている。もし外からその値を書き換えられたら、利用者を偽のサイトへ飛ばせてしまう。その値を使う前に、確かめてほしい」。この弱点は、オープンリダイレクトと呼ばれます。偽のログイン画面へ誘い込む手口に使われます。
この 1 件のルールは、セキュリティ向けのルールセットには入っていませんでした。同じ調べ方(ルールに照らす)でも、ルールセットを替えるだけで、見つかるものが変わります。
では、この 1 行は危ないのか。コードを追うと、移動先の値は、使う前に別のファイルの関数で確かめられていました。移動先を同じサイトの中の場所(/settings のような書き方)に限り、https:// で始まる別のサイトや、// で始まる書き方は、トップページに置き換える関数です。
ところが、この確かめ方には見落としがありました。// は弾くのに、/\(スラッシュの次に逆スラッシュ)で始まる値は通していたのです。ブラウザは、URL の中の逆スラッシュを、スラッシュと同じに扱うことがあります。だから /\ は // と同じように、別のサイトへの移動として読まれることがあります。細工したリンクを踏ませてログインまでしてもらうと、ログインのあとに別のサイトへ飛ばせてしまう書き方でした。
この見落としを見つけたのは、Semgrep ではありません。この 1 行を直している途中で、CodeRabbit をコマンドから動かしてレビューさせたところ、/\ を指摘しました。今回使った Semgrep のルールセットには、この書き方が入っていませんでした。ルールに照らす調べ方と、AI が読む調べ方では、見つけるものが違う、という例です。この指摘を受けて、/\ で始まる値もトップページに置き換えるように直しました。
どうやってコードを追ったかの細かい手順は、記事の最後の補足に載せました。
最初のルールセットの 0 件は、「安全」という意味ではありませんでした。0 件が意味するのは、「このルールセットのルールに合う書き方は無かった」ということだけです。理由は三つあります。
だから、調べ方を替えたり、重ねたりします。ルールセットを足す。値の流れを追うツールを使う。AI のレビューを入れる。一つで 0 件でも、別の調べ方で出てくることがあります。
静的解析には、もう一つ仕事があります。APIキーやパスワードなどの秘密を、コードにそのまま書き残していないかを探すことです。
これは、ここまでの話とは危なさの種類が違います。コードの書き方の弱点は、今動いているサイトが外から破られるかどうかの話でした。秘密の書き残しは、サイトの応答には出てきません。外から何万回問い合わせても見つかりませんし、それだけで外から破られることもありません。
困るのは、リポジトリ(コードと変更の履歴をまとめて置いておく場所)そのものが、人の手に渡ったときです。たとえば、GitHub のアカウントや、開発者のパソコンが破られたときです。そのとき、コードに書き残したキーがあれば、そのキーでクラウドを使われたり、データを抜かれたりします。
しかも、Git(コードの変更を一つずつ記録する仕組み)は、過去のすべての変更を残しています。キーを書き込んだあと、次の変更でファイルから消しても、消す前の記録は残ります。消したつもりでも、リポジトリを手に入れた人は、過去の記録から取り出せます。だから、今のファイルだけでなく、Git の履歴まで探します。
Gitleaks は GitHub の配布ページから、自分のパソコンに合った実行ファイルを取ってくるだけで使えます。promptflow のリポジトリで、Git の履歴をすべて調べました。

過去の変更 7,633 件を、1 分 26 秒で調べ終わりました。見つかったのは 2 件です。どちらも同じファイルの、隣り合った 2 行でした。1 件目は AWS のアクセスキーの ID の形、2 件目はそれと組になる秘密の文字列の形をしていました。
コマンドに付けた --redact は、必ず付けてください。付けないと、見つかったキーの値が、画面と結果のファイルにそのまま書き出されます。結果のファイルを誰かに渡した時点で、キーを二か所目に漏らすことになります。
ルールに照らすツールは、「念のため」で指してきます。指されたものが全部危ないわけではありません。だから、一件ずつ誤検知かどうかを確かめます。
見る場所は、指された行と、その値の出どころです。値がどこから来て、使う前に確かめているかを見ます。
| 値の出どころを追った結果 | 誤検知かどうか | 次にすること |
|---|---|---|
| 値は外からは来ない(プログラムの中で決めた値だけ) | 誤検知 | 理由を書いて、誤検知の印を付ける |
| 外から来るが、使う前に確かめている | 誤検知(ツールには、確かめている処理が見えていない) | 確かめている場所を書いて、誤検知の印を付ける |
| 確かめているが、確かめ方が足りない | 弱点(軽い) | 確かめ方を直す |
| 外から来て、どこでも確かめていない | 弱点 | 優先して直す |
今回の Semgrep の 1 件は、最初は表の 2 行目に見えましたが、追っていくと 3 行目でした。ツールの結果だけでは、どの行に当たるかは分かりません。分かるのは、人か AI がコードを追ったときです。ここが、ツールに任せきれないところです。
秘密の書き残しなら、見るのは三つです。説明書の見本の値ではないか。テスト用に作った値ではないか。今も使えるか。
Gitleaks は値を伏せて出すので、指された変更の中身を手元で表示して確かめました(値は、この記事にも作業の記録にも書き出していません)。
EXAMPLE で終わるもの)でもありませんでした。手元で試すための小さなプログラムに、キーが直接書き込まれていました。見つかったときに、最初に確かめるのは「そのキーが、今も使えるか」です。キーの止め方と、Git の履歴からちゃんと消すやり方は、第 10 回(直し方)で扱います。
誤検知だと分かったら、印を付けて、次の検査から出ないようにします。そうしないと、毎回同じものが出続けて、新しく出た弱点が埋もれます。
// nosemgrep: <ルールの名前> と書き、その横に誤検知だと考えた理由を書きます。gitleaks:allow と書くか、見つかったものの指紋(見つかったもの一つずつに付く、識別のための文字列)を .gitleaksignore というファイルに書いて外します。大事なのは、印と一緒に理由を書くことです。理由の無い印は、弱点をごまかすのにも使えてしまいます。外す範囲も、一行ずつ狭くします。テスト用のファイルを丸ごと外すような広い外し方をすると、そこにうっかり書いたキーも見逃すからです。
一度調べて 0 件でも、それは「その日の、その調べ方で 0 件」というだけです。
だから、静的解析は一度で終わらせず、仕組みにして繰り返します。私たちは、同じ検査を次の 3 つのときに実行しています。
人が毎朝何かをしているわけではありません。一度設定すれば、機械が毎日調べます。結果は、第 9 回で扱う「結果を集める場所」に送り、毎日の実行の画面は第 12 回でお見せします。
今回のツールは、すべて無料から始められます。料金は、各社の公式の料金ページで確かめました。
| ツール | 調べ方 | 無料で使える範囲 |
|---|---|---|
| Semgrep(Community Edition) | ルールに照らす | 制限なし(オープンソース) |
| Semgrep(無料プラン) | ルールに照らす・値の流れを追う | リポジトリ 10 個・10 人まで |
| SonarQube(Community Build) | ルールに照らす | 自分のサーバーで動かせば制限なし。値の流れを追う検出は有料版から |
| SonarQube Cloud(Free プラン) | ルールに照らす(値の流れを追う検出は Team 以上) | 非公開のコードは 5 万行まで |
| GitHub CodeQL | 値の流れを追う | 公開しているリポジトリだけ。非公開は GitHub の Team 以上のプランに加えて、1 人あたり月 30 ドル |
| CodeRabbit | AI が読む | 公開しているリポジトリは、プルリクエストのレビューまで無料。非公開は、プルリクエストの要約と、エディタ・コマンドからのレビュー(1 人あたり 1 時間に 3 回まで) |
| Gitleaks | 秘密の書き残しを探す | 制限なし(オープンソース) |
私たちのリポジトリは非公開なので、CodeRabbit はプルリクエストのレビューまで使える契約にしています。CodeQL と、GitHub の秘密の書き残しの検査(非公開は 1 人あたり月 19 ドル)は使っていません。代わりに、無料の Semgrep と Gitleaks を毎日実行しています。くわしい料金の比べ方は、第 3 回に書きました。
次回、第 7 回は、使っている部品の弱点を検査します。自分たちが書いたコードは、サービス全体のごく一部です。残りの大半は、外から取ってきた部品(ライブラリ)でできています。その部品に知られた弱点が残っていないかを調べる検査を、同じようにコマンドと画面で進めます。件数がいちばん多く出るのは、この検査です。
指された 53 行目と、その値がどこから来るかを並べました。

result.returnTo(ログインのあとの移動先)へ画面を移しています。Semgrep が指したのはここです。resolveSilentSsoCallbackError)が作っています。sanitizeReturnTo という関数に通しています。この関数は、移動先を同じサイトの中の場所(/settings のような書き方)に限り、https:// で始まる値や // で始まる値をトップページ(/)に置き換えます。確かめる処理が別のファイルにあるので、ファイルをまたいで追わない無料版の Semgrep には、この処理が見えません。
ただし、557 行目の照合が止めるのは、外から state をすり替えることだけです。移動先は、ログインの入口の画面でも決められます。
/sso)は、URL に付けた returnTo の値を、ログイン後の移動先として受け取ります。sanitizeReturnTo を通ります。/\ で始まる値は、ここを素通りします。/\ で始まる移動先へ画面が移ります。この最後の画面の移動は、Semgrep が指した 53 行目ではなく、同じファイルの 33 行目で起きます。Semgrep が指したのは、別のサイトへ移動しうる 2 か所のうち、1 か所だけでした。
semgrep scan --config <ルールセット>:どのルールセットで調べるかを指定します。p/security-audit や p/javascript は、Semgrep の公開の置き場(レジストリ)から自動で取ってきます。--metrics=off:使い方の統計を、Semgrep の会社に送らない指定です。--json --output <ファイル名>:結果を、あとで機械で扱える形式(JSON)でファイルに保存します。時間切れの警告のように、画面に出ない情報もここに残ります。gitleaks git .:今いるリポジトリの、Git の履歴を全部調べます。今のファイルだけを調べるときは gitleaks dir を使います。--redact:見つかった値そのものを、画面にも結果のファイルにも出さず、REDACTED と伏せます。この連載では、攻撃ツールが誰でも無料で手に入る時代に、自分のサービスを自分で検査し、弱いところを先に見つけて直す手順を、順に示します。検査の対象は自社サービスに限り、他人のサイトを攻撃する手順は扱いません。 自分で検査するのが難しい方は、EarthLink Network が検査を代行します。弱点は見つかったが自分では直せない、という方は、私たちが修正まで引き受けます。お気軽にご相談ください。 役に立ったと感じたら、ぜひ「いいね」と記事の購読をお願いいたします。
自社プロダクトは https://www.eln.ne.jp/products にまとめています。
上原正吉。EarthLink Network Co., Ltd. 代表。AI社員に日々の業務を任せ、開発では人が逐一指示しなくても作業が進むHOOTL(Human Out Of The Loop)の仕組みを自社で実践しています。このブログでは、業務自動化と24時間自動開発のノウハウ、具体的な設定方法、日々の運用で見つかった問題と改善を共有します。
また、AIで業務や開発を組み替えたい会社・チーム向けに、AI活用のコンサルティングも受け付けています。ご相談は www.eln.ne.jp からどうぞ。
EarthLink Network は、会社の全業務を AI で回すために、必要になったものを自社で作っています。いま作っているプロダクトの一覧と概要は、こちらにまとめています。
→ EarthLink Network が自社でつくっている18のプロダクト
会社と各プロダクトの詳細は、公式サイト www.eln.ne.jp をご覧ください。