web-dev-qa-db-ja.com

railsでユーザー入力をサニタイズする最善の方法

私はこれについて多くのことを読み、ここに関連する質問がたくさんあることを知っていますが、すべてをサニタイズする方法についての決定的なガイドを見つけることができませんでした。 1つのオプションは、挿入時にサニタイズすることです。たとえば、モデルに次のものがあります

before_validation :sanitize_content, :on => :create
def sanitize_content
  self.content = ActionController::Base.helpers.sanitize(self.content)
end

すべてのモデルのすべてのフィールドでこれを実行する必要がありますか? :on =>:createも削除する必要があるので、更新時にも実行されると思いますか?

もう1つのオプションは、simple_format、または.html_safeまたはsanitize(fieldname)を使用して、データがビューに表示されるときにサニタイズすることです。挿入だけでなく、すべてのフィールドのすべてのビューでサニタイズする必要がありますか?どこでも手動でこれを行わなければならないことは非常に面倒ではないようです

助けてくれてありがとう

27
Dave

TL; DR
ユーザー入力とクエリについて:アクティブレコードクエリメソッド(_.where_など)を常に使用するようにしてください。文字列補間を使用してパラメーターを渡すことを避けます。それらをハッシュパラメーター値として、またはパラメーター化されたステートメントとして渡します。

安全ではない可能性のあるユーザー生成html/javascriptコンテンツのレンダリングについて Rails 3として、html/javascriptテキストは自動的に適切にエスケープされ、 html/javascriptとして解釈されるのではなく、ページ上のプレーンテキストなので、明示的にサニタイズする必要はありません(または<%= h(potentially_unsafe_user_generated_content)%>を使用します)

私があなたを正しく理解していれば、アクティブレコードクエリメソッドを正しく使用する限り、この方法でデータをサニタイズすることを心配する必要はありません。例えば:

悪意のあるユーザーが_user_name_フィールドに次の文字列を入力した結果、パラメーターマップが次のようになっているとします。

_:user_name => "(select user_name from users limit 1)"
_

悪い方法(これをしないでください):

_Users.where("user_name = #{params[:id}") # string interpolation is bad here
_

結果のクエリは次のようになります。

_SELECT `users`.* FROM `users` WHERE (user_name = (select user_name from users limit 1))
_

この方法で文字列を直接補間すると、キー_:user_name_を持つパラメーター値のリテラルコンテンツがサニタイズせずにクエリに配置されます。おそらくご存知のように、悪意のあるユーザーの入力は単純な「SQL」として扱われ、その危険性は明らかです。

良い方法(これを行う):

_Users.where(id: params[:id]) # hash parameters
_

[〜#〜]または[〜#〜]

_Users.where("id = ?", params[:id]) # parameterized statement
_

結果のクエリは次のようになります。

_SELECT `users`.* FROM `users` WHERE user_name = '(select user_name from users limit 1)'
_

ご覧のとおり、Railsは実際には、パラメーターをハッシュまたはメソッドパラメーター(使用しているクエリメソッドに応じて)として渡す限り、サニタイズします。

newまたはcreateメソッドは値のハッシュを予期しているため、新しいモデルレコードの作成に関するデータのサニタイズのケースは実際には適用されません。安全でないSQLコードをハッシュに挿入しようとしても、ハッシュの値はプレーンストリングとして扱われます。次に例を示します。

_User.create(:user_name=>"bobby tables); drop table users;")
_

クエリの結果:

_INSERT INTO `users` (`user_name`) VALUES ('bobby tables); drop table users;')
_

したがって、上記と同じ状況。

それがお役に立てば幸いです。私が何かを見逃したか、誤解したかどうかを教えてください。

編集 htmlとjavascriptのエスケープに関して、短いバージョンは、ERBが文字列コンテンツを「エスケープ」して、プレーンテキストとして扱われるようにすることです。 can本当に必要な場合は、_your_string_content.html_safe_を実行して、htmlのように処理します。

ただし、単に_<%= your_string_content %>_のようなことを行うだけで完全に安全です。コンテンツはページ上の文字列として扱われます。実際、Chrome Developer ToolsまたはFirebugを使用してDOMを調べると、実際にその文字列を引用符で囲む必要があります。

68
Paul Richter

SOの答えについての知識とコードのソースを見つけたときはいつも感謝しているので、この質問に対してそれを提供します。

ActiveRecordとActionControllerの両方が、SQL入力をサニタイズするメソッドを提供します。

特にActiveRecord::Sanitization::ClassMethodsには、sanitize_sql_for_conditionsとその他の2つのエイリアスがあります。sanitize_conditionsおよびsanitize_sql。 3つは文字通りまったく同じことを行います。

sanitize_sql_for_conditions

SQL条件の配列、ハッシュ、またはストリングを受け入れ、それらを有効なSQLフラグメントにサニタイズしますfor WHERE句

ただし、 ActiveRecord には

sanitize_sql_for_assignmentどれ

SQL条件の配列、ハッシュ、または文字列を受け入れ、それらを有効なSQLフラグメントにサニタイズしますSET句の場合

  • これらのメソッドはActiveRecord :: BaseとしたがってActiveRecordモデルにはデフォルトで含まれていますに含まれていることに注意してください。

一方、 ActionController では、ActionController::Parameters

大量更新のためにどの属性をホワイトリストに登録するかを選択し、公開すべきでない属性を誤って公開しないようにします。この目的で2つのメソッドを提供します:requireおよびpermit

params = ActionController::Parameters.new(user: { name: 'Bryan', age: 21 })
req  = params.require(:user) # will throw exception if user not present
opt  = params.permit(:name)  # name parameter is optional, returns nil if not present
user = params.require(:user).permit(:name, :age) # user hash is required while `name` and `age` keys are optional

パラメーターマジックは、Strong Parameters、 docs here と呼ばれます。

Railsを学び、分かりやすくするためだけに、それが誰にとっても役立つことを願っています! :)

11
Bryan Dimas