yumetodoの旅とプログラミングとかの記録

旅や登山の記録やプログラミング関連の話とかフリーソフト紹介とか

チケット購入サイトeplusのログインシステムのバグ

はじめに

知り合いに増矢馨子さんというピアニストの方がいる。

www.kaorukomasuya.com

  • Webページはどうやら更新されていない

で、今度、10/10に室内楽のコンサートがある。

web.archive.org

それのチケットがいつもと違ってチケットぴあではなくeplusだったので、会員登録をすることにしたのだ。

eplusのここがまずだめ

  • ログイン中かわからない
  • ログアウト方法がないor不明瞭
  • ログインしててもチケット購入のときはまたログインさせられる

eplus.jp

訪れればわかる、この糞of糞感。

本題

会員登録を無事に終え、チケット購入をしようとしたら、ログインを求められた、ログインしてるのに。でそれはとりあえずスルーして、ログインができない。

www.youtube.com

リアルキーボードクラッシャーやって、隣りにいた母親に怒られた。

原因

セキュリティをちょっとわかる勢C++erである私は当然KeePassでパスワードを自動生成した。しかしその自動生成したパスワードの長さが20文字だった。

eplus pass word change

パスワード登録画面には、

8文字以上16文字以下

という記述がある。

おかしい

いやちょっとまって、最初の登録の時に20文字ぶち込んでもなにもエラーにならなかったぞ、つまり入力フォームのValidationをしていない・・・?

検証したところ、登録時にいれた20文字の末尾が削られて16文字として登録されていた。

送信部分のソースコードを見てみる

幸いいかにもレガシー感漂うこのサイトのJavaScriptはminifyされていなかった。しかもHTML直書きだった。わぉ。

まずは登録フォームのほう。

$(document).ready(function() {
    // Validatorクラスのインスタンスを生成する。
    // 引数は入力チェック対象となるフォームのjQueryオブジェクト
    var validator = new Validator($('#registerMemberForm'));

    // 項目に必須属性を追加する
    var requiredFields = [];
    $('em.required').each(function() {
        if($(this).attr('epName')){
            var fields = $(this).attr('epName').split(",");
            $.each(fields,function(i,val){
                var field = $('input[name="' + val + '"]');
                if(field.length != 0){
                    requiredFields.push(field);
                }
                var field = $('select[name="' + val + '"]');
                if(field.length != 0){
                    requiredFields.push(field);
                }
            });
        }
    });

    validator.required(requiredFields);

    // ValidatorForHanyoKomokuインスタンス生成
    var validatorForHanyoKomoku = new ValidatorForHanyoKomoku("/api/v1/FTRegisterMember/hanyoKomokuCheck");

    // 確認ボタン押下
    $("#main-submit").on('click', function() {

        // 入力チェック前にアコーディオンが閉じられていた場合、入力チェック用にcss変更.非表示の場合にvalidation対象外になってしまうため
        var accordionChange = false;
        var detailsBox = $("#registerMemberForm .accordionBox .detailsBox");
        if(detailsBox.css('display') == 'none'){
            detailsBox.css('display','block');
            accordionChange = true;
        }

        // 入力チェックを実行する。
        var validError = validator.validate();
        var hanyoError = validatorForHanyoKomoku.validate();

        // cssを元に戻す
        if(accordionChange){
            detailsBox.css('display','none');

            // アコーディオン内に入力エラーがあった場合は開いて表示
            if(detailsBox.find(".errorCell").length != 0){
                accordionBoxChange(true);
            }
        }

        if(!validError || !hanyoError){
            window.scrollTo(0,dispErrorObj.offset().top);
            return false;
        }

        // 汎用項目のAJAX送信用パラメータを設定(リスト化された項目がパラメータ設定されないためここで設定.ajaxPostForFormではvalueListがString配列であることが特定できないためvalidateのjsに処理を記載)
        var param = {};
        param.soshikiHanyoKomokuSetList = validatorForHanyoKomoku.get();

        // 事前チェックでOKなら確認画面へ
        $.ajaxPostForForm(contextpath + "/api/v1/FTRegisterMember/updateCheck", $('#registerMemberForm'),param)
        .done(function(data, status, xhr, infoMessages, hasWarnings, warnMessages, hasErrors, errorMessages) {
            var res = $.parseJSON(xhr.responseText);
            if (res.isSuccess) {
                // 遷移する。
                $.doSubmit($("#registerMemberForm"), {
                    op : "startInitConfirmMemberInfo"
                });
            }
            else {
                // エラー時はダイアログ表示
                $.infoMsgBox(res.msgs[0].msg);
            }
        });
    });
    //後略
});

あれ、Validationあるじゃん。

次はパスワード変更ページ。

$(document).ready(function(){
    // 必須属性付与
    var validator = new Validator($('#updatePasswordForm'));
    validator.required([$('input[name="newPassword"]'), $('input[name="passwordConf"]')]);

    // 変更
    $('#update').click(function (e) {
        // 入力チェック
        if (!validator.validate()) {
            window.scrollTo(0,dispErrorObj.offset().top);
            return false;
        }
        $.doSubmit($("#updatePasswordForm"), {op : "startInitComplete"});
    });
});

あれ、Validationあるじゃん。ちなみに処理を追いきれなかったが、おそらく^[0-9a-zA-Z_\-@.]{8,16}$という正規表現で確認している。

しかし、会員登録ページのほうは16文字を超えても受け付けてしまう

結論

会員登録ページのValidationバグ

追記:これは仕様だと主張するeplus

お問い合わせフォーム投げつけてたんだが、メールが帰ってきた。

[Incident: 171007-000343]
本メールの一部または全文をSNS等へ掲載、二次利用する事はお断りいたします。

とか言ってるが、引用は妨げられないはずなので引用すると

[Incident: 171007-000343]
16文字を超えるパスワードがご入力できる状況となっております。 なお、こちらは弊社e+サイトでの仕様となり、障害等ではございません。

いや、だからその仕様がバグってるって言ってるんですがねぇ・・・。

ねちっこく、もう一度お問い合わせフォームに投げつけました。

追記:これは不正アクセス防止だと主張するeplus

メールが帰ってきた。

[Incident: 171021-000375]
パスワードにつきましては不正アクセス防止のため、 桁数を把握されないよう、16桁以上での設定を可能としています。

・・・はい?不正アクセス防止??

ちょっと私の知識では追いつけないのでぐぐったところ

upa-pc.blogspot.jp

がヒットした。

入力文字数が明記されていると、不正にログインをしようと考えている攻撃者にヒントを与えてしまいます。

やっぱり何を言っているのか分からねぇ。

eplus register password

そもそも登録フォームにはplaceholder="8文字以上16文字以下"とあって、明確にパスワードの長さを指定しているわけで、ヒントもクソもねーと思うんですが。

でよしんば書かれていなかったとして、不正アクセス防止効果はあるんでしょうか?
辞書攻撃にせよブルートフォース攻撃にせよ、長さの短いものから試行していくわけで、無意味ではないかと思ってしまうんですが(無知)

追記:と思ってたら @BlackWingCat 氏が教えてくれた。

追記: どうもValidationが有効になったぽい

追加検証は気力がない(大学の実験レポート終わらない)のでやりません。