LoginSignup
13
10

More than 1 year has passed since last update.

CodeIgniter3のSecurityクラスのCSRF対策を把握しておく

Last updated at Posted at 2016-12-08

CodeIgniter3のSecurityクラスのCSRF対策を把握しておく

CodeIgniterのCSRF対策がCookieで管理していたので、Sessionじゃないとダメじゃないか?
と思って勢いで書いてみたけど理にかなっていました。
この記事には嘘が記載されている可能性があります。
追記: Cookieの改ざんがありえる以上、安全でないかもしれません。(CodeIgniter4.1.5以前は)
https://togetter.com/li/316198
https://twitter.com/ockeghem/status/879876546034520065
https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html

確認したCodeIgniter

CodeIgniter3 origin/3.1-stable 5d6e77b092ca8f1700a7407bf59bcab6b0e30808

準備

実際にCodeIgniter3で以下の様に実装してCSRFを有効にしています。

config/config.php
<?php
// ...
$config['csrf_protection'] = true;
// ...
controllers/Csrfcheck.php
<?php
class Csrfcheck extends CI_Controller {

    public function attack()
    {
        if ( $this->input->method(TRUE) !== 'POST' )
        {
            show_404();
        }
        echo 'Success!!';
    }
}

検証

この状態でCURLからPOSTリクエストを行ってもエラーが発生します。

$ curl -F "csrf_test_name=value1" http://localhost/csrfcheck/attack

しかし、CodeIgniterのSecurityクラスではCookieの値とPOSTの値が同一であるかを判定しているのでCookieに同一の値を付与するとリクエストが通ってしまいます。

$ curl -F "csrf_test_name=value1" -b "csrf_cookie_name=value1"  http://localhost/csrfcheck/attack
Success!!

これは危険ではないか!?

Cookie変更できる脆弱性があった場合にこれではcsrf_cookie_nameを書き換えて、csrf_test_nameを同一のPOST値を入れられて危ないのではないかと考えましたが、

Cookieの値は基本的にクロスドメインでの読み書きはできないためこの様な実装になっているものかと思います。

そのため、CodeIgniterの実装のようにCookieでのCSRF対策は理にかなってる実装だなと思い勉強になりました。

Cookieであることの便利さ

例えばCSRF有効にしているページでAjaxリクエストを発生させなければいけない時に、PHP側で都度Ajaxのレスポンスで$this->security->get_csrf_hash();の値を含ませずともCookieからとれるのでコードがすっきりします。

ただし、JSで書き換える事ができるcookie_httponlyをTRUEに設定する事がセキュアでは

views/sample.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
    <a href="">Ajaxリクエストのトリガー</a>
    <?php echo form_open('welcome/csrfcheck/');?>
        <input type="text" name="request">
    <input type="submit">
    <?php echo form_close();?>
    <script>
        var hidden = $('input[name="<?php echo config_item('csrf_token_name');?>"]');
        $('a:first').click(function(ev) {
            ev.preventDefault();
            $.ajax({
                url: '<?php echo site_url('csrfcheck/ajax_api')?>',
                method: 'post',
                                // cookieからcsrfの値を取得
                data: {'csrf_test_name': getCsrfCookie()}
            }).done(function() {
                hidden.val(getCsrfCookie());
            });
        });

        /**
         * cookieからcsrfのトークンの値を取得する関数
         **/
        function getCsrfCookie(){
            var cookies = document.cookie.split("; ");
            for (var i = 0; i < cookies.length; i++) {
                var key, value;
                [key, value] = cookies[i].split("=");
                if (key !== '<?php echo config_item('csrf_cookie_name');?>') {
                    continue;
                }
                return value;
            }
        }
    </script>
</body>
</html>

まとめ

CodeIgniterのCSRFはCookieとPOST値を比較していますが安全です。
追記: ただし、Cookieの改ざんがありえる以上、安全でないかもしれません。
https://togetter.com/li/316198
https://twitter.com/ockeghem/status/879876546034520065
https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html

また、現状ではCodeIgniter4のCSRF Filterも同じ様に実装されています。

参考:https://github.com/bcit-ci/CodeIgniter/blob/3.1-stable/system/core/Security.php

コメントありがとうございます。
CodeIgniter 4.1.5でsessionによるCSRFが追加されたようです。

13
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10