わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

プレースホルダのコード例をRubyで

前期は毎週金曜1限が授業.
pptファイルは,1年前のものを見直し手直しているのですが,先週金曜の授業では,プレースホルダのところを書き換えました.
情報セキュリティとプレースホルダ,といえばSQLインジェクションです.
あらかじめusersというテーブルにusernameとpasswordという属性が定義されていて,値がある程度入っているとします.セキュリティ対策を考えていないログインフォームで,ユーザ名にtakehiko,パスワードにabcdを入力すると,サーバ内部では

SELECT count(*) FROM users WHERE username='takehiko' AND password='abcd';

をクエリとしてDBMSに送り,0だったら「正しいユーザ名とパスワードではありません」とする,というのだと,ユーザ名にtakehiko,パスワードに『1' OR 'X'='X』を書いたら

SELECT count(*) FROM users WHERE username='takehiko' AND password='1' OR 'X'='X';

となってしまって,WHERE節のところが常に真となり,パスワードを知らなくても,成り済まし認証ができてしまう,というやつです.
プレースホルダを使って,RubyActiveRecordなら

result = User.find_by_sql ["SELECT count(*) FROM users WHERE username=? AND password=?", u, p]

と書くと,uに"takehiko",pに"1' OR 'X'='X"が代入されていたとしても,内部で生成されるSQL文は

SELECT count(*) FROM users WHERE username='takehiko' AND password='1\' OR \'X\'=\'X';

となって,passwordが『1' OR 'X'='X』と一致するものを求め,そんなのをパスワードにする人はいないのでめでたく拒絶できる,というものです.
授業でもそのように説明したのですが,さてではそのRubyの式で,ログインの可否を判定するのは,どうすればいいのか,授業準備のときに,少し悩みました.
小さなデータベースを作って試したところ,

result.first.count

を見れば,そこに「count(*)」の数,すなわち該当件数が格納されていることがわかりました.なお,結果が1レコードしかない場合でも,find_by_sqlの戻り値は配列ですので,「.first」は必須です.
SQL文の元*1の中のcountを大文字にして,

result = User.find_by_sql ["SELECT COUNT(*) FROM users WHERE username=? AND password=?", u, p]

としても,result.first.countで取得でき,result.first.COUNTはエラーです.
もう一つ,

result = User.find_by_sql ["SELECT count(*) AS c FROM users WHERE username=? AND password=?", u, p]

とすると,result.first.cから値を取り出せ,result.first.countはエラーになります.
find_by_sqlと別の方法で,個数を取り出すものとして,countとcount_by_sqlがあります.条件なしなら,

User.count

とするのが最も手軽で,これはusersテーブルの持つレコード数になります.
「User.count_by_sql "SQL文"」で条件を付けられますが,"SQL文" は文字列に限られ,

result = User.count_by_sql ["SELECT count(*) FROM users WHERE username=? AND password=?", u, p]

というのを試したもののエラーでした.
授業に話を戻しますと,授業自体でRubyのコード例を取り上げたのは,数年前のプログラミング演習で,Rubyで書かれた通信のクライアントプログラムを提示し,Cで同じ処理をするプログラムを作りなさいという課題を出して以来です.あれでRubyに拒否反応を示した学生が,研究室に入ったりしたものです.
Ruby」という言語自体は,自己紹介の際,研究に授業に裏方でRubyを活用していることを明かしていますし,1年生向けCプログラミング授業の初回にも,数あるプログラミング言語の一つとして挙げています.

*1:「用意済みSQL文」というのでしたっけ.