FuelPHPのUri::base()は何を返すのか?

少し前のQuiitaのこの記事。

Uri::base(false)でhttp://xxxx.jp/を取得できなかったとあります。

気になったので、FuelPHP 1.7.2をインストールして検証してみます。

class Controller_Welcome extends Controller
{
    public function action_index()
    {
        return Uri::base();
    }
}

結果:http://localhost:8000/

ということで、http://xxxx.jp/部分を取得できました。

では、どういう場合に、取得できなくなるのでしょうか?

config/config.phpbase_urlの設定を変更します。

'base_url'  => 'http://xxxx.jp/',

結果:http://xxxx.jp/

この場合も取得できました。

'base_url'  => 'test/',

結果:test/

このケースではhttp://から始まるURLは取得できません。

まとめ

Uri::base()base_urlの値を返しますが、base_urlは設定値がnullの場合(デフォルト)、自動的に検知されます。

config/config.php

     * Set this to null to have it automatically detected.
     */
    // 'base_url'  => null,

参考

Tags: fuelphp

FirefoxだとAjaxでのCSRFができなくなってる?

(2015-03-20 追記) 【訂正】 Firefox 36でAjaxを利用したCSRF攻撃が可能なことを確認しました。この記事で攻撃できないとしていたのは、Firefoxのアドオンが防いでいたようです。

Ajaxを利用したCSRF攻撃を検証していたのですが、どうも手許のFirefox(34.0)からだと攻撃が成功しません。 なんということでしょう! いや、攻撃できない方がいいんですが。 あと、以前のFirefoxだと攻撃できたかどうかは記録がないので不明だったりしますが、たぶんできたような気はしてます...

検証方法

攻撃対象ページと罠ページを作成します。

攻撃対象ページ

まず、リクエストを受ける攻撃対象ページを作成します。

POSTメソッドでリクエストされた場合に、ログに記録するだけです。もちろんなんのCSRF対策もしません。

GETされた場合は、確認のためにログを表示するようにします。

<?php

$log = __DIR__ . '/post.log';

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $lines = file($log);
    $lines = array_reverse($lines);
    echo '<pre>' . "\n";
    foreach ($lines as $line) {
        echo htmlspecialchars($line);
    }
    echo '</pre>' . "\n";
} else {
    $data = $_POST;
    $data['time'] = date(DATE_ATOM);
    $data['user_agent'] = $_SERVER['HTTP_USER_AGENT'];

    $line = sprintf('[%s] "%s" "%s"' . "\n", $data['time'], $data['comment'], $data['user_agent']);
    file_put_contents($log, $line, LOCK_EX | FILE_APPEND);
}

これで、攻撃対象ページは完成です。

罠ページ

次に、罠ページを作成します。攻撃したい人をこの罠ページにアクセスさせれば、CSRFによる攻撃が実行されます。

jQueryで自動的にAjaxでPOSTするようにします。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>CSRF attack via Ajax</title>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
        <script>
            $(function () {
                function post() {
                    $.ajaxSetup({
                        crossDomain: true,
                        xhrFields: {
                            withCredentials: true
                        }
                    });

                    $.post(
                        'https://csrf-ajax-target.herokuapp.com/',
                        {comment: 'This is CSRF attack via Ajax'}
                    )
                }

                post();
            });
        </script>
    </head>

    <body>
        <p>CSRF attack via Ajax</p>
        <p><?php echo date(DATE_ATOM); ?></p>
    </body>
</html>

これで、完了です。面倒なので、攻撃したとかそういう親切な表示はありません。現在時刻だけ表示してます。

試したい人へ

(2015-03-23 追記) サンプルサイトを変更しました。

すぐに試せるように、罠ページをhttp://csrf-trap.herokuapp.com/ajax-post/に作成しました。

ということで、攻撃されてみたい人はhttp://csrf-trap.herokuapp.com/ajax-post/にアクセスし、その後、「See results.」のリンクをクリックしてください。

攻撃が成功していれば、1行目に「CSRF attack was succeeded.」、2行目にCSRFでPOSTしたメッセージ「This is CSRF attack via Ajax」が表示されます。

結果

攻撃成功

  • Firefox 36.0 (Linux)(2015-03-20 追記)
  • Chrome/39.0.2171.65 (Linux)
  • Opera/9.80 (Linux)

攻撃失敗

  • Firefox 34.0 (Linux)

関連

Tags: ajax, csrf, firefox

FuelPHPのcsrf_autoloadはtrueに設定しよう!

FuelPHPには、security.csrf_autoloadという設定があります。この設定は、デフォルトはfalseです。

何が言いたいのか?

この設定をtrueにしよう!ということが言いたいわけです。

security.csrf_autoloadtrueにすると、POST、PUT、DELETEメソッドでのすべてのリクエストで自動的にCSRF保護のトークンチェックが行われるようになります。

trueにするとどうなるのか?

トークンがなかったり、マッチしない場合は、以下のエラー(例外)になります。

Fuel\Core\SecurityException [ Error ]:
CSRF validation failed, Possible hacking attempt detected!

つまり、自動チェックにすればCSRF対策の漏れがなくなります(GETで重要な処理をするような行儀の悪いアプリの場合はダメですが)。

ただし、フォームなどへのトークンの埋め込みは自分でやる必要があります。 まあ、自動チェックでないCSRF対策の場合と同じです。

Fieldsetクラスを使っている場合は、

$fieldset->form()->add_csrf();

とすればOKです。

弊害としては、本来CSRF保護が必要ないフォームなどでも必ずトークンを付ける必要があるということと、もしCSRF保護をしたくない処理があった場合でも特定の処理だけオフにすることができないことです。

この自動チェックはFuelPHP起動のかなり初期に(Securityクラスがロードされたときに)実行されるため、イベントで特定のページだけ設定値を変更するようなこともできません。

どうしても一部だけオフにしたい場合は、Coreに手を入れるしかないように思います。

なんでこんなことを言い出したのか?

CSRF対策が漏れていたり、まったくないアプリが結構世の中にあることがわかったからです。どうも私が思っていたより、この世界ではCSRF対策がされていないようです。

フレームワークにCSRF保護の仕組みがあっても(ほとんどのフレームワークにあると思います)、そもそもまったく使ってないようなアプリもGitHubを探せばいろいろと出てくるようです。

あと、自分でアプリを完全に管理して、重要アクションを完全に把握して、必要な処理にCSRF対策を入れるという場合以外では、やっぱり漏れや忘れが起こりそうです。

ということで、今はCSRF保護をデフォルト(自動)にする方がよいと思っています。

関連

Tags: fuelphp, csrf