
複数の端末で時計にズレがあった場合「どちらのデータが新しいのか」判断できず困ることがあります。
ライセンス終了日確認で、永遠に過去の時刻にしてずっと使う…なんてガバ仕様のアプリが昔は結構ありました。
これを防ぐためには 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 円であれば、改ざんする方がコスパは悪いでしょう。
問題がある場合、面倒ですが自社サーバーから署名&暗号化された時刻情報を取得するといった対処も検討しましょう。(そうやってどんどんサーバー運用費用は上がっていく…)
セキュリティ問題はどこまでいっても破られる可能性があります。そんな時、いかに「改ざんする方がコスパが悪い」と思わせられるかどうかが大事です。