複数の端末で時計にズレがあった場合「どちらのデータが新しいのか」判断できず困ることがあります。
ライセンス終了日確認で、永遠に過去の時刻にしてずっと使う…なんてガバ仕様のアプリが昔は結構ありました。
これを防ぐためには NTP サーバーと呼ばれるサーバー上の時刻を使うのが便利です。
以下のコードで取得できます。
using System; public class NTPTime { /// <summary> /// NTP サーバーよりサーバー時刻を取得 /// </summary> public static DateTime Get() { // windows 標準だから重そう //const string NTP_SERVER = "time.windows.com"; // DNS サーバーを通さない方が、より取得成功率が上がる //const string NTP_SERVER = "ntp.jst.mfeed.ad.jp"; const string NTP_SERVER = "210.173.160.27"; const int NTP_PORT = 123; // NTPサーバーへの接続用 UDP 生成 System.Net.Sockets.UdpClient objSck; System.Net.IPEndPoint ipAny = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0); objSck = new System.Net.Sockets.UdpClient(ipAny); // リクエスト送信 byte[] sdat = new byte[48]; sdat[0] = 0xB; objSck.Send(sdat, sdat.Length, NTP_SERVER, NTP_PORT); // 日時データ受信 byte[] rdat = objSck.Receive(ref ipAny); // 1900/1/1 からの経過秒数 uint from19000101 = (uint)(rdat[40] << 24 | rdat[41] << 16 | rdat[42] << 8 | rdat[43]); // UTC Time DateTime dt = new DateTime(1900,1,1).AddSeconds(from19000101); return dt; } }
問題点
日本時間ではない
日本時間に変換したい場合は、以下のコードでコンバートします。
var utcTime = NTPTime.Get(); var jpTime = System.TimeZoneInfo.ConvertTimeFromUtc(utcTime, System.TimeZoneInfo.Local);
取得に失敗する
NTP サーバーという無料提供のサーバーにタダ乗りしているだけなので、動作が不安定だったり、取得に失敗することはあるでしょう。
サンプルのようにベタ IP にしたり、複数の NTP サーバー値を比較することで、問題はある程度緩和できます。
今回のサンプルは、単純化するため一つのサーバーからのみ取得しています。
受信ソケットデータが改ざんされる
アプリのお試し期間判定で、時刻を改ざんして使われたくない…そんな理由で NTP サーバーを検討する方もいるかもしれません。
このパケットは知っている人なら中身がバレバレなので、たとえ NTP サーバーから時間を取得しようが、ソケットデータ自身を改ざんされる恐れがあります。
この場合「ソケットデータの改ざんで、ユーザーがどれ程の利益を得るか」が判断のポイントになります。
アプリが年間 1000 万もかかるのであれば「お試し期間延長」は意味を持ちますが(やったらダメですよ?)、年間 1000 円であれば、改ざんする方がコスパは悪いでしょう。
問題がある場合、面倒ですが自社サーバーから署名&暗号化された時刻情報を取得するといった対処も検討しましょう。(そうやってどんどんサーバー運用費用は上がっていく…)
セキュリティ問題はどこまでいっても破られる可能性があります。そんな時、いかに「改ざんする方がコスパが悪い」と思わせられるかどうかが大事です。