Local Storageに保管されるCognitoトークンへのセキュリティ対策

## 導入
- Amplifyライブラリを用いてReactアプリにCognitoを統合する場合、デフォルト設定ではLocal StorageにCognitoのトークンが保管されます。    
- Local Storageへのトークン保管に否定的な意見を目にすることもありますが、個人的な意見としては`適切なXSS対策を実施する前提で`Local Storageもトークン保管先として選択しうると思っています。
    - トークン保管先にLocal StorageではなくCookieを選択した場合でも、XSSには無力なため、開発の過程やサプライチェーンでXSS脆弱性を組み込まないことが重要。
    - そのうえで、スケーラビリティや一斉ログイン等の要件を踏まえた上で、適切な保管先を選択するのが望ましいはず。
(徳丸先生の[SPAセキュリティ入門](https://www.docswell.com/s/ockeghem/ZM6VNK-phpconf2021-spa-security)から多大なる影響を受けています)
- 本記事では、Amplifyライブラリ利用時にLocal StorageにCognitoのトークンが保管されることを実機確認した上で、XSS脆弱性を組み込まないための対策例について、記載していこうと思います。



## Local StorageにCognitoのトークンが保管されることを確認する

### Cognitoのセットアップ

- ユーザープールを作成します
- アプリクライアントを追加します

(ユーザープールやアプリクライアントはAWSコンソール上のガイドに従って作成しましたが、初めてCognito利用される方は[チュートリアル: ユーザープールの作成](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/tutorial-create-user-pool.html)が参考になると思います)


### Amplifyライブラリを用いた、ReactアプリへのCognito統合

- Reactアプリを作成します

```jsx
# アプリ作成
npx create-react-app [appName]　--template typescript
# ローカルサーバで実行
npm start
```

- Amplifyパッケージをインストールします

```jsx
npm i @aws-amplify/ui-react aws-amplify
```

- Cognitoユーザープール設定情報を含むclassを定義します

```jsx
// 自身のリージョン、ユーザープールID、クライアントID情報を記載してください
class CognitoExport {
  public readonly REGION = "ap-northeast-1";
  public readonly USER_POOL_ID = "region_user_pool_id_here";
  public readonly USER_POOL_APP_CLIENT_ID= "user_pool_app_client_id";
}
export default CognitoExport
```

- App.tsxを編集し、Cognitoユーザープールと Authenticator UI コンポーネントを使用して React ウェブアプリケーションのユーザーを認証するように設定します

```jsx
import React from "react";

import "./App.css";
import { Amplify } from "aws-amplify";
import CognitoExport from "./CognitoExport";
import { Authenticator } from "@aws-amplify/ui-react";
import "@aws-amplify/ui-react/styles.css";
import { Auth } from "aws-amplify";

const cognitoExport = new CognitoExport();
Amplify.configure({
  Auth: {
    region: cognitoExport.REGION,
    userPoolId: cognitoExport.USER_POOL_ID,
    userPoolWebClientId: cognitoExport.USER_POOL_APP_CLIENT_ID,
  },
});

function App() {
  return (
    <Authenticator>
      {({ signOut, user }) => (
        <div>
          <p>Welcome</p>
          <button onClick={signOut}>Sign out</button>
        </div>
      )}
    </Authenticator>
  );
}

export default App;
```

([Amazon Cognito と AWS Amplify を使用して React アプリケーションユーザーを認証する](https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/authenticate-react-application-users-by-using-amazon-cognito-and-aws-amplify.html)を参照)

### 動作検証

- ローカルサーバで実行したところ、サインイン画面が無事表示されました
![スクリーンショット 2023-10-01 1.38.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/1de75aaa-c030-4ad9-d0cb-5c6559f97598.png)


- サインイン後にLocal Storageを確認してみると、`CognitoIdentityServicePorvider.クライアントID.(ユーザ名).キー情報`形式のキーに対して、トークン等が格納されていることが確認できました
![スクリーンショット 2023-10-01 1.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3562653/b6106600-985f-c19b-a4d5-271d555bb075.png)

    ※当該検証で利用した資源は削除済みですが、画像を一部加工しています。

(Cognitoのトークンについては、[ユーザープールでのトークンの使用](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html)を参照)

## XSS脆弱性を組み込まないための対策例
- トークン保管先がLocal StorageとCookieのどちらであっても、XSS対策が重要です。
- 開発時やサプライチェーンにおいて、XSS脆弱性混入を防ぐための対策例を列挙してみました。

### 開発時のXSS脆弱性混入対策

#### IPAの「安全なウェブサイトの作り方」への準拠

- [IPAの「安全なウェブサイトの作り方」](https://www.ipa.go.jp/security/vuln/websecurity/cross-site-scripting.html)を参照した上で、XSSへの根本的・保険的対策を抜けもれなく実施するのが、最も重要だと思います。

#### CSPの利用
- [コンテンツセキュリティポリシー (CSP)](https://developer.mozilla.org/ja/docs/Web/HTTP/CSP)とは、クロスサイトスクリプティング (Cross-site_scripting) やデータインジェクション攻撃などのような、特定の種類の攻撃を検知し、影響を軽減するためのセキュリティヘッダです。
- [IPAの「安全なウェブサイトの作り方」](https://www.ipa.go.jp/security/vuln/websecurity/cross-site-scripting.html)に記載されている`スタイルシートを任意のサイトから取り込めるようにしない。`や`クロスサイト・スクリプティングの潜在的な脆弱性対策として有効なブラウザの機能を有効にするレスポンスヘッダを返す。`はCSPで実装可能です。
- 加えて、CSPの[script-srcディレクトリ](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Security-Policy/script-src)を利用することで、JavaScriptの読み込みソースを制限することができ、こちらもXSS対策となります。

#### WAFの利用

- WAFをアプリケーションの前段に配置することは、XSSへの保険的対策となります。
- AWS WAFであれば、[コアルールセット (CRS) マネージドルールグループ](https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-baseline.html)にXSS対策のルールが含まれています。

#### セキュリティテストの実施

- SASTやDASTなどのセキュリティテストをCICDパイプラインや開発ライフサイクルに組み込むことは、脆弱性の早期検出に効果的です。

### サプライチェーンでのXSS脆弱性混入対策

#### SCAツールの利用

- [Snyk Open Source](https://snyk.io/jp/product/open-source-security-management/)等のSCAツールを利用することは、オープンソースコンポーネントが内包する脆弱性の早期検出に効果的です。

#### Amazon Inspectorの利用

- Amazon EC2、AWS Lambda 関数、Amazon ECRを利用している場合には、[Amazon Inspector](https://aws.amazon.com/jp/inspector/)もオープンソースコンポーネントが内包する脆弱性の早期検出に効果的です。
- Inspectorコンソール上での脆弱性モニタリングに加えて、SBOMのS3エクスポートを具備しており、出力レポートに対してAthenaやQuickSightで分析を実施することで、ソフトウェア部品レベルに対する脆弱性管理が可能です。


## 終わりに
- Amplifyライブラリを用いてReactアプリにCognito統合する場合、各種トークンはLocal Storageに保管されますが、Local Storageの利用自体はセキュリティ上の懸念事項ではなく、適切なXSS対策を実施することでリスク軽減が可能です。
- 本記事が皆様のお役に立てれば幸いです。

## 注意事項
- 本記事は万全を期して作成していますが、お気づきの点がありましたら、ご連絡よろしくお願いします。
- なお、本記事の内容を利用した結果及び影響について、筆者は一切の責任を負いませんので、予めご了承ください。