GitHub Copilot のコードセキュリティ: React の XSS
2023年10月19日
0 分で読めます人工知能 (AI) と大規模言語モデル (LLM) の進化の時代において、GitHub の Copilot のような革新的なツールがソフトウェア開発を変革しています。私が公開した過去の記事では、この変革の影響と、インテリジェントに自動化されたこれらのツールがもたらす利便性、およびコーディングプラクティスで堅牢なセキュリティを維持するための新たな課題について説明しました。また、Snyk では AI によるコーディングに関するセキュリティリスクのケーススタディも公開しています。
この記事では、React コードベースで GitHub Copilot を使用する場合のセキュリティ要素と、React コンポーネントの JSX ファイルでフロントエンド開発者向けにコードが自動補完される場所について説明します。その目的は、GitHub Copilot によって提案されたコードがセキュアコーディングの原則に準拠しているかどうかを検証し、特に React 開発のコンテキストにおいて、開発者が潜在的な XSS の脆弱性を回避できるコードを作成できるよう支援することです。
XSS の重大な影響についてご存じない方々のために説明すると、XSS は基本的に、攻撃者が他のユーザーによって表示される Web ページにクライアントサイドスクリプトを注入することを可能にします。
開発者が GitHub Copilot を採用
AI はさまざまな分野で変革的な影響を及ぼしていますが、中でも注目すべきなのがソフトウェア開発です。この分野で新たに登場したイノベーションの 1 つとして、GitHub Copilot が挙げられます。これは、OpenAI の Codex を搭載した、IDE (VS Code など) 内にバンドルされている開発者ツールです。
GitHub Copilot は、AI ペアプログラマーとして機能するよう設計されています。この機能は、コーディングの提案や、コードの行全体またはブロックの記述が可能で、複数の言語と互換性があり、React などの一般的なフロントエンドライブラリおよびフレームワークに拡張できます。
Copilot をツールとして活用する開発者が増える中で、Copilot が提案するコードの構文を信頼できるだけでなく、そのコードが定められた基準を満たし、ユーザーに危害を及ぼさないための十分な安全性を備えていることを確認する必要もあります。
この記事の目的は、JavaScript コードの例と、JSX および React 固有のセキュリティプラクティスの詳細な説明によって、開発者に情報を提供するだけでなく、アプリケーションセキュリティを損わずに、開発者が AI によって強化されたこれらのツールを責任を持って評価および活用できるようにすることです。
危険領域: React の dangerouslySetInnerHTML 関数の使用
React は、開発者が HTML を React コンポーネントから直接設定できるようにする API (`dangerouslySetInnerHTML` 関数) を提供しています。
この関数の使用は、その名が示すように危険が伴います。この関数により、XSS からの保護に役立つ出力エンコーディングを実行する React のセキュリティ管理をバイパスし、コンポーネントに HTML を手動で挿入できます。この機能は、信頼できるソースからのリッチテキストコンテンツを処理する場合など、特定のケースでは有用です。以下はその例です。
1function MyComponent() {
2 return <div dangerouslySetInnerHTML={{__html: '<h1>Hello World</h1>'}} />;
3}
これによって迅速かつ直接的に HTML コンテンツを処理できますが、アプリケーションが XSS 攻撃を受けるリスクが高まります。`dangerouslySetInnerHTML` によって設定される HTML コンテンツにユーザーが提供したデータが含まれる場合、攻撃者が注入した任意のスクリプトが実行され、その影響が広範囲に及ぶ可能性があります。
1function MyComponent({userInput}) {
2 // This can expose the application to XSS risks if userInput contains a malicious script.
3 return <div dangerouslySetInnerHTML={{__html: userInput}} />;
4}
そのため、アプリケーションで `dangerouslySetInnerHTML` を使用する場合には注意が必要です。
GitHub Copilot が提案するコードの安全性
`dangerouslySetInnerHTML` で発生する XSS を緩和するために、上記のセキュリティ管理の一部を導入する開発者にとって、GitHub Copilot は役に立つでしょうか。
`dangerouslySetInnerHTML` ディレクティブを使用する次の React コンポーネントコードについて考えてみましょう。
1 <Row className="justify-content-between">
2 <Col md="6">
3 <Row className="justify-content-between align-items-center">
4 <div
5 dangerouslySetInnerHTML={{
6 __html: `
7 <img src=${database.authorScreenshotURL}
8 alt=${
9 authorScreenshotDescription
10 } />
11 `,
12 }}
13 />
14 </Row>
このコンポーネントに流れてくる変数 `authorScreenshotDescription` はユーザーによって制御され、画像のテキスト説明を指定するために使用されます。
攻撃者は、この XSS の脆弱性を攻撃し、`authorScreenshotDescription` 変数の値を `\"
責任ある開発者は、以下の事項を知っておく必要があります。
`dangerouslySetInnerHTML` は、名前が示すとおり危険であるため、基本的に使用するべきではありません。しかし、特定のユースケースではこれを使用する必要があります。
この React API を使用する必要がある場合は、HTML 要素を作成できるようにする `<` などの危険な文字をエスケープするための安全な出力エンコーディングを実装する必要があります。
次は何をすべきでしょうか。この React コンポーネントに対する簡単な XSS エスケープ関数の作成を開始します。もちろん、そのために GitHub Copilot を利用できます。
関数名とその引数の入力を開始します。関数本文のコーディングを開始しようとすると、GitHub Copilot で以下のコードが自動提案されます。問題なさそうに見えます。必要な処理は TAB キーを押すだけです。当然ながら、このセキュリティエスケープ関数を利用するために、コンポーネントにおける `dangerouslySetInnerHTML` API の使用を忘れずに更新しましょう。
過去に攻撃者が成功した XSS 攻撃を試したところ、それが失敗することがわかりました。
GitHub Copilot が提案した `escapeCrossSiteScripting` 関数のコード補完はうまく機能したようで、以前に新しい `<img />` HTML 要素を作成して JavaScript コードを実行した山括弧がエスケープされました。
まだ危機は去っていません。
攻撃者は執拗であり、攻撃ペイロードを自動化することなど朝飯前です。そのため、正しい文字列の組み合わせを見つけるために、何千もの組み合わせの反復処理 (一般的にはファジングと呼ばれます) を実行する可能性があります。ここで、コンピューターハッキングの強みが発揮されます。開発者はあらゆる単一障害点から保護を行う必要がありますが、攻撃者はどんなに小さくても侵入経路を 1 つ見つけるだけでよいのです。
そのため、攻撃者は次のペイロードを試す可能性があります。
1s \"<img src=x onLoad=alert(1)
上では、`onError` 属性ハンドラーを `onLoad` ハンドラーに変更しました。ここで、この Web ページを再度読み込むと、この攻撃が成功し、ポップアップが表示されます。
開発者であれば、このセキュリティ脆弱性がどのようにして発生し、またそれをどのように回避できるのかを考えるはずです。経験豊富な React の開発者は、属性の値を引用符で囲むのがコードの安全性の観点からはるかに優れたコーディング規則であると教えてくれるかもしれません。そこで、その助言に従い、次のように `alt=` 属性値を変更してみます。
1 <div
2 dangerouslySetInnerHTML={{
3 __html: `
4 <img src=${database.authorScreenshotURL}
5 alt="${escapeCrossSiteScripting(
6 authorScreenshotDescription
7 )}" />
8 `,
9 }}
10 />
この変更後に、以前に成功した攻撃ペイロード `s \"
これで問題なさそうですね。
ただしそれも...
攻撃者が `onLoad` 特殊属性の悪用を継続するより短いペイロードの形でこの状況から脱する独創的な方法を見つけ出し、末尾の文字列がすべてコメントとして処理されるよう、末尾のセミコロンと `//` 文字列を注入するまでの話です。
以下のペイロードが使用されます。
1s \" onLoad=alert(1); //
XSS が再び成功しました。
しかし、別のアプローチを試してみたらどうなるでしょう?
`dangerouslySetInnerHTML` セクション内で `alt=` 属性を二重引用符で囲まず、代わりにエスケープ関数全体をリファクタリングしたらどうでしょうか?
この方法を試してみます。React コンポーネントの JSX コードは、引き続き以下のようになります。
1 <div
2 dangerouslySetInnerHTML={{
3 __html: `
4 <img src=${database.authorScreenshotURL}
5 alt=${escapeHTML(
6 authorScreenshotDescription
7 )} />
8 `,
9 }}
10 />
次に、既存の `escapeCrossSiteScripting` を、より安全で、`<`, `>` や `&` 以外の文字もエンコードする適切な HTML エスケープ関数にリファクタリングします。
そこで、コードの入力を開始すると、当然ながら GitHub Copilot が起動して、以下が提案されます。
実際には関数本体のコード全体が提案され、このサニタイズが終了するまでコード提案を完了し続けるよう指示しました。これは、一重引用符や二重引用符などの他の危険な文字を考慮する、より優れた出力エンコードロジックのように見えます。
アプリケーションに次のような元のペイロードを提供したとします。
1s \"<img src=x onError=alert(1)
GitHub Copilot から提案されるコードは、攻撃ペイロードがエスケープしようとした二重引用符を含む、潜在的な危険性があるすべての文字をエンコードしていたはずです。
しかしそれでも、問題は発生します。以下のような単純な攻撃ペイロードでも、XSS がトリガーされ、任意の JavaScript コードの実行が許可されるためです。
1s onLoad=alert(1)
しかしなぜ、このような問題が発生するのでしょう?
React XSS のセキュリティに関する問題のハイライト
この最後の試行は HTML エスケープ関数としては包括的でしたが、出力エスケープの基本原則と一般的なサニタイズセキュリティロジックが欠けています。重要なのはコンテキストです。
これは、機密性の高い API にデータが流れ込むコンテキストです。このケースでは、属性値は HTML 属性の値であり、HTML 要素の値ではありません。`alt` 属性の終了と、次の属性である `onLoad` の開始を指定するために、単純に空白文字を使用できることが問題の中心です。
React アプリケーションにおける XSS からの保護
GitHub Copilot のようなツールはソフトウェア開発に革命をもたらしていますが、これらの AI モデルでは安全ではないコードも提案される可能性があることを覚えておくことが重要です。そのため、アプリケーションを保護するには、堅牢なセーフティネットが不可欠です。それこそが、Snyk のような開発者セキュリティツールの役割です。
開発者ファーストのセキュリティツールの業界で高く評価されている Snyk では、便利な IDE 拡張を提供しています。React コードを記述する際に、Snyk は潜在的なセキュリティの脆弱性を検出できます。これには、Copilot で検出できない可能性がある `dangerouslySetInnerHTML` の危険な使用によって発生する脆弱性などが含まれます。このリアルタイムの保護は、安全な React アプリケーションを作成し、XSS 攻撃を防ぐのに役立ちます。
Snyk の差別化要因となる重要な機能の 1 つが、DeepCode AI です。DeepCode は、ユーザーがコードを記述しているときに、AI を活用した検出結果を利用して、コードをスキャンし、セキュリティ、パフォーマンス、ロジックに関するコードの問題を特定します。しかも、潜在的な問題を特定するだけではありません。DeepCode AI は、特定された問題に対して自動生成された修正を提供する AI Fix も備えているため、安全かつ効率的なコーディングがさらに容易になります。
Snyk は、開発者が IDE の利便性を活用してより安全なコードを作成できるように設計された無料ツールです。GitHub Copilot などのツールによる AI を活用したコーディング支援を利用する際には、Snyk のようなツールによる補完的な保護が非常に貴重な資産となります。
Snyk を使用することに加えて、こちらの Web アプリケーションセキュリティプラクティスに従う必要もあります。これらのセキュリティ管理の 1 つが、安全な出力エンコーディングの実行に関連しています。
フロントエンド Web アプリケーションでこのようなセキュリティ リスクを軽減するには、`dangerouslySetInnerHTML` などの機密性の高い API に流入するユーザー入力を慎重にサニタイズする必要があります。ユーザー入力は、常に信頼できないものとして扱う必要があります。そのため、ユーザーから提供された HTML コンテンツはすべて、堅牢な HTML サニタイズプロセスを経る必要があります。そのために、DOMPurify などのいくつかのサードパーティライブラリを活用できます。
1import DOMPurify from "dompurify";
2
3function MyComponent({userInput}) {
4 const cleanInput = DOMPurify.sanitize(userInput);
5 return <div dangerouslySetInnerHTML={{__html: cleanInput}} />;
6}
また、テキストコンテンツをレンダリングするだけの場合は、中括弧 `{}` を使用した React のテキストコンテンツ処理の使用など、`dangerouslySetInnerHTML` の代替手段を検討してください。
まとめ
結論として、AI の提案によってコーディングプロセスを加速できる一方で、安全なアプリケーションを開発するには人間の目による監視と堅牢なセキュリティツールが依然として不可欠です。
また、`dangerouslySetInnerHTML` は React で HTML を処理する手段となりますが、その潜在的なセキュリティへの影響を理解することが重要です。慎重に使用し、流入するデータをサニタイズすることで、React アプリケーションを XSS の脆弱性から保護できます。
ぜひ Snyk を試して、そのリアルタイムの脆弱性評価と自動修正によって React アプリケーションのセキュリティが強化され、安全なソフトウェアの構築を目指すエンジニアの生産性が向上することをお確かめください。特にセキュアコーディングに関しては、格言にあるとおり予防は治療に優ります。