スマホアプリのプロキシ型チート実践

# はじめに
本投稿の内容を実際に行うと犯罪行為になります。本投稿は、以下を目的とします。

* アプリ開発者向けにプロキシ型のチートがいかに容易く行えるかをしめす
* アプリ開発時に有用な `mitmproxy` というツールを紹介する（どう有用かは後程）
* 概念ではわかるチート（の一例）を具体的なコードまで落とし込む

本投稿を理解するのには、ある程度の知識が必要なのでチータの人は見ても無駄だと思います。

# 概略
HTTP(S)通信を行うアプリと、サーバの間に自前のプロキシサーバを立て、HTTP Request 及び HTTP Response を書き換えます。これにより、アプリを騙したり、サーバを騙したりすることが可能となります。

例えば、アプリからサーバへのRequest を書き換えることでサーバを騙して以下が行える場合があります。

* 実際にはドロップしないレアユニットを落としたと思い込ませる
* 実際には実現しなかった高スコアを叩き出したと思い込ませる
* などなど

逆に、サーバからアプリへのResponse を書き換えることでアプリを騙して以下が行える場合があります。

* ボスの HP が 1 となるような簡単なステージにしてしまう
* まだたどり着いていない隠しステージにしてしまう
* などなど

プロキシには、python で書かれている `mitmproxy` というツールを使います。Windows では動かないので、Linux か Mac を使います。

```
$ mitmproxy -s cheat.py
```

とすると起動して、 `cheat.py` で定義した書き換えルールに従って、Request/Responseを書き換えるプロキシがポート8080番で立ち上がります。

デバイスの HTTP プロキシを上記のプロキシに設定し、スマホアプリを楽しむだけです。当然ですが、脱獄は不要です。

# 実践例
私の大好きなスマホアプリに、〇〇〇〇シ〇 というアプリがあります。このアプリは通信にHTTPを使っており、通信内容もJSONをBASE64エンコードしたものをやり取りするだけ、という、セキュリティに全く気を使っていないつくりとなっているのでこれを例にとりましょう。

まずは、 `mitmproxy` を使ってどのような通信がなされているのかを確認します。クエスト開始時には、`/quest/start` などのようなわかりやすいPOST通信がなされているのでその Response を覗きます。内容は暗号化のようなものがなされていますが、構成文字が全てASCII文字であることや、データ末尾に＝が存在することから、BASE64であることが想像できます。実際に以下のような python コードでデコードできることが分かりました。

```py
base64.b64decode(str)
```

生成した文字列を見ると、ただのJSONファイルなので、結局以下のようなコードで ハッシュ型 にすることができます。

```py
data = json.loads(base64.b64decode(str))
```

ハッシュの中を覗くと、敵のステータス(HPや攻撃力)などがわかりやすく書かれているのでそこをちょこっと変えて、相手の攻撃力を 1にして、 HPも1 にしてしまいます。例えばstage 1 の 1番目に出てくる敵のHPを 1 にするコードはこんな感じです。

```py
data["quest"][0][0]["hp"] = 1
```

このようにデータを書き換えて、json型にして、BASE64エンコーディングすると、楽勝ステージの出来上がりです。

```py
body = base64.b64encode(json.dumps(data))
```

これらをまとめてスクリプトにします。

```cheat.py
def response(ctx, flow):
    if flow.request.path != "/quest/start":
        return
    with decoded(flow.response):
        c = flow.response.content
        data = json.loads(base64.b64decode(c.strip()))
        for i in range(len(data["quests"])):
            for j in range(len(data["quests"][i])):
                target = data["quests"][i][j]["hp"] = 1
        flow.response.content = base64.b64encode(json.dumps(data))
```

たった数行のスクリプトで、すべての敵の HPが 1 のステージに書き換えが可能になりました。

# mitmproxy のすごいところ
上記でみるように `mitmproxy` は非常に有能です。python が使える人にとってはこれ一択でよいでしょう。以下に有用なユースケースを上げます

* 開発中のアプリには本番のURLを埋め込むが、`mitmproxy` でリクエストは開発サーバに転送
* gzip 圧縮されていても、HTTPS通信でも復号化してくれる
* CUIで動作するので非常に軽い。vi ライクな操作体系なので解析も便利
* チート対策（入力異常系）の試験がやりやすい
* （本記事では扱わなかったが）セッションハイジャックが楽にできる

だいたい想像できるコトはこれで実現可能です。

# 再考: プロキシ型チートの mitigation （緩和策）
上記例では簡単にチートができてしまいました。逆にどのようにすればよいのでしょうか？いくつか対策を取り上げて検討してみましょう

## ■データの暗号化をする
今回暗号化はされていなかった(BASE64程度では暗号化とはいえませんね？)のですが、データを独自の方法で暗号化することで簡単に中身が見えないようになると思います。ただし、アプリを逆アセンブルされて、暗号・復号ロジックを読まれるとどうしようもありませんね。

## ■改ざん検知用のハッシュを持つ
例えば、HTTPヘッダに、BODYのmd5sumなどをつけておけば、データが改ざんされていることをアプリ側で検知できるはずです。ただしこれも、アプリの逆アセンブルをされると、ヘッダも合わせて改ざんされてしまうでしょうね。

## ■クエスト後のデータ送信時にクエストデータも送る
クエスト後のRequestでクエストデータを送ることで書き換えたクエストデータもサーバ側に送られるので、改ざん検知ができます。ただし、HTTPリクエストをまたぐため、少しややこしくはなりますが、Request時に元に戻してしまえばよいので対策にはならないでしょう

## ■サーバ側で何らかのチェックを行う
例えば、クエスト攻略時間が極端に短かったり、ありえない総ダメージ数の場合にエラーと判定するものです。ただし、これはものすごく手間になりますし、誤検知もふえるでしょう。

## ■まとめると
どの対策も緩和にはなりますが、ここまでできる人にとっては突破されてしまうでしょうね・・・

# おわりに
プロキシ型のチートの手法の一例を紹介しました。本投稿を通じていかにチートが楽にできてしまうかに気付いて、セキュリティに気を使ったアプリを開発してくれるモチベーションにしてくれると嬉しいです。

# 参考
`mitmproxy` のインストール等は以下のQiita記事に詳しいです。

http://qiita.com/yimajo/items/c67cb711851f747c35e5