Content Security Policy (CSP) の report を MonogoDB で集計して、ポリシーを見直す(の途中)


1週間程度、Content Security Policy (CSP) の reportをログ出力していたので、
ログをMongoDBに取り込んで、結果を集計して、設定を変更してみます。


ログの整形 取り込みまで

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-uriContent Security Policy により読み込みがブロックされたリソースの URIhttps://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/ 等
referrerreferrerの内容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-directivescript-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-filejs ファイルで、violated-directivescript-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"] + "|");
});
  • 結果
countsource-fileblocked-uri
1740https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.jsinline
165https://monotalkxyz.disqus.com/embed.jshttps://c.disquscdn.com/next/embed/alfie.f51946af45e0b561c60f768335c9eb79.js
156https://pagead2.googlesyndication.com/pagead/js/r20170410/r20170110/show_ads_impl.jsinline
156https://pagead2.googlesyndication.com/pagead/js/r20170417/r20170110/show_ads_impl.jsinline
120https://pagead2.googlesyndication.com/pagead/js/r20170412/r20170110/show_ads_impl.jsinline
12https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.jshttps://pagead2.googlesyndication.com/pagead/js/r20170410/r20170110/show_ads_impl.js
6https://pagead2.googlesyndication.com/pagead/js/r20170410/r20170110/show_ads_impl.jshttps://pagead2.googlesyndication.com/pagead/osd.js
6https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.jshttps://pagead2.googlesyndication.com/pub-config/r20160913/ca-pub-6873737391821354.js
2https://monotalkxyz.disqus.com/embed.jshttps://disqus.com/next/config.js
2https://monotalkxyz.disqus.com/count.jshttps://monotalkxyz.disqus.com/count-data.js?1=BlogPost-201&1=BlogPost-205&1=BlogPost-209&1=BlogPost-215&1=BlogPost-216&1=BlogPost-217&1=BlogPost-218&1=BlogPost-219&1=BlogPost-220
2https://monotalkxyz.disqus.com/embed.jshttps://c.disquscdn.com/next/embed/common.bundle.08b03b11c747b79c4d4135b49e2f8725.js
1https://monotalkxyz.disqus.com/embed.jshttps://c.disquscdn.com/next/embed/lounge.bundle.987c6346e2effb8e4f350d48ecf53d13.js
1https://monotalkxyz.disqus.com/embed.jshttps://c.disquscdn.com/next/embed/lounge.bundle.76d63bdce6267b47c41551edb26ad379.js
1https://monotalkxyz.disqus.com/count.jshttps://monotalkxyz.disqus.com/count-data.js?1=BlogPost-47
1https://cdn.doubleverify.com/dvtp_src_internal75.jsinline
1https://pagead2.googlesyndication.com/pagead/js/r20170410/r20170110/show_ads_impl.jshttps://pagead2.googlesyndication.com/pagead/js/r20170410/r20170110/rum.js
1https://monotalkxyz.disqus.com/count.jshttps://monotalkxyz.disqus.com/count-data.js?1=BlogPost-220&1=BlogPost-61&1=BlogPost-62&1=BlogPost-65&1=BlogPost-67&1=BlogPost-68&1=BlogPost-71&1=BlogPost-73

img-src の定義をしてみる

  • source-file が自ドメインで、violated-directiveimg-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"] + "|");
    });
    

  • 結果

