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を有効にしています。
<?php
// ...
$config['csrf_protection'] = true;
// ...
<?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に設定する事がセキュアでは
<!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が追加されたようです。