Observatory by Mozilla で、ウェブサイトのセキュリティをチェックする 2回目


Mozilla は、Web サイトのセキュリティ分析ツール Observatory by Mozilla を提供しています。
過去に、Django製の Mezzanine で構築されたブログに対して http-observatory-cli で 脆弱性を検証してみる | Monotalk で、python client を使って、セキュリティチェック、対策を実施したのですが、VPS の移行後に再度チェックしたところ 0点になっていました。さすがに放置するのはまずいかと思いましたので、改めて Observatory by Mozilla でチェックを実施し、マイナス評価を受けた項目に対して、対策を施しましたので結果を記載します。


前提

サイトの構成は以下の通りです。

  • OS

    cat /etc/redhat-release
    CentOS Linux release 7.5.1804 (Core) 
    

  • HTTPサーバ

    Server version: Apache/2.4.29 (CentOS)
    

  • PythonのVersion

    Python 3.6.4
    

  • DjangoのVerison

    python3.6 -m pip list | grep Django
    Django                     1.10.8  
    


対策前 の、Observatory by Mozilla の 結果

以下、改善前の Observatory by Mozilla の実行結果になります。
テスト時に幾つかタブがありますが、HTTP Observatory の結果になります。

Scan Summary

Score が 0/100 で 0点です。

項目名
Host:www.monotalk.xyz
Scan IDxxxxxxx
Start Timexxxxxxx
Duration5 seconds
Score0/100
Tests Passed7/11

Test Scores

TestScoreExplanation
Content Security Policy-25Content Security Policy (CSP) header not implemented
Cookies-20Anti-CSRF tokens set without using the SameSite flag
Cross-origin Resource Sharing0Content is not visible via cross-origin resource sharing (CORS) files or headers
HTTP Public Key Pinning0HTTP Public Key Pinning (HPKP) header not implemented (optional)
HTTP Strict Transport Security0HTTP Strict Transport Security (HSTS) header set to a minimum of six months (15768000)
Redirection-20Does not redirect to an HTTPS site
Referrer Policy0Referrer-Policy header not implemented (optional)
Subresource Integrity-50Subresource Integrity (SRI) not implemented, and external scripts are loaded over HTTP or use protocol-relative URLs via src=”//…”
X-Content-Type-Options0X-Content-Type-Options header set to “nosniff”
X-Frame-Options0X-Frame-Options (XFO) header set to SAMEORIGIN or DENY
X-XSS-Protection0X-XSS-Protection header set to “1; mode=block”

減点を受けている項目は、Content Security PolicyCookiesRedirectionSubresource Integrity の 4項目です。
Content Security Policy の 減点を解消するのは、Ads を設定しているので、非常に難しく、それ以外の 3項目に対して対策を実施します。

X-Content-Type-OptionsX-Frame-OptionsX-XSS-Protection については過去に実施した対策が効いているので、出力されておりません。
Apache、Django のデフォルト設定だとこのあたりは減点受けます。
以前、Django製の Mezzanine で構築されたブログに対して、脆弱性を検証してみる | Monotalk で対策は実施していますので、興味ある方はご確認ください。


対策

それぞれの警告の対策について記載します。


Cookies

これは、SameSite フラグを使用せずに CSRF トークンを設定しているため 減点されているようです。
SameSite については以下の記事が参考になりました。

記事を読む限り、基本的にSameSite=Lax; を付与しておけば良さそうに思いました。
対象となる Cookie は、HTTPサーバ、Django で設定しているものがあり、それぞれで Samesite 属性を設定します。

Django

Django 2.1.x では、SameSite 属性がサポートされていますが、Django 1.x では、デフォルトで設定ができません。
Django 1.x で SameSite 属性が設定できるライブラリ jotes/django-cookies-samesite がありましたので、これを使って属性を設定します。

  • インストール

    python3.6 -m pip install django-cookies-samesite
    

  • INSTALLED_APPS に、django_cookies_samesite を追加する。

    INSTALLED_APPS = (
        'mezzanine_api',
        "django_cookies_samesite"                                                                                                              
    )
    

  • MIDDLEWARE_CLASSES の先頭に、django_cookies_samesite.middleware.CookiesSameSite を追加する。

    MIDDLEWARE_CLASSES = (
        'django_cookies_samesite.middleware.CookiesSameSite',
        ...
    )
    

  • SameSite のポリシーとして、Lax を設定
    SESSION_COOKIE_SAMESITE = 'Lax'

以上で、設定は完了です。
デフォルトだと、csrftoken と、sessionid に samesite 属性が付与しようとしていますが、sessionid には、属性が何故か付与されませんでした。

Apache

Header edit で設定ができるようです。
以下の記事が参考になりました。


Redirection

これは、 Apache でも Django でも設定ができます。
以下の記事で内容をまとめていますので、ご確認ください。


Subresource Integrity

以前、確認した際、3rd party 静的コンテンツ、1st party の静的コンテンツの対処法がわからず、放置していたので、1st party の静的コンテンツについて対応を実施します。
3rd party 静的コンテンツ は、Google Analytics や、 GTM の js を使用していますが、以下の記事を見る限り、Bugfix、機能追加で内容が変化していくので、付与するのは難しいのかと思います。
以下、3rd patry 静的コンテンツに関する Statck Overflow の記事になります。
* Sub Resource Integrity value for //maps.google.com/maps/api/js - Stack Overflow

webpack で、Subresource Integrity を設定する plugin

Blog テーマのJS、CSS の生成に、wenpack を使用しています。
当初、webpack の plugin で生成できないか調べていたのですが、既に使っているowais/django-webpack-loader: Transparently use webpack with django との相性が悪そうだったので使用しませんでした。
以下、見つけたものを幾つか記載します。

