Edited at

Ruby/RailsのLoggerで任意コマンドを実行可能なので注意

More than 1 year has passed since last update.


TL;DR

ターミナル開いて以下叩くとlsが実行されます。

$ ruby -e 'require("logger"); Logger.new("| ls")'


概要

Ruby/Rails界隈でもevalや``(バッククウォート)は

ユーザからの入力値入れちゃったら大変だから気をつけろ〜的なのは散々言われていますが

Loggerで任意シェルコマンド実行できるのは知らなかったのでメモとして残しておきたかったのと

それに付随してrailsのconstantizeは危険な場合があるという話です


動作環境

Ruby 2.3.1

Rails 5.0.0


Loggerでのコマンド実行方法

TL;DRにも書きましたが、単にLogger.newの引数に|+好きなコマンドを入れるだけで実行できます

> require("logger")

> Logger.new("|pwd")


どういう時に危険か

The Safest Way to Constantize... (これ読んでLoggerでコマンド実行できること知りました)

この記事はRailsのconstantizeが危ないよという記事です。

constantizeとはレシーバの文字列をクラスとして返してくれるやつですね

> puts "Fixnum".class 

# => String
> puts "Fixnum".constantize
# => Fixnum

動的にクラスを引っ張ってくるので地味に便利なのですが

仮にユーザからの入力値に対してconstantizeした場合に気をつける必要があります。

例えば

class ContentsController < ApplicationController

# GET /contents
def index
obj = params[:type].constantize.new(params[:value])
end
end

なんていうコントローラがあった場合に/contents?type=Logger&value=|悪意のあるコマンド(実際にはエンコードされてるでしょう)を叩くとLogger.new("|悪意のあるコマンド")ができあがってしまい、いとも簡単に|以降のコマンドを実行してしまいます。

ではどうやってconstantizeを安全に使うかというのは先ほどの「The Safest Way to Constantize...」に載ってるのでそちらを参照して下さい。


まとめ

よくよく考えるとあんまこんなコード書かないかもしれませんが知ってるのと知ってないのとでは大違いなので頭の片隅にでも置いておくといいかもしれませんね