1週間程度、Content Security Policy (CSP)
の reportをログ出力していたので、
ログをMongoDBに取り込んで、結果を集計して、設定を変更してみます。
ログの整形 取り込みまで
- ログの形式
以下のような形式で出力されています。
django-csp-reports という、plugin で、レポートのログ出力を行なってます。
Django製の Mezzanine で構築されたブログに対して http-observatory-cli で 脆弱性を検証してみる | Monotalk
で、インストールを行なっていますが、そちらのログフォーマットで設定すると以下のようになります。
WARNING 2017-04-17 02:40:06,206 utils 17040 140383643092736 Content Security Policy violation: {
"csp-report": {
"blocked-uri": "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js",
"disposition": "report",
"document-uri": "https://www.monotalk.xyz/",
"effective-directive": "script-src",
"original-policy": "default-src 'self'; report-uri /report/",
"referrer": "",
"status-code": 0,
"violated-directive": "script-src"
}
}
-
ログ出力されたjson を、MongoDBに取り込める形に成形
直接取り込んでおけばですが、ワンライナーで取り込める形式に変換ができたので、 よしとしておきます。
cat your_app.log | sed -e "s/^WARNING.*/{/g" | sed ':loop; N; $!b loop; s/\n//g' | sed 's/}{ "csp-report": /\ /g' | sed 's/{ "csp-report": //g' | sed 's/ }}/ }/g' > seikei.json
-
MongoDB に、db
csp_analyze
を作成して、reports
というcollection名で、データをインポートする
上記で作成したjsonファイルを、取り込みます。
mongoimport --db csp_analyze --collection reports --type json --file seikei.json
reportとして、どんな情報が出ているかわからないので、調べる。
report として出力される情報の意味がわからないと集計もできないので、
どんな情報が取得できているか確認します。
-
全データの保持するキーを重複を排除して出力する
上記スクリプトの実行で、コンソールに以下のキー値が出力されました。var keyArray = new Array(); db.reports.find().forEach(function(elem){for(key in elem) keyArray.push(key); }); var distinctKeyArray = keyArray.filter(function (x, i, self) { return self.indexOf(x) === i; }); for(key in distinctKeyArray) print(distinctKeyArray[key]);
_id は、MongoDB のキー値ですね。
キーの一覧が得られましたので、_id blocked-uri column-number disposition document-uri effective-directive line-number original-policy referrer source-file status-code violated-directive
各キーの設定値について確認します。 -
各項目の説明
項目名 | 説明 | 値の例 |
---|---|---|
blocked-uri | Content Security Policy により読み込みがブロックされたリソースの URI | https://doc-00-0c-docs.googleusercontent.com 等 |
column-number | エラーレポートが報告されたコンテンツの対象の列を表示(していると思います) | 27224 等 |
disposition | “enforce” か “reporting” が設定される。 reportしているので、”reporting”のみ | enforce or reporting |
document-uri | 違反が生じたドキュメントの URI。アクセスのあったぺージのURLが設定されます。 | https://www.monotalk.xyz/blog/archive/2017/4/ 等 |
effective-directive | 違反 を検知したポリシー設定 | script-src 等 |
line-number | 違反が検知されたソース上の行数 | 4985 等 |
original-policy | サイトに適用しているCSPヘッダの内容 | default-src ‘self’; report-uri /report/ 等 |
referrer | referrerの内容 | https://www.google.co.in/ 等 |
source-file | ポリシー違反したリソース名 | https://pagead2.googlesyndication.com/pagead/js/r20170417/r20170110/rum.js 等 |
status-code | 元のリソースのHTTPステータスコードを表す非負整数値 | 200, 0 等 |
violated-directive | 違反があったポリシーセクションの名前 | img-src 等 |
- 各項目のサンプル値取得に使用した distinct クエリ
// blocked-uri db.reports.distinct("blocked-uri").forEach(function(elem) { print(elem); }); // column-number db.reports.distinct("column-number").forEach(function(elem) { print(elem); }); // disposition db.reports.distinct("disposition").forEach(function(elem) { print(elem); }); // line-number db.reports.distinct("line-number").forEach(function(elem) { print(elem); }); // original-policy db.reports.distinct("original-policy").forEach(function(elem) { print(elem); }); // referrer db.reports.distinct("referrer").forEach(function(elem) { print(elem); }); // source-file db.reports.distinct("source-file").forEach(function(elem) { print(elem); }); // status-code db.reports.distinct("status-code").forEach(function(elem) { print(elem); }); db.reports.distinct("violated-directive").forEach(function(elem) { print(elem); });
どんな情報が出力されるのかはわかったので、集計する
現在のポリシー定義
実際の値は、改行はありませんが、区切りで改行を入れています。
Content-Security-Policy-Report-Only
でレポートのみを行うようにしています。
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' pagead2.googlesyndication.com www.google-analytics.com monotalkxyz.disqus.com;
report-uri /report/
script-src の定義をしてみる
script-src のレポートを集計して、ホワイトリストに定義するドメインを追加します。
source-file
が自ドメインで、violated-directive
がscript-src
のデータを集計
var count = 0;
// Query
db.reports.aggregate([{
$match: {
"violated-directive": "script-src", "source-file" : { $regex: /https:\/\/www.monotalk.xyz/ }
}
},
{
$group: {
"_id": {
"source-file": "$source-file",
"blocked-uri": "$blocked-uri"
},
"count": {
"$sum": 1
}
}
},
{
$sort: {
"count": -1
}
}
]).forEach(function(element){
if(count == 0) {
print("|" + "count" + "|" + "source-file" + "|" + "blocked-uri" + "|");
print("|:-------------------|:--------------------|:--------------|");
}
count++;
print("|" + element.count + "|" + element._id["source-file"] + "|" + element._id["blocked-uri"] + "|");
});
blog の管理画面側で、
"blocked-uri" : "eval"
の警告が出ていますが、これは、blog 管理画面側だけなのでとりあえず、放置します。
source-file
がjs
ファイルで、violated-directive
がscript-src
のデータを集計
結果セットをBlog に貼り付けるため、Markdown
のテーブル形式に変換しています。
var count = 0;
// Query
db.reports.aggregate([{
$match: {
"violated-directive": "script-src", "source-file" : { $regex: /.js$/ }
}
},
{
$group: {
"_id": {
"source-file": "$source-file",
"blocked-uri": "$blocked-uri"
},
"count": {
"$sum": 1
}
}
},
{
$sort: {
"count": -1
}
}
]).forEach(function(element){
if(count == 0) {
print("|" + "count" + "|" + "source-file" + "|" + "blocked-uri" + "|");
print("|:-------------------|:--------------------|:--------------|");
}
count++;
print("|" + element.count + "|" + element._id["source-file"] + "|" + element._id["blocked-uri"] + "|");
});
- 結果
img-src の定義をしてみる
-
source-file
が自ドメインで、violated-directive
がimg-src
のデータを集計
var count = 0; // Query db.reports.aggregate([{ $match: { "violated-directive": "img-src", "source-file" : { $regex: /https:\/\/www.monotalk.xyz/ } } }, { $group: { "_id": { "source-file": "$source-file", "blocked-uri": "$blocked-uri" }, "count": { "$sum": 1 } } }, { $sort: { "count": -1 } } ]).forEach(function(element){ if(count == 0) { print("|" + "count" + "|" + "source-file" + "|" + "blocked-uri" + "|"); print("|:-------------------|:--------------------|:--------------|"); } count++; print("|" + element.count + "|" + element._id["source-file"] + "|" + element._id["blocked-uri"] + "|"); });
-
結果
violated-directive
がscript-src
のデータのblocked-uri
を集計
var count = 0;
// Query
db.reports.aggregate([
{
$match: {
"violated-directive": "img-src"
}
},
{
$group: {
"_id": {
"blocked-uri": "$blocked-uri"
},
"count": {
"$sum": 1
}
}
},
{
$sort: {
"count": -1
}
}
]).forEach(function(element){
if(count == 0) {
print("|" + "count" + "|" + "blocked-uri" + "|");
print("|:-------------------|:--------------|");
}
count++;
print("|" + element.count + "|" + element._id["blocked-uri"] + "|");
});
- 結果
一旦集計をやめて、ポリシー定義
CSP のエラーレポートはこれ以外にも、大量に出てますが、ちょっと集計が疲れてきたので、
やめて、得られた情報を少しポリシー を見直します。
上記の集計結果から以下の3点いえるかと思います。
-
google の alaytics や、adsense、google drive のコンテンツへのリンクを貼り付けているが、
結構知らないドメイン(google所有)にリンクされている。 -
monotalkxyz.disqus.com
に対して、c.disquscdn.com
(CDNサーバ) 等、ソーシャルサービスが使っている、
CDNドメインがあって、そのあたりの警告が出力されている。 -
goole の alaytics や、adsense、などは、結構 inline スクリプトが記載されている。
これを踏まえて、以下の通り、ポリシーを修正しました。
Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' *.disqus.com pagead2.googlesyndication.com www.google-analytics.com;
img-src 'self' *.disqus.com *.disquscdn.com *.googleusercontent.com *.google.com googledrive.com
report-uri /report/
完全に定義の途中ですが、一旦記載します。
だいぶ先は長そうです。
未だに相当数のエラーが出ているし。。
以上です。
コメント