Hash を管理する Django の setting ファイルを作成して、Template で参照する

テーマのデプロイ時に、Hash を管理する setting ファイルを作成して、Template から、参照、css と、js のリンクタグに hash を追加するようにしました。
以下、修正した内容を記載します。

  • settings.py
    Hash を管理する python スクリプトを読み込む記載を追加しました。

    #####################
    # SRI SETTINGS #
    #####################
    try:
        from blog.sri import *
    except ImportError:
        pass
    

  • create_sri_settings.sh
    以下、sri.py を生成するスクリプトです。
    cat $BLOG_ROOT/static/js/html5shiv.js | openssl dgst -sha256 -binary | openssl enc -base64 で Hash を生成しています。
    Hash の生成方法は、Subresource Integrityについて調べた | 69log を参考にしました。

    BLOG_ROOT="/var/www/site/blog"
    rm -f "$BLOG_ROOT"/blog/sri.py
    echo "HTML5SHIV_SRI="'"'sha256-`cat $BLOG_ROOT/static/js/html5shiv.js | openssl dgst -sha256 -binary | openssl enc -base64`'"' >> "$BLOG_ROOT"/blog/sri.py 
    echo "RESPOND_MIN_SRI="'"'sha256-`cat $BLOG_ROOT/static/js/respond.min.js | openssl dgst -sha256 -binary | openssl enc -base64`'"' >> "$BLOG_ROOT"/blog/sri.py 
    echo "BUNDLE_JS_SRI="'"'sha256-`cat $BLOG_ROOT/static/webpack_bundles/bundle-*.js  | openssl dgst -sha256 -binary | openssl enc -base64`'"' >> "$BLOG_ROOT"/blog/sri.py 
    echo "BUNDLE_CSS_SRI="'"'sha256-`cat $BLOG_ROOT/static/webpack_bundles/bundle-*.css | openssl dgst -sha256 -binary | openssl enc -base64`'"' >> "$BLOG_ROOT"/blog/sri.py 
    

  • base.html
    Template 内での読み込み記述は以下になります。

    <!--[if lt IE 9]>
    <script src="{% static "js/html5shiv.js" %}" {% if settings.HTML5SHIV_SRI %}integrity="{{ settings.HTML5SHIV_SRI }}" crossorigin="anonymous"{% endif %} ></script>
    <script src="{% static "js/respond.min.js" %}" {% if settings.RESPOND_MIN_SRI %}integrity="{{ settings.RESPOND_MIN_SRI }}" crossorigin="anonymous"{% endif %} ></script>
    <![endif]-->
    
    {% if settings.BUNDLE_JS_SRI %}
        {% with 'async defer chatset="UTF-8" crossorigin="anonymous" integrity="'|add:settings.BUNDLE_JS_SRI|add:'"' as attrs_str %}
            {% render_bundle 'bundle' 'js' attrs=attrs_str %}
        {% endwith %}
    {% else %}
        {% render_bundle 'bundle' 'js' attrs='async defer chatset="UTF-8"' %}
    {% endif %}
    

  • default.py
    このBlog は、Mezzanine を使って構築していますが、Template 内で settings.py の値を使用する場合は、defalut.py で、対象の値を登録する必要があります。

    from mezzanine.conf import register_setting                                                                                               
    
    ###########################
    # FOR CLEAN_BLOG SETTINGS #
    ###########################
    register_setting(
        name="TEMPLATE_ACCESSIBLE_SETTINGS",
        description=("Sequence of setting names available within templates."),
        editable=False,
        default=("BUNDLE_JS_SRI",
                 "RESPOND_MIN_SRI",
                 "BUNDLE_CSS_SRI",
                 "HTML5SHIV_SRI"
                 ),
        append=True,
    )
    
    register_setting(
        name="BUNDLE_JS_SRI",
        description="",
        editable=False,
        default="",
    )
    register_setting(
        name="RESPOND_MIN_SRI",
        description="",
        editable=False,
        default="",
    )
    register_setting(
        name="BUNDLE_CSS_SRI",
        description="",
        editable=False,
        default="",
    )
    register_setting(
        name="HTML5SHIV_SRI",
        description="",
        editable=False,
        default="",
    )                                                                                                                                          
    


対策後 の、Observatory by Mozilla の 結果

以下対策後の結果になります。

Scan Summary

Score が 20/100 で 20点です。

項目名
Host:www.monotalk.xyz
Scan IDxxxxxxx
Start Timexxxxxxx
Duration5 seconds
Score20/100
Tests Passed8/11

Test Scores

TestScoreExplanation
Content Security Policy-25Content Security Policy (CSP) header not implemented
Cookies-5Anti-CSRF tokens set without using the SameSite flag
Cross-origin Resource Sharing0Content is not visible via cross-origin resource sharing (CORS) files or headers
HTTP Public Key Pinning0HTTP Public Key Pinning (HPKP) header not implemented (optional)
HTTP Strict Transport Security0HTTP Strict Transport Security (HSTS) header set to a minimum of six months (15768000)
Redirection0Does not redirect to an HTTPS site
Referrer Policy0Referrer-Policy header not implemented (optional)
Subresource Integrity-50Subresource Integrity (SRI) not implemented, and external scripts are loaded over HTTP or use protocol-relative URLs via src=”//…”
X-Content-Type-Options0X-Content-Type-Options header set to “nosniff”
X-Frame-Options0X-Frame-Options (XFO) header set to SAMEORIGIN or DENY
X-XSS-Protection0X-XSS-Protection header set to “1; mode=block”

Subresource Integrity の結果は、1st party の静的コンテンツに設定はしたものの、結果は変わりませんでした。
gtm.js 等は使わないわけにはいかないので、現状これくらいが限界なのかなと思いました。
以上です。

コメント