ActixWeb にパッチを送った話

公開:2020/09/17
更新:2020/09/17
4 min読了の目安(約2600字TECH技術記事

はじめに

先日、ActixWeb にパッチを送りました。とは言っても本体ではなく examples の中に格納されている todo アプリの話。

https://github.com/actix/examples/pull/366

本記事では僕がなぜこのパッチを送る事になったのか、どうやってこの todo アプリのバグを見つけたのか、を紹介したいと思います。技術的な話はほぼありません。

どうやって見つけたのか

僕はウェブアプリを試す時に そのアプリが信用できる物かどうか を確認する意味で <script>alert(1)</script> といった XSS なコードを入力ボックスに埋め込む事をよくやります。悪意はありません。これは自衛です。おそらくエンジニアの方であれば皆やると思います。(え、やらない、そうですか)

とは言っても最近はテンプレートエンジンを使うのが当たり前になってきていて、そのまま alert が表示される事はほぼなくなりました。そして最近では XSS を作ってしまう方が恥ずかしいレベルになってきています。そんな中、先日試した ActixWeb の examples/todo は見事に alert が表示されてしまったのです。

しかしソースコードを見ても Rust のテンプレートエンジン Tera をちゃんと使っている。まさか最近のテンプレートエンジンでデフォルトで HTML をエスケープしない物なんかある訳ないだろうと思い一旦は「もしかしてこれヤバい物を見つけてしまったんじゃないか」とすら考えました。

なにが問題だったのか

ここで Tera がどうやって HTML をエスケープしているかを見てみましょう。Tera がテンプレートエンジンの中で値を評価しているのは src/renderer/processor.rs の eval_expression です。

        if self.should_escape && needs_escape && res.is_string() && !expr.is_marked_safe() {
            res = Cow::Owned(
                to_value(self.tera.get_escape_fn()(res.as_str().unwrap())).map_err(Error::json)?,
            );
        }

この should_escapeProcessor のコンストラクタ new渡されます。そしてこの should_escapeautoescape_suffix から作られます。
autoescape_suffixデフォルト値.html.htm.xml です。おや、ドットが含まれていますね。そう、Tera はファイルタイプではなく拡張子で autoescape_on を指定するのです。たしか todo アプリのソースで使われているテンプレートファイルの拡張子は .html.tera でした。

https://github.com/actix/examples/blob/a1f2f9514e04f5872d89e7d52e5e59dfa4ae01fd/todo/templates/index.html.tera

となれば .tera もエスケープ対象にしないといけませんよね。

修正した内容

本来、Tera の思想は拡張子 .html なファイルを扱うので、autoescape_on を使って拡張子 .tera をエスケープ対象に設定してやります。

https://github.com/actix/examples/pull/366/files#diff-571e364f06e9977e9b8dc3d0a0a02382

これでようやく XSS せずに入力される様になりました。

ところで

Tera は自動でエスケープすると思っておられる方が結構いる様で GitHub には .html.tera というファイル名を使っておられる方がたくさんおられます。
ぜひお気を付けください。そして autoescape_on を使う様に、もしくはテンプレートの中で {{ value | escape }} を使うよう、教えてあげて下さい。

さもないと alert(1) が表示されてしまいますよ。