見出し画像

KusoKoudoKaigi 3 で私が提出したコードの解説

KusoKoudoKaigi って?

KusoKoudoKaigiとは、珠響そうきさんが主催している、「クソコード」を公募してそのなかから1位を決めるというイベントです。私は第1回で優勝し、第2回では審査員を務めました。

第3回の配信の様子は以下になります。

この記事では、私が提出したWebアプリについての解説を行います。

作成したもの

今回作成したものは、ImageMagickを用いて画像を指定したサイズに縮小したものをダウンロードできる簡単なWebアプリです。(予告なく停止します)

ソースコードはここで公開しています。

使用した技術は、主にRuby, Vue.js, ImageMagick, Docker, Cloud Run GitHub Packagesです

何が「クソ」なのか

このWebアプリには、様々なダメなポイントが含まれています。些細なものから重大なものまで、以下に列挙しました。

Vue.jsがdevelopment buildのままになっている
.dockerignore や .gitignore の指定がない
得体の知れないDocker imageをFROMに指定している
脆弱性の含まれているmiddlewareを使用してしまっている
入力値のvalidationを行っていない

これらについて順に解説をしていきます。

Vue.jsがdevelopment buildのままになっている

まずはこのWebアプリのhtmlを眺めてみます。すると、<script> でVue.jsをCDNからダウンロードしているのがわかります。

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>

ここで読み込まれているVue.jsは開発用にbuildされたものです。開発用のため、警告の出力やデバッグモードが有効になっていますが、そのぶんファイルサイズが大きいものになっています。

即席で作ったWebアプリではありますが、本番で公開する場合には以下のようにminify buildされたものを使用するのが一般的ですね。無駄なデータのダウンロードが発生してしまいます。動作の遅さにも繋がるので、本番用のものを使いましょう。

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>

ちなみに、 vue.js は334KBで、 vue.min.js は 91KBでした。およそ3倍のファイルサイズ差があるのですね。

.dockerignore や .gitignore の指定がない

GitHubリポジトリそのものを見てみると、.gitignore や、Dockerfileがあるのに .dockerignore がありません。
.gitignore が存在していない場合、本来バージョン管理したくないもの、秘匿したいものを誤ってgitリポジトリに含めてしまったり、それをGitHubで公開してしまったりしてしまいます。

.dockerignore が存在しないということは、Dockerfile内にリポジトリ内に含まれるコードを全て COPY するような記述があれば .git/ ディレクトリがDocker imageに含まれてしまい、無駄に大きなimageが作成されてしまいます。サイズの大きなimageは、その分Deployの時にimageをダウンロードする時間に繋がります。

そして実際にDockerfile内では "COPY . ." を行っているので、これはimageサイズの肥大化に繋がります。

得体の知れないDocker imageをFROMに指定している

先ほどの記述からわかるように、このWebアプリはDocker containerとして動作しています。

Dockerでは、言語の実行環境などをまとめてある別のDocker imageを土台にして自分の変更を加えていくことができ、それはDockerfile内における "FROM" という命令に目的のDocker imageを指定することで可能です。

便利な反面、素性のよくわからないDocker imageには悪意のあるプログラムが含まれている可能性もあり、実際過去に仮想通貨をマイニングするプログラムが含まれたDocker imageが公開されていた事件もありました。

FROM docker.pkg.github.com/haruelico/imagemagick-docker/imagemagick:6-latest

そしてこのDockerfileでは、なにやら得体の知れないimageをFROMとして指定していることがわかります。

(得体の知れない、と言っても私の作成したimageであることは一目瞭然ですが、それはそれ。)

このDocker imageには何が含まれているのでしょうか?GitHubにて公開されていることが予想できるので、探してみましょう。

これがそのDockerfileですね。

なぜImageMagickを自前でbuildしているのでしょうか。一般的にはImageMagickはOSのパッケージマネージャーからインストールするものですが、何かおかしなことをしているように見えます。特に5行目の "git checkout" は怪しいです。

ここで、次の項目になるのですが…

脆弱性の含まれているmiddlewareを使用してしまっている

" && git checkout 787f16384 \" はいかにも怪しいです。これにより、意図的にImageMacigkの6.9.3-8をbuildしています。

画像1

この時点で、 "6-latest" というtag (Docker imageにつけるversionのようなもの) が全くの嘘であることもわかります。

そして、ImageMagickの6系における6.9.3-9以前のバージョンには、「ImageTragick」と呼ばれる有名な脆弱性が存在しています。

なので、例えば次のような内容のmvgファイルを変換しようとすると、

push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|cat "/etc/passwd)'
pop graphic-context

画像2

このように、サーバーのログに "/etc/passwd" の内容が出力されてしまいます。このWebアプリはDocker container内で動作しているので特に影響はありませんが、脆弱性を含むミドルウェアを使用することはやってはいけません。

自前buildなどではなく、OSのパッケージマネージャからインストールすれば、脆弱性対策がされたものが使用できるので、それを使用すべきです。

また、普段から使用しているソフトウェアの脆弱性情報はこまめに確認しておくべきです。

参考リンク

入力値のvalidationを行っていない

そして最後に特大のものを。

Webアプリの、サーバーサイドの実態である "server.rb" のコードを見てみましょう。18行目で、requestに含まれているパラメータをそのまま文字列として展開し、shellで実行しています。

puts `convert -geometry #{request_body['size']} image.#{ext} out.png`

これは非常に危険なコードです。フロントでは数値しか入力できないようになっていますが、見ればわかるようにバリデーションは一切行われていません。仮に、"size"の値として "; echo \"HELLO HELLO\";" を指定しみるとどうなるでしょうか?

画像3

画像4

このように、サーバー側で 'echo "HELLO HELLO"' というコマンドが実行できてしまいます!

一番簡単な対応としては、 "String#to_i" を使用して文字列から数値に変換することです。

"200".to_i
# => 200
"echo 'HELLO HELLO'".to_i
# => 0

ユーザーからの入力値をやみくもに信用です、正しく検証することは、安全なWebアプリの作成には欠かせません。

また、ImageMagickの機能を利用するのに、shellから実行するのではなく、RMagickなどのプログラミング言語に合わせて作成されたライブラリを使用することも、このようなshell injectionの対策になるでしょう。

参考リンク

感想

今回、提出にあたって「やってはいけないことをやる」ということを考えたときに思い浮かんだのは、「脆弱性を混入させる」というものでした。そこで何ができるかの案を色々出し、ImageMagickの脆弱性を利用することを思い付きました。実装の過程でそれどころではない脆弱性を埋め込むことになりましたけれど。

見た目には普通に画像を縮小するWebアプリなので、地味で優勝もできないだろうと踏んでいたのですが結果優勝となり、2冠達成、とても嬉しいです。

この記事が気に入ったらサポートをしてみませんか?