Ruby で bash の特殊文字をエスケープする
本日は Ruby のちょっとしたユーティリティー関数のご紹介です。
Web アプリケーションを作っていると、ときには入力されたデータを引き渡して外部コマンドを実行したいときがあると思います。そのときに重要なのが、特殊記号のエスケープです。これを忘れると、 Web サーバー上で任意コマンドを実行されてしまう可能性もあります。
そこで、 bash が特殊文字として認識する文字をエスケープする Ruby の関数を作ってみました。信頼できない文字列を外部コマンドに渡す際は、ぜひご活用ください。ついでなので、先頭のハイフン(オプションとして解釈されてしまうおそれがある)やコントロールキャラクタ(空白・タブを除く)を削除する機能も付けています。
ちなみに、エスケープが必要な記号は、すべての記号を名前に含むファイルを作って、 bash にタブ補完させて調べました。まあ、これなら間違いはないでしょう(笑)。
それでは、さっそくソースコードです。
def shellesc(str, opt = {})
str = str.dup
if opt[:erace]
opt[:erace] = [opt[:erace]] unless Array === opt[:erace]
opt[:erace].each do |i|
case i
when :ctrl then str.gsub!(/[\x00-\x08\x0a-\x1f\x7f]/, '')
when :hyphen then str.gsub!(/^-+/, '')
else str.gsub!(i, '')
end
end
end
str.gsub!(/[\!\"\$\&\'\(\)\*\,\:\;\<\=\>\?\[\\\]\^\`\{\|\}\t ]/, '\\\\\\&')
str
end
# 呼び出し例
shellesc('; rm *') # => '\;\ rm\ \*'
shellesc('--force', :erace => [:hyphen]) # => 'force'
shellesc("abc\ndef", :erace => [:ctrl]) # => 'abcdef'
shellesc("abc", :erace => [/b/]) # => 'ac'
ソース後半の呼び出し例を見ていただければ使い方は一目瞭然かと思います。基本的に第一引数にエスケープ前の文字列を渡して呼び出せば、エスケープ後の文字列を返します。
第二引数はオプションの Hash ですが、現在は :escape しか指定できません。 :escape は削除する文字の指定で、以下の値を配列で複数指定できます。
- :ctrl
- 空白、タブ以外の非表示文字を削除します。
- :hyphen
- 先頭のハイフンを削除します。
- 正規表現・文字列
- マッチする文字列を削除します。
使い方はこれだけです。簡単ですね。
なお、これでエスケープした文字列をダブルクォートなどで囲うと、二重エスケープ(エスケープ用のバックスラッシュがゴミとして残る)になってしまうのでご注意ください。逆に、ダブルクォートで囲うことを前提に $, ¥, ` のみをエスケープするという手もあるのですが、その出力をさらにダブルクォートで囲うとエスケープが剥がれて危険なので止めました。
一応、これだけやれば十分かなと思っているのですが、もし問題点などお気づきでしたら、ぜひご指摘ください。可能な限り速やかに修正したいと思います。
ではでは、本日はこれにて。
詳しくはこちらの記事をどうぞ!

この記事にコメントする