countsource-fileblocked-uri
9https://www.monotalk.xyz/blog/pycharm-terminal-%E3%81%8B%E3%82%89python%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B/https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOMUhXVThDQ3p5TWs
9https://www.monotalk.xyz/blog/pycharm-terminal-%E3%81%8B%E3%82%89python%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B/https://drive.google.com/uc?export=view&id=0By5O5w7iwOMONnhXRExfNkZWN3c
7https://www.monotalk.xyz/static/CACHE/js/9e88e3aedae2.jsdata
6https://www.monotalk.xyz/blog/mezzanine-%E5%80%A4%E3%81%AF%E5%9E%8Bcharacter-varying200%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AF%E9%95%B7%E3%81%99%E3%81%8E%E3%81%BE%E3%81%99/http://drive.google.com/uc?export=view&id=0By5O5w7iwOMOWmMybXZQWV8wTkU
6https://www.monotalk.xyz/blog/mezzanine-%E5%80%A4%E3%81%AF%E5%9E%8Bcharacter-varying200%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AF%E9%95%B7%E3%81%99%E3%81%8E%E3%81%BE%E3%81%99/http://drive.google.com/uc?export=view&id=0By5O5w7iwOMOc09ZWXBQWFd5QTA
5https://www.monotalk.xyz/blog/Turn-off-Java-program-warning-on-SonarQube/https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOVmVBWG1hbXUwdEU
5https://www.monotalk.xyz/blog/Turn-off-Java-program-warning-on-SonarQube/https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOS2pHbU1TZ2MwOUU
5https://www.monotalk.xyz/blog/Turn-off-Java-program-warning-on-SonarQube/https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOMFg4M2UtRFJtbHc
3https://www.monotalk.xyz/blog/install-SonarQube-on-El-Capitan/https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOalBpWkpXeGRKX00
2https://www.monotalk.xyz/blog/configure-mongodb-and-postgres-on-wercker/http://drive.google.com/uc?export=view&id=0By5O5w7iwOMOSXdleklpRG1YbFk
2https://www.monotalk.xyz/blog/configure-mongodb-and-postgres-on-wercker/http://drive.google.com/uc?export=view&id=0By5O5w7iwOMOeHlySVBtWk5ZVWM
2https://www.monotalk.xyz/static/CACHE/js/9e88e3aedae2.js.pagespeed.jm.OH66oSK0of.jsdata
1https://www.monotalk.xyz/static/mezzanine/js/jquery-1.8.3.min.jshttps://drive.google.com/uc?export=view&id=0By5O5w7iwOMOZTFRQmdSLWNTTEE
1https://www.monotalk.xyz/static/CACHE/js/9e88e3aedae2.js.pagespeed.jm.OH66oSK0of.jshttps://c.disquscdn.com/next/embed/assets/img/loader-bg.173909e4737a7481df14d5492b5eeb48.png
  • violated-directivescript-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"] + "|");
});
  • 結果
countblocked-uri
147data
53https://docs.google.com
32https://drive.google.com
17https://referrer.disqus.com/juggler/stat.gif?event=lounge.loading.view
17https://c.disquscdn.com/next/embed/assets/img/loader-bg.173909e4737a7481df14d5492b5eeb48.png
15https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOMUhXVThDQ3p5TWs
15https://drive.google.com/uc?export=view&id=0By5O5w7iwOMONnhXRExfNkZWN3c
10https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOTE5BOWFKbmFjQUE
10https://googledrive.com/host/0By5O5w7iwOMOcnhRVndXNVZLLTA/2016-01-10_14.58.17.png
10https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOMDJ3NHFEb25VTFU
9https://doc-0o-6s-docs.googleusercontent.com
8http://drive.google.com/uc?export=view&id=0By5O5w7iwOMOc09ZWXBQWFd5QTA
8http://drive.google.com/uc?export=view&id=0By5O5w7iwOMOWmMybXZQWV8wTkU
5https://doc-10-6s-docs.googleusercontent.com
5https://drive.google.com/uc?export=view&id=0By5O5w7iwOMOalBpWkpXeGRKX00
5https://doc-0k-6s-docs.googleusercontent.com
5https://doc-0g-6s-docs.googleusercontent.com

一旦集計をやめて、ポリシー定義

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/

完全に定義の途中ですが、一旦記載します。
だいぶ先は長そうです。
未だに相当数のエラーが出ているし。。
以上です。

コメント