React+AmplifyにContent-Security-Policy-Report-Onlyを設定しCloudWatchにログをためる話

##背景
業務で開発しているReactアプリの中で、XSSの対策としてContent-Security-Policyの設定を行うか議論があり、
そのための判断基準を設けるために一旦、Content-Security-Policy-Report-Onlyを設定してレポートを収集して分析を行う流れになった。

##Content-Security-Policy(CSP)とは
[簡易な認識] : XSS対策の一環で、HTTPのヘッダーの中にそのサイトで使用するJSのスクリプトやコンテンツ、画像やフォントなどを読み込む場所をホワイトリストとして設定することで
それ以外の場所から読み込まれるものはブロックするもの。

詳しい内容はいろんなサイトが紹介している

https://developer.mozilla.org/ja/docs/Web/HTTP/CSP

##Content-Security-Policy-Report-Onlyとは
Content-Security-Policyを設定するとブロックされたものは表示できなくなり、変に設定するとアプリとして壊れるかもしれない。
そこでCSPのエラーが起きる場合でもブロックせずにアプリはそのままで指定した場所にレポートのPOSTだけ飛ばすもの。

POSTする中身はこんな感じのJSON
![スクリーンショット 2021-10-12 18.44.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751681/5ffafd8a-0496-9db1-3b1f-6324bbef3111.png)



##構成

![スクリーンショット 2021-10-12 17.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751681/1e97911c-a605-e8dd-9c88-f126c9a73c49.png)


##実装
####1.Lambdaの実装
lambdaはeventから渡されたJsonデータをCloudwatchに[INFO]としてログに出す。データはeventのbodyに詰まっている。

```python:cspFunction.py
import json
from logging import getLogger, INFO
logger = getLogger(__name__)
logger.setLevel(INFO)


def lambda_handler(event, context):
    """
    Lambdaが呼び出される最初の関数
    """
    logger.info("{}".format(json.dumps(event['body'])))
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        }
    }
}
```

####2.APIGatewayの実装
1.APIGatewayからREST APIでPOSTのAPIを作成
2.Lambdaは上で作成したLambdaを選択
3.Lambda プロキシ統合の使用にチェック

まぁ特に変な設定は必要ないかなと思う。

####3.Content-Security-Policy-Report-Onlyの設定
今回はAmplifyを使用して構築を行なっているため、AmplifyのCustom Headerから設定を行う。

```
customHeaders:
  - pattern: '**/*'
    headers:
      - key: Content-Security-Policy-Report-Only
        value: >-
          script-src xxx; 
          style-src xxx
          img-src xxx; 
          font-src xxx;
          connect-src xxx;
          report-uri  APIGatewayで作成したAPIのパス;
```

以上の設定で、もしCSPに違反した場合、CloudWatchにCSPのレポートが送信されログが溜まるようになる。

##問題点
今回の実装するにあたって１番の問題で未解決に終わったのが`report-to`の問題である。
####report-toとは
report-uriと同じくCSPに違反した場合のレポートを送るURLを設定するところ。
現在、Content Security Policy Level 3(https://w3c.github.io/webappsec-csp) がリリースされたため、
report-uri (Content Security Policy Level 2)が非推奨になった。対応ブラウザ表は以下である。


||Chrome|Safari|Edge|FireFox|
|:---:|:---:|:---:|:---:|:---:|
|Content Security Policy Level 3|○|×|○|×
|Content Security Policy Level 2|○|○|○|○

Content Security Policy Level 3に対応していないブラウザがあるため`report-to`,`report-uri`の両方を設定することが推奨されている。


調査の結果、上記のように両方設定するのがいいとのことだったため、私も両方設定することにしたのだが...





###動かない...report-to....動かない...

少しreport-toとreport-uriで設定の記述が違うものの、大した違いはないので実装(記述)自体は間違っていないはずなのに動かなかった。
report-toとreport-uriを両方設定していれば、対応するブラウザ(chromeとedge)だとtoが優先されて動き、非対応(Firefoxとsafari)だとuriが動く。
そのためuriを設定しているFirefoxとsafariはCSPのレポートがちゃんと送信してくれている。

調査してみると、report-toは即時にポストするのではなく一旦ブラウザのネットワークのキューに詰め込み、ブラウザ側の制御で順次送信するとあった。
しかし、いくら待ってみてもうんともすんとも言わない。。。
まずはネットワークのキューにしっかり入っているかを確認した。(chrome://net-exporとhttps://netlog-viewer.appspot.com/#import を使って確認できる)

![スクリーンショット 2021-10-12 18.33.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751681/c1561bef-43d3-048d-b588-a713daa7a6f0.png)

...ちゃんとあるな...。
しかし何回やってもattemptが増えて、その内、キューが消えているが、一向にCSPのレポートが送信されない。
そこでこんな記事を見つけた。

https://community.brave.com/t/reporting-api-does-not-send-reports-in-dev-beta/74232

何回やってもキューには入るけど、実際にはレポートの送信はされないらしい。ほんまに？？？
記事自体も2年前だし、動くものが出ていなきゃダメでは？？と思いつつ、いくら設定しても動かないのは事実だしな。

結局、一応前ブラウザで動作確認の取れているuriで一旦設定した。


##感想
CSPの設定自体は苦労なく設定できるためよかった。
しかし`report-to`に関してはどうしようもなかったため、もしどなたか動いていたらコメントいただけると嬉しいです...


##参考文献
https://techblog.securesky-tech.com/entry/2020/05/21/

https://blog.jxck.io/entries/2017-02-13/csp-report-case-study.html

https://stackoverflow.com/questions/60632559/how-to-add-report-to-content-security-policy-directly-in-web-config

https://qiita.com/o_Ozzzzk/items/c505b7dd20707eb4ee08