[iOS]端末の時間を進めることによるチート(不正行為)の現状と、それをお手軽に防ぐ方法


##端末時間を進めることによるチートの現状
iPhoneでは、ユーザーは端末時間の変更を設定アプリから簡単に行うことが出来ます。これはこれで便利な機能なのですが、例えば時間の経過が重要な要素となるゲームなどでは、この機能を利用することによりチート(ズル)を行うことが出来ます。

いわゆるソーシャルゲームの場合、基本的にサーバーと通信することが前提となっているため、端末の時刻に依存しない仕様となっているか、時刻の設定が不正だった場合には警告を出す、という仕様になっているものがほとんどです。

しかしながら、いわゆる進化系ゲームなどに見られるような個人開発のカジュアルゲームでは、チート対策が行われていないことがほとんどであるように思います。もちろんあえて実装してないというパターンもあるのだとは思いますが、サーバーとの通信処理等も必要となるため、小規模なアプリや個人開発アプリでは、実装コストを考えて対応してない、というケースもあるのでは、と思っています。

そこでこの記事では、<b>たった3行のコードで時間変更チートを補足できるライブラリ</b>をご紹介します。

##SRGTimeCheatCapturer
SRGTimeCheatCapturerは、お手軽に端末の時刻設定が正しいかどうかのチェックを行うことが出来るライブラリです。内部でntpサーバーに問い合わせて時刻を取得しているため、サーバーサイドのプログラミングの知識等は一切必要ありません。

SRGTimeCheatCapturer
https://github.com/kazu0620/SRGTimeCheatCapturer

##導入してみる
Podfileに以下の行を追加してpod updateすることでSRGTimeCheatCapturerをインストールすることが出来ます。

```ruby:Podfile
pod 'SRGTimeCheatCapturer', '~> 0.0.1'
```

##チートを補足してみる
AppDelegateのapplicationDidBecomeActive:の中に下記の処理を追記することで、アプリを開いたタイミングで端末の時間が正しいかどうかのチェックを行うことが出来ます。

objective-cの場合

```objc:objective-cの場合

- (void)applicationDidBecomeActive:(UIApplication *)application {
    SRGTimeCheatCapturer *capturer = [SRGTimeCheatCapturer new];
    [capturer checkWithOnCheatCaptured:^(){
        NSLog(@"CHEAT CAPTURED!");
    }];
}
```

swiftの場合

```objc:swiftの場合
    func applicationDidBecomeActive(application: UIApplication) {
        let capturer = SRGTimeCheatCapturer();
        capturer.checkWithOnCheatCaptured({() in
            NSLog("CHEAT CAPTURED!");
        })
    }
```
##チート判定の閾値を変更する
SRGTimeCheatCapturer では、<b>allowableTimeDiff </b>というプロパティを変更することで、チート判定の閾値を変更することが出来ます。デフォルトは50秒(海外のntpサーバーを利用していることもあり少々多めにとっています)となっているのですが、例えば世の中にはチートを行う意図はなく5分前行動のために時計を5分進めているユーザーの方、などもいたりします。

そういうユーザーの方も問題なくゲームを遊べるよう、明らかに悪意があると判定できるところまで閾値を変えて利用することも可能です。ゲームシステムや状況に合わせて、調整して頂ければと思います。ちなみに、私が過去に進化系カジュアルゲームを運用したときは、チート判定の閾値を60分に設定していました。

##時間が正しくない場合には操作不能にする
チートを補足した場合の対応については、例えば警告を出す、フラグを立ててちょっぴりゲーム進行を不利にする、など色々なものが考えられます。ここではサンプルとして、時間が正しく設定ない場合にはアプリ自体を操作できなくする例も記述しておきます。下記のメソッドは、ボタンを1つだけ持ったUIAlertを表示し、ボタンを押すとsafariが立ち上がり指定したURLに遷移する、という処理になっています。このメソッドをcheckWithOnCheatCaptured内で呼び出すことにより、アプリの操作を不能にすることが出来ます。テキストや遷移先URLは、適宜変更してご利用下さい。

objective-cの場合

```objective-c:objective-cの場合
- (void) showTimeCheatWarning {
     UIAlertController *alertController = [UIAlertController
         alertControllerWithTitle:@"時刻を正しく設定して下さい。"
         message:@"下記の操作を行うことにより、時刻を設定することが出来ます\nホーム画面から設定タップ->[一般]->[日付と時刻]を選択"
         preferredStyle:UIAlertControllerStyleAlert
     ];
     [alertController
         addAction:[UIAlertAction actionWithTitle:@"OK"
         style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
             NSURL *url = [NSURL URLWithString:@"http://xxxxxxxxxx"];
             [[UIApplication sharedApplication] openURL:url];
         }
     ]];
     [self.window.rootViewController presentViewController:alertController animated:YES completion:nil];
}
```

swiftの場合

```swift:swiftの場合
    func showTimeCheatWarning() {
        var alertController = UIAlertController(
            title: "時刻を正しく設定して下さい。",
            message: "下記の操作を行うことにより、時刻を設定することが出来ます\nホーム画面から設定タップ->[一般]->[日付と時刻]を選択",
            preferredStyle: .Alert
        )
        let urlOpen = UIAlertAction(
            title: "OK",
            style: UIAlertActionStyle.Destructive) { (action) in
                var url = NSURL(string:"http://xxxxxxxxxx")
                UIApplication.sharedApplication().openURL(url!)
        }
        alertController.addAction(urlOpen)
        self.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
    }
```

##オフライン時の対応について
SRGTimeCheatCapturerは、ntpサーバーと通信を行うことにより現在の時刻をチェックしています。つまり、オフライン時にはチェックが行われないということに注意して下さい。当然、オフライン時にアプリが利用できないという仕様は、ユーザー体験を大きく損ないます。それでも確実にチートを防ぎたい、という場合はReachabilityなどのライブラリを利用し、通信状況を判定するのが良いでしょう。

参考:[iOSの通信状態を確認する方法](http://qiita.com/andy0440sada/items/d62330b34b9a7aab001c)

##(余談)ios-ntpについて
SRGTimeCheatCapturerでは、ios-ntpというライブラリを独自にカスタマイズしたものを利用しています。このios-ntpですが、Google Codeに公開されている元々の制作者の方のリポジトリは、既にメンテナンスされていない状態となっています。

そこでGitHubには有志の方がこれをForkしてメンテナンスしている、というリポジトリがいくつが出来ています。もちろんそれは素晴らしいことなのですが、現在CocoaPodsの[Specs](https://github.com/CocoaPods/Specs/blob/master/Specs/ios-ntp/1.0.0/ios-ntp.podspec.json)に登録されているios-ntpのリポジトリについては注意が必要です。というのも、コミットの差分を見てみると、元々の開発者の方が作ったものの意図とは明らかに違う方向で大きくロジックを変更している修正がいくつかあったからです。

14個のntpサーバーと通信し、有効なもの8個の平均値から時刻を算出する、というのが元々のロジックなのですが、CocoaPods上のものは呼び出しのインターフェイスも変更した上で、指定した一つのntpサーバーとのみ通信する、という処理に置き換わっていました。「良くない変更なのでは？」というコメントをしてる方にも「多分よくないけど、今作ってるモノにすぐに必要だったんだよ」とのお返事が。さらにこのリポジトリは、Issuesが利用できない設定となっています。

そこで、もしもios-ntpを利用する必要がある場合は、下記のリポジトリのものを使うことをオススメします。こちらは、本来の実装意図と大きく変わらない状態で正常にメンテナンスされています。

https://github.com/jbenet/ios-ntp