Objective-C
iOS
Swift

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

More than 3 years have passed since last update.

端末時間を進めることによるチートの現状

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

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

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

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

SRGTimeCheatCapturer

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

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

導入してみる

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

Podfile
pod 'SRGTimeCheatCapturer', '~> 0.0.1'

チートを補足してみる

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

objective-cの場合

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

swiftの場合

swiftの場合
    func applicationDidBecomeActive(application: UIApplication) {
        let capturer = SRGTimeCheatCapturer();
        capturer.checkWithOnCheatCaptured({() in
            NSLog("CHEAT CAPTURED!");
        })
    }

チート判定の閾値を変更する

SRGTimeCheatCapturer では、allowableTimeDiff というプロパティを変更することで、チート判定の閾値を変更することが出来ます。デフォルトは50秒(海外のntpサーバーを利用していることもあり少々多めにとっています)となっているのですが、例えば世の中にはチートを行う意図はなく5分前行動のために時計を5分進めているユーザーの方、などもいたりします。

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

時間が正しくない場合には操作不能にする

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

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の場合
    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の通信状態を確認する方法

(余談)ios-ntpについて

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

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

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

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

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