CSPのreport-uriを試してみたよ


# まえがき

業務でCSPを設定してるサイトに広告を入れることになりまして。
CSPの設定自体がはじめてだったのでどうしていいのか分からず、結局人海戦術で対応しました。。。

で、その後で`report-uri`を知ったので、備忘として残します。

なお、**そのサイトではまだ使っておらず、ローカルで試しただけです**。。
ダメだったらゴメンね(´・ω・｀)
ヤ、でも自分のPCでは動いたんだ(;´Д｀)

あと、業務に関わるパラメータはテキトーに一括置換で書き換えてます。
たぶん大枠問題ないと思うんですが、なんか変になってても許してください(;´Д｀)

**2018/02/19 追記**
わたくし、嘘をついておりました・・・・(´・ω・｀)
いや、元々記述していた内容でも動くんですが、そんなまだるっこしいことしなくて良かったので、
記事の内容を直しておきます。


# 通常の設定

CSPを有効にするには、要はレスポンスヘッダーに入ればいいので、
Nginxの設定で入れてもよいし、プログラム側でヘッダーに出力してもよいです。

今回は、Nginxに `add_header` で入れてます。

```nginx
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com;"
```

・・・・ホントは`script_src`とか`image-src`とかも入れてるんですが、あまりにも長いので`default-src`のみ書いときます。



# report-uriによる報告機能

問題の`report-uri`、設定は以下のようになります。

```nginx
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com; report-uri https://local.izunak.com/csp/report;"
```

`report-uri`に報告先へのURLを設定します。

報告の内容は、以下のようなJSONが、`report-uri`に設定したURLへPOSTにより送られます。

```json:report-uriの報告内容例
{
    "csp-report": {
        "blocked-uri": "ブロックしたURL",
        "column-number": 25,
        "disposition": "report",
        "document-uri": "https://local.izunak.com/",
        "effective-directive": "default-src",
        "line-number": 67042,
        "original-policy": "default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com; report-uri https://local.izunak.com/csp/report;",
        "referrer": "",
        "script-sample": "",
        "source-file": "https://local.izunak.com/bundle-pc.js",
        "status-code": 0,
        "violated-directive": "default-src"
    }
}
```


# 具体的な例

自分のローカル環境上で試した設定と、報告内容を記載します。

## nginx.conf
```nginx
http {

    #・・・（略）・・・

    log_format  postdata '[$time_local] request_body: [$request_body]';

    #・・・（略）・・・

}
```

ログの出力フォーマットを定義してます。
ゴチャゴチャ書いてあっても見づらいので、時間とPOSTパラメータだけ出力させています。

## default.conf
```nginx
server {
    listen 443 default ssl;
    ssl on;
    server_name local.izunak.com;

    #・・・（略）・・・

    # CSP
    # ちょっとだけ記述しやすいように変数化
    set $default_src "default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com;";
    set $report_uri "report-uri https://local.izunak.com/csp/report;";

    add_header Content-Security-Policy $default_src$report_uri;

    # 報告を出力
    location /csp/report {
      access_log /var/log/nginx/postdata.log  postdata;
      fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    #・・・（略）・・・
}
```

CSPの設定、1行で書くのがイヤになったので各変数つくって最後にくっつけてます。。
だって見づらいんだもん(´・ω・｀)

`report-uri`の報告先として自身の`/csp/report`を指定し、`location`で用意した受取先で直接ログ出力をしています。

ここで`fastcgi_pass`を設定しているのは、`location`内でPOSTパラメータを受け取れるようにするためです。
入れないとPOSTが空なので、なんの役にも立たないログファイルが出来上がるので気をつけて。

