6

Flask-Securityを使用すると、Python Flask Web アプリケーション開発の認証と承認に伴う面倒な作業が大幅に軽減されます。しかし、私は1つの障害に遭遇しました。

ログイン ページでは、さまざまな無効な入力に応答してメッセージが表示されます。例: - 指定されたユーザーが存在しない - 無効なパスワード - アカウントが無効になっている

これは、セキュリティのベスト プラクティスに従っていません。ログイン試行が拒否された理由の詳細をユーザーに漏らしてはなりません。上記のメッセージにより、ハッカーは有効なユーザー名を簡単に識別できます。

これらの標準的な Flask-Security メッセージをオーバーライドして、すべて「無効なユーザー名またはパスワード」などに置き換えたいと考えています。ただし、これを行う便利な方法は見つかりませんでした。

これらのメッセージは、site-packages/flask_security/core.py の _default_messages に保存されます。そのファイルを変更することはできますが、それは良い解決策ではありません。Flask-Security を再インストールまたは更新すると、ファイルが壊れてしまいます。

Flask-Security のデフォルト ビューをカスタマイズできることはわかっています。ただし、ビューには次のような役立つコードが含まれています

{{ render_field_with_errors(login_user_form.email) }}

ログインフォームの実装の詳細を非表示にします。いくつかのメッセージを変更するためだけに、役立つコードをすべて破棄して、そのほとんどを書き直すことはしたくありません。

Flask-Security のログイン メッセージをカスタマイズするより良い方法を知っている人はいますか?

4

3 に答える 3

6

レイチェルは部分的に正しいです (ところで、すべてのメッセージはフラスコ セキュリティの core.py にあります)。デフォルトのセキュリティ メッセージを変更することで、エラー メッセージの範囲を広げることができます。ただし、フィールドの標準レンダリングを使用していると仮定すると、問題の原因となったフォーム要素にエラー メッセージが添付されます。したがって、問題がユーザー名またはパスワードにあることを理解することは難しくありません。

私がしたこと:

  1. 構成ファイルのメッセージを広範なメッセージに変更します。以下のメッセージを変更しました。

    SECURITY_MSG_INVALID_PASSWORD = ("Bad username or password", "error")
    SECURITY_MSG_PASSWORD_NOT_PROVIDED = ("Bad username or password", "error")
    SECURITY_MSG_USER_DOES_NOT_EXIST = ("Bad username or password", "error")
    
  2. エラー メッセージなしでフィールドをレンダリングするマクロを使用しました ( render_field)。フラスコブートストラップを使用している場合、そのようなマクロは存在しないため、独自のマクロを作成しました (非常に簡単です。エラー ブロックと、フォーム要素に色を付けるクラスを削除するだけです)。以下は、flask-bootstrap からコピーして貼り付けただけで、フィールド エラー コードのみを削除したものです。

    {% macro bootstrap_form_field_no_errors(field,
                        form_type="basic",
                        horizontal_columns=('lg', 2, 10),
                        button_map={}) %}
    {% if field.widget.input_type == 'checkbox' %}
      {% call _hz_form_wrap(horizontal_columns, form_type, True) %}
        <div class="checkbox">
          <label>
            {{field()|safe}} {{field.label.text|safe}}
          </label>
        </div>
      {% endcall %}
    {%- elif field.type == 'RadioField' -%}
      {# note: A cleaner solution would be rendering depending on the widget,
         this is just a hack for now, until I can think of something better #}
      {% call _hz_form_wrap(horizontal_columns, form_type, True) %}
        {% for item in field -%}
          <div class="radio">
            <label>
              {{item|safe}} {{item.label.text|safe}}
            </label>
          </div>
        {% endfor %}
      {% endcall %}
    {%- elif field.type == 'SubmitField' -%}
      {# note: same issue as above - should check widget, not field type #}
      {% call _hz_form_wrap(horizontal_columns, form_type, True) %}
        {{field(class='btn btn-%s' % button_map.get(field.name, 'default'))}}
      {% endcall %}
    {%- elif field.type == 'FormField' -%}
    {# note: FormFields are tricky to get right and complex setups requiring
       these are probably beyond the scope of what this macro tries to do.
       the code below ensures that things don't break horribly if we run into
       one, but does not try too hard to get things pretty. #}
      <fieldset>
        <legend>{{field.label}}</legend>
        {%- for subfield in field %}
          {% if not bootstrap_is_hidden_field(subfield) -%}
            {{ form_field(subfield,
                          form_type=form_type,
                          horizontal_columns=horizontal_columns,
                          button_map=button_map) }}
          {%- endif %}
        {%- endfor %}
      </fieldset>
    {% else -%}
      <div class="form-group">
          {%- if form_type == "inline" %}
            {{field.label(class="sr-only")|safe}}
            {{field(class="form-control", placeholder=field.description, **kwargs)|safe}}
          {% elif form_type == "horizontal" %}
            {{field.label(class="control-label " + (
              " col-%s-%s" % horizontal_columns[0:2]
            ))|safe}}
            <div class=" col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}">
              {{field(class="form-control", **kwargs)|safe}}
            </div>
            {%- if field.description -%}
              {% call _hz_form_wrap(horizontal_columns, form_type) %}
                <p class="help-block">{{field.description|safe}}</p>
              {% endcall %}
            {%- endif %}
          {%- else -%}
            {{field.label(class="control-label")|safe}}
            {{field(class="form-control", **kwargs)|safe}}
    
            {%- if field.errors %}
              {%- for error in field.errors %}
                <p class="help-block">{{error}}</p>
              {%- endfor %}
            {%- elif field.description -%}
              <p class="help-block">{{field.description|safe}}</p>
            {%- endif %}
          {%- endif %}
      </div>
    {% endif %}
    {% endmacro %}
    
  3. すべてのフィールドのすべてのエラーを表示する新しいマクロを作成し、フォームの上部に配置しました。複数のエラーが同時に生成されることは見たことがありませんが、どのフィールドがエラーを引き起こしたのかはわかりません。繰り返しますが、私のコードはフラスコ ブートストラップ スタイルと一致しますが、ブートストラップ固有の要素は簡単に削除できます。

    {% macro fields_errors() %}
    {% for field in varargs %}
    {% if field.errors %}
      {% for error in field.errors %}
        {% call _hz_form_wrap(horizontal_columns, form_type) %}
          <div class="alert alert-danger">{{error}}</div>
        {% endcall %}
      {% endfor %}
    {% endif %}
    {% endfor %}
    {% endmacro %}
    

フォーム自体では、すべてのフォーム フィールドでこのマクロを呼び出します。

    {{ fields_errors(login_user_form.email, login_user_form.password, login_user_form.remember) }}`
于 2014-04-19T05:15:11.987 に答える
5

コードを読むと、すべてのメッセージに構成のデフォルトが設定されているように見えます。

_default_messages = {
    'INVALID_PASSWORD': ('Invalid password', 'error'),
}

... later on in the init method ....

for key, value in _default_messages.items():
        app.config.setdefault('SECURITY_MSG_' + key, value)

メッセージを変更するには、app.config で次のように設定するだけです。

SECURITY_MSG_INVALID_PASSWORD = ('Your username and password do not match our records', 'error'),

そうでない場合は、モジュールをリファクタリングしてbabelなどを使用することは難しくないようです。良きFOSS市民として素晴らしいことです。

于 2014-04-18T21:03:06.350 に答える