参考: [Nginx で POST データのログをフィルタする](https://recruit.gmo.jp/engineer/jisedai/blog/nginx-post-log-filter/)

## 報告内容

ログに出力された報告内容は↓のように出力されます。

```Bash:/var/log/nginx/postdata.log
[08/Feb/2018:22:17:16 +0900] request_body: [{\x22csp-report\x22:{\x22blocked-uri\x22:\x22ブロックしたURL\x22,\x22column-number\x22:25,\x22disposition\x22:\x22report\x22,\x22document-uri\x22:\x22https://local.izunak.com/\x22,\x22effective-directive\x22:\x22default-src\x22,\x22line-number\x22:67042,\x22original-policy\x22:\x22default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com; report-uri https://local.izunak.com/csp/report;\x22,\x22referrer\x22:\x22\x22,\x22script-sample\x22:\x22\x22,\x22source-file\x22:\x22https://local.izunak.com/bundle-pc.js\x22,\x22status-code\x22:0,\x22violated-directive\x22:\x22default-src\x22}}]
```

ダブルクォーテーションが`\x22`になってるけど・・・・だいたいOK。

JSONを使いやすくするプラグインがあるのは知ってるけど、
これにしか使わないならわざわざ入れる必要もないかな、と・・・・。

今回はCSPにはじかれたURLさえ分かればいいのでやりませんでしたが、
`report-uri`の受取先としてプログラムを用意して処理することももちろん可能です。
プログラム側でCSPをヘッダー出力しているなら、
受け取った後にプログラムでワッショイワッショイした方が手間が省けるんですかねたぶん。


# Content-Security-Policy-Report-Only

`report-uri`でNGだったURLを報告してもらえる・・・・とはいえ。
許可していないばっかりに広告が表示されないと収入が減って困る、という場合、
`Content-Security-Policy-Report-Only`を使用するとよいかも？

```nginx
add_header Content-Security-Policy-Report-Only "default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com; report-uri https://local.izunak.com/csp/report;"
```

`Content-Security-Policy-Report-Only`で設定した場合、
**このパラメータ上で設定されたルールに該当しないリクエストが来た場合、リクエストのブロックはされず、`report-uri`に設定されたURLへ報告するだけとなります**。

`Content-Security-Policy`との併用も可能です。

```nginx
server {
    listen 443 default ssl;
    ssl on;
    server_name local.izunak.com;

    #・・・（略）・・・

    # CSP
    # ちょっとだけ記述しやすいように変数化
    set $default_src "default-src 'self' 'unsafe-inline' 'unsafe-eval' local.izunak.com;";
    set $report_uri "report-uri https://local.izunak.com/csp/report;";

    add_header Content-Security-Policy $default_src;
    add_header Content-Security-Policy-Report-Only $default_src$report_uri;

    # 報告を出力
    location /csp/report {
      access_log /var/log/nginx/postdata.log  postdata;
      fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    #・・・（略）・・・
}
```

# あとがき

まえがきに書いたとおり、まだ業務で設定した内容ではないので、
~~不正確だったり不備があるかもしれません。。~~
**不備ありました・・・・orz**
ごめんなさい・・・・記事にする前にローカルで動くことは確認していたんですが・・・・

修正したのは、`Content-Security-Policy-Report-Only`でしか`report-uri`が使えないような記述をしていた個所です。

`Content-Security-Policy`への追加も試していたんですが、
おそらく単純に書き方が悪かったせいで正常動作せず、
`Content-Security-Policy-Report-Only`を試した際にたまたまその不備が解消されていたんではないかと。。



# 余談

ぜんぜんCSPとも関係ないんですが、コレ書いてるときに気になったので、、、

すぐ消すようなローカル環境をチャラっと作る際、だいたいドメインを`***.izunak.com`にしてるんですが、これって一般的なものなんですかね・・・・？
何度か気になってググってみるも答えが見つからず・・・・orz

はじめてVirtualPCをつかうプロジェクトをやったとき、先輩からすでに用意されたのをもらったんですが、そのときに設定されてたのが`***.izunak.com`だったんで、なんとなく自分の中でコレが標準になってます。