Webアプリを創る

ASP.NET Core でボットネット対策(その1 middlewareを使う)

2016年12月20日
DDoS 攻撃への対応

この記事は「ASP.NET Advent Calendar 2016 - Qiita」の20日目になります。

昨日のブログでは、ボットネットからアクセスがあることを書きましたが、その中で Google Cloud Datastore は自動スケールするので役に立ちそうだということを書きました。自動スケールされると破産するのではと心配する人もいると思いますが、Google Cloud では費用制限を設定するので破産することはないし、1秒間に1万回の読み込みをしても、1時間だと $21.6 とそれほど大したことはありません。

ただし、攻撃を放置しておくと結構な料金になってしまうので、攻撃を止める方法を考えてみました。

まず、DDoS 攻撃の現状がどうなっているかについては、Googleのシンクタンク部門である Google Ideas とセキュリティ企業の Arbor Networks と協力して、DDoS攻撃の状況をリアルタイムで表示するDigital Attack Mapを公開しています。そのサイトの Understanding DDoS によると、1週間小さな組織のシステムをダウンさせる能力があるボットネットを $150 で借りられるそうです。自分の場合は、最初はこの程度でできる攻撃への対応を考えてみようと思っています。

DDoS 攻撃への対応

攻撃を止める基本は、ボットのIPアドレスを特定して、そこからのアクセスを拒否することです。IPアドレスが特定できれば、アクセスの拒否については、ファイアーウォール、リバースプロキシーでも簡単にできます。そのため、DoS攻撃の場合は、比較的簡単に防御できます。

しかし、DDoS 攻撃の場合は、大量のボットから攻撃があるので、そもそも正常なアクセスなのか攻撃なのかを区別するのが容易でないし、攻撃元を特定して遮断していく作業は容易でありません。もし、攻撃元がボットをどんどん変更して攻撃してくれば、対応は本当に困難になります。

HTTP リクエストの場合、接続する URL 以外に IPアドレス、Referer、User-Agent の情報が送られてきます。低価格で借りたボットネットであれば、これらの情報に片寄りが生じるはずなので情報をうまく解析すれば、ボットのIPアドレスは特定できるはずです。

また、Pokémon GO で有名になったコイル警備員、すなわち Google の reCAPTCHA を使うことも有効です。アクセス拒否は、もし間違って拒否をするとそのユーザーを失うことになるので、疑わしいという状況では使いたくありません。でも、コイル警備員だとユーザー側の負担は遙かに軽いので、疑わしIPアドレスに対して使う事ができます。

Middleware を作成して特定のIPアドレスからのアクセスを拒否する

まず最初に、ASP.NET Core アプリケーションで、特定のIPアドレスからのアクセスを拒否するケースを考えてみます。

この場合には、HTTP Request があって、そのリクエストが MVC の処理に入るまでに処理をする必要があるため、Middleware を作成して処理する必要があります。ASP.NET Core の Middleware の Document に、middleware pipelineの図(下図)がありますが、

middleware pipeline の画像

その図をみれば、どう処理をしたらいいかをイメージできると思います。標準の MVC Middleware コンポーネントの前にカスタム Middleware コンポーネントを作成して処理をすることになります。

Middleware の作成に Visual Studio を使う場合は、「ミドルウェア クラス」のテンプレートがあるのでそれを使うと便利です。それを少し変更して非同期にすると下のようなコードになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace MyApp
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class MyMiddleware
    {
        private readonly RequestDelegate _next;
        public MyMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            await _next(httpContext);
        }
    }
    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class MyMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddleware>();
        }
    }
}

このコードと middleware pipeline の図を眺めていると大まかな使い方は理解できました。Invoke メソッドの、await _next(httpContext) が次のパイプラインを呼び出す処理で、その前後にコードを書くことで独自の処理を行わせることができます。

以下は、上のコードを修正して、特定のIPアドレスからのアクセスを拒否するようにしたサンプルです。

    public class AntiBotMiddleware
    {
        private readonly RequestDelegate _next;
        //拒否するアドレスの指定
        private readonly List<string> _blackIpAddress = new List<string> {"192.168.0.1", "192.168.0.2"};
        public AntiBotMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext)
        {
            //IPアドレスの取得
            string remoteIpAddress = httpContext.Connection.RemoteIpAddress.ToString();
            if (_blackIpAddress.Contains(remoteIpAddress))
            {
                //403 を返す                
                httpContext.Response.StatusCode = 403;
                return;
            }
            await _next(httpContext);
        }
    }

_next を呼ばなければ、次のパイプラインに行かずに返ります。それで、ブラウザーでは「サーバーエラー 403アクセスが拒否されました」と表示されるようになります。

この middleware を実行させるためには、Startup.cs の Configure メソッドに登録する必要があります。

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseAntiBotMiddleware();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

ここで、注意すべきことは、登録の順番が重要になってくるということです。今回の middleware は、その性格からいって UseMVC より前に置く必要があります。そして、UseStaticFiles の前におけば、静的ファイルもアクセス拒否の対象になり、後ろに置けば静的ファイルを除いたものがアクセス拒否の対象になります。

このサンプルは、プロキシーサーバーを使っている場合はうまく動作しません。httpContext.Connection.RemoteIpAddress が、プロキシーサーバーのアドレスになるためです。以下は、Nginx をプロキシーサーバーに使っている場合のサンプルです。

        public async Task Invoke(HttpContext httpContext)
        {
            //IPアドレスの取得
            string remoteIpAddress = GetRequestIP(httpContext);
            if (_blackIpAddress.Contains(remoteIpAddress))
            {
                //403 を返す                
                httpContext.Response.StatusCode = 403;
                return;
            }
            await _next(httpContext);
        }
        private static string GetRequestIP(HttpContext httpContext)
        {
            string ip = GetHeaderValueAs("X-Forwarded-For", httpContext);
            if (string.IsNullOrWhiteSpace(ip))
                return  httpContext.Connection.RemoteIpAddress.ToString();
            return ip;
        }
        private static string GetHeaderValueAs(string headerName, HttpContext httpContext)
        {
            StringValues values;
            if (httpContext?.Request?.Headers?.TryGetValue(headerName, out values) ?? false)
            {
                return values[0];
            }
            return null;
        }

なお、このサンプルが動作するためには、Nginx側で、X-Forwarded-For の設定をしておく必要があります。

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Dependency Injection を使う

上のサンプルで不便なのは、拒否するアドレスの指定を初期化で行っているところです。拒否するアドレスは画面で検索できたり変更できた方がいいですね。その場合には、Dependency Injection(DI)を使うのが自然です。DI という名前を聞くと難しそうに思うのし、解説を読んでも難しいのですが、以下のように簡単に作って試してみたら動作しました。

まず、普通に Class を作ります。(正式には、最初に Interface を作るようです)

public class AntiBotServise
{
    public List<string> BlackIpAddress { get; set; }
    public AntiBotServise()
    {
        BlackIpAddress = new List<string> {"192.168.0.1", "192.168.0.2"};
    }
}

すべてのリクエストで共通に使用するので、Startup.cs の ConfigureServices で AddSingleton を使って DIサービスコンテナに登録します。

public void ConfigureServices(IServiceCollection services)
{
   services.AddSingleton<AntiBotServise>();
   // Add framework services.
   services.AddMvc();
}

Middleware の方で、次のように Injection します。

public class AntiBotMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AntiBotServise _antiBotServise;
    public AntiBotMiddleware(RequestDelegate next, AntiBotServise antiBotServise)
    {
        _next = next;
        _antiBotServise = antiBotServise;
    }
    public async Task Invoke(HttpContext httpContext)
    {
        //IPアドレスの取得
        string remoteIpAddress = GetRequestIP(httpContext);
        if (_antiBotServise.BlackIpAddress.Contains(remoteIpAddress))
        {
            //403 を返す
            httpContext.Response.StatusCode = 403;
            return;
        }
        await _next(httpContext);
    }
}

Controller や View でも、同様に Injection できるので、画面で検索できたり変更したりできるようになります。

補足

長くなってきたので、middleware で eCAPTCHA を使う話は次回にします。

ASP.NET Advent Calendar では、Azure の話が多いようです。確かにエンタープライズ向けのクラウドでは Azure がベストだと思います。

しかし、DDoS の件を調べていると Google の名前がたびたび出てきます。Google は、公開用 Web サーバーが有力な収益源の一つなので、その関係のサービスは、Google の方がはるかに充実しています。また、.NET Foundation に加盟したことでもわかるように、Google Cloud Platform での .NET サポートはかなり進んできているという印象です。

ボットネットの脅威を考えたら Google Cloud を使うのがベストだ

2016年12月19日

これは Google Cloud Platform(2) Advent Calendar 2016 の19日目の記事です。

今年の11月に「Mirai」ボットネットが、Twitterなどの利用するDNSサービスへDDoS攻撃をしてサービスをダウンさせた事件があって、IoT ボットネットが話題になっています。先日、その IoT ボットネットから自分のWebサイトにもアクセスがありました。もちろん IoT ボットネットからという確証はありませんが、早朝から深夜までアクセスがあったので、常時電源の入っている機器には間違いありません。それだと普通のPCではないし、IP アドレスを調べるとモバイル回線からのアクセスはないのでスマートフォンでもなさそうです。

攻撃者は遊び程度だったようで、50台ぐらいのボットネットで、アクセスの間隔もかなり開いていました。それでもサーバーの CPU 負荷が普段より数倍になったので気がつきました。自分のWebサイトは時々スパイクアクセスがあるので、サーバーにかなり余裕をもたせていたので、今回は無事でした。

でも、もう少し攻撃が激しければ、システムがダウンしたと思います。そういうことを考えれば、サーバーはできるだけ増強したいのですが、その分サーバー料金がかさむのが痛いところです。

スパイクアクセスの場合は、同じページにアクセスが集中するので出力キャッシュが効果的なのですが、DDoS攻撃の場合はどういう攻撃されるか分からないので、現状だと、データベースの部分が弱いので、その能力のアップを急ごうと思っています。

現在、AWS を使っているので、DynamoDB だったら短時間でスループットを上げられるのかと調べてみると、よくある質問に以下のような回答がありました。スループットを増やすのに数分以上かかるようで、それだったらシステムはダウンしてしまいます。

Q: テーブルのプロビジョニングされたスループットレベルを変更するにはどのくらい時間がかかりますか?

通常、スループットを減らす場合は数秒から数分、増やす場合は数分から数時間かかります。

追加スループットが必要になった時点でスループットを増加したり増加のスケジュールを立てたりすることはお勧めできません。スループット容量のプロビジョニングは、必要なときに確実に容量を確保できるようかなり前から行っておくことをお勧めします。

それで Google Cloud Platform を調べたら、さすがに Google です。Cloud Datastore は、完全に自動スケールで、何も考える必要はありません。料金は以下のように完全に使っただけの料金になります。(以下東京リージョンの料金です)

  • データの保存 $0.18 GB/月
  • エンティティの読み込み数 $0.06 /10万エンティティ
  • エンティティの書き込み数 $0.18 /10万エンティティ
  • エンティティの削除数 $0.02 /10 万エンティティ

一方、AWS の DynamoDB の料金は以下のようになっています。

  • 書き込みスループット: $0.00742 :10 ユニットの書き込み容量あたり/1 時間 (1 時間あたり最大 36,000 回の書き込みを実行するために十分な容量)

  • 読み込みスループット: $0.00742 : 50 ユニットの読み込み容量あたり/1 時間 (1 時間あたり最大 180,000 回の強力な整合性のある読み込み、または最大 360,000 回の結果整合性のある読み込みを実行するために十分な容量)

こちらは、スループットで料金が決まります。この料金の記載だと、結果整合性のある読み込みだと、50ユニットの設定で、月2億5千万回のアクセスができ $5.34 で済みます。Google Cloud Datastore だったら $155 もかかると言いたいのでしょう。さすがに Amazon は商売上手です。

でも、公開用 Web サーバーの場合は、スパイクアクセスやDDoS攻撃のことを考えるとそんな単純な話ではありません。

価格を比較するため、公開用 Web サーバーで月1千万PVのWebサイトがあるとします。月1千万PVあれば Webサービスだとかなり楽に経営ができます。

Cloud Datastore の料金は、1pv に必要なデータベースへのリクエストを1エンティティとすると、料金は月 $6 になります。仮に 1pv当たり平均 10 エンティティが必要だとしても、月 $60 です。安い言い切るのは難しいかもしれませんがリーズナブルな料金です。

DynamoDB の場合は、容量をあらかじめ確保しておく必要があります。スパイクアクセスやDDos攻撃を考えると予測不可能で必要な容量の計算なんかできないですね。取りあえず1秒間に100pvのアクセスということにして、1pv で必要なデータベースへのアクセスが1ユニットだとすると、50ユニットが必要で月$5.34です。Cloud Datastore とほぼ同じ金額になります。

でも、ここでよく考えてみてください。DynamoDB は、全体のスループットではなくて、テーブル毎にスループットを設定する必要があります。ローカルセカンダリインデックスは、5個まででかつテーブルの作成のときに定義してやる必要がありそれ以降の変更はできないので、一つのテーブルに各種のデータを詰め込むのは無理で、それなりの数のテーブルが必要になると思います。DDos攻撃はどこが狙われるか分からないので、1秒間に100pvのアクセスに耐えるようにしようと思えば、すべてのテーブルにそにアクセスに耐えるだけのユニットを割り当てる必要があります。そしてさらに最悪なのが、1pv 当たりに必要なデータベースへのアクセスが平均では2ユニットであっても、あるページで50ユニット必要であれば、そのページが狙われるとダメなので、そのテーブルには50×50=2500ユニットも割り当てる必要がありそれだけで月$267になってしまいます。

1秒間に100pvというのは、どの程度かというと、今回のボットネットからの自分のサイトへのアクセスは、1台で1秒に15回以上のアクセスがありました。10台あれば150pvになるので100pvの容量だとエラーが多発するようになります。1秒間に100pvというのは、DDoS攻撃対策では、殆ど話にならないレベルです。

こう考えていくと、DynamoDB は、DDoS対策としては最悪でした。そして、通常の利用においても DynamoDB ファーストにするとテーブル数が多くなってコストが嵩みます。このことは、AWS もよく理解しているようで、Amazon AWSでユーザ数1100万以上にスケーリングするためのビギナーズ・ガイド という記事から引用します。

NoSQL データベースではなく、最初はSQLデータベースを使用しましょう。

  • まずはSQLデータベースの使用を推奨します。技術は確立されています。

  • 既存のコードやコミュニティ、サポートグループ、書籍やツールが多く存在します。

  • 最初の1000万ユーザでSQLデータベースが破壊されることはありません。(データベースが大規模でない限り)壊れることはないでしょう。

  • スケーラビリティの明確なパターンがあります。

NoSQL データベースが必要になるのはいつなのでしょうか。

  • 5TB以上のデータを初年に格納する必要がある場合や信じられないほどのデータベース集中型のワークロードの場合。

  • アプリケーションの要件が超低遅延である場合。

  • 高いスループットが必要な場合。読み込みや書き込みの際の出入力にかなり手を加える必要があります。

  • リレーショナルデータがない場合。

要するに DynamoDB は、アプリケーションを高速化するために、ごく少数のテーブルに対して使うべきもののようです。この影響で、業務系の人を中心に、NoSQL ファーストはダメだと思っている人が多いと思いますが、AWS の事情であって、本来はどちらがいいというものではなくて、アプリケーションの性格によって使うべきものです。

クラウドも AWS、Azure、Google Cloud のどれがいいのかという一般的な答えはないと思います。しかし、システムダウンしないWebサーバーにしようと思ったら、Cloud Datastore は殆どコストなしで無制限にスケールできるので Google Cloud を使った方がはるかにいいと思います。Google Cloud のアクセス集中に対する強さは、今年 Pokémon GO で実証されています。一方、AWS で、SQLデータベースをメインに使った場合、エンタープライズ向けの閉じたシステムだと、アクセス数はそれなりの精度で想定できるので問題はあまり生じないと思いますが、公開 Web サーバーで、DDoS攻撃 のことまで考えると非常に難しい問題になります。SQLデータベースではスケールさせるのにコストが非常に高くなるし、急激なアクセス増への対応も簡単ではないと思います。この点は、Azure も同じだと思います。

自分の Web サイトは、できれば DDoS攻撃に強いものにしていきたいので、サーバーを AWS から Google Cloud Platform に移そうと思っています。でも、Amazon EC2 のリザーブドインスタンスを買っているので、その期間が終了するまでは待ちます。リザーブドインスタンスは料金の割引は大きいのですが制約は厳しいと思います。google Compute Engine の方は、1ヶ月単位で自動的に継続割引が適用されます。何もしなくてもいいのには気に入りました。(計算してみたら AWSは3年の標準リザーブドインスタンスだとAmazon EC2 の料金が安いですが、3年縛りは厳しいし、値下げが適用されないのでリスクが高いです。コンバーティブルリザーブドインスタンスにすると google Compute Engine の継続割引後の料金とほぼ同じです。)

.NET Core で PostgreSQL を使ってみたら結構いけていたという話

2016年12月15日

これは PostgreSQL Advent Calendar 2016の15日目の記事です。

この Web サイトは、今年の4月から ASP.NET を Core の方に更新して Ubuntu サーバーで運用しています。実際に運用してみて、IIS よりも Nginx を使う方が便利だと思うようになってきたので、サーバーはすべて Linux サーバーにする予定です。

その際の問題は、データベースをどうするかということです。ASP.NET とはなんといっても SQL Server を使うのが定番です。それで、自分も SQL Server Express を使ってアプリを作っていました。しかし、残念なことに SQL Server が Linux に対応するのは来年の半ばということです。

最近、PostGIS を使いたいという思惑もあって、PostgreSQL を使ってみたら、結構いけていると感じたので、この記事を書くことにしました。まだ、使い始めて1ヶ月なので、今後問題が見つかるかもしれないので最終的どうなるかはわかりませんが、PostgreSQL を使っていこうと思っています。

Entity Framework Core で使う

.NET Core や .NET Framework から、PostgreSQL を使う場合は、Npgsql というデータプロバイダーを使用します。Npgsql には、Entity Framework Core(EF Core)用のプロバイダーもあるので、EF Core で使っている範囲では、SQL Server を使うのと違いは殆どありません。

参考 Npgsql の EF Core 用のドキュメント

例として、Visual Studio の ASP.NET Core の Web アプリケーションのテンプレートで認証を「個別のユーザーアカウント」にした場合に、EF Core が使用されているので、それを PostgreSQL を使うように修正してみます。

  1. Nuget パッケージ の追加 Npgsql.EntityFrameworkCore.PostgreSQL を追加します。

  2. Startup.cs の修正 以下のように、options.UseSqlServer と SQL Server を使う設定になっているので、

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
       .AddEntityFrameworkStores<ApplicationDbContext>()
       .AddDefaultTokenProviders();
    ------ 以下省略
}

options.UseNpgsql に変更して、Npgsql を使うようにします。

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
                options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
       .AddEntityFrameworkStores<ApplicationDbContext>()
       .AddDefaultTokenProviders();
    ------ 以下省略
}
  1. appsettings.json に記載してある接続文字列を修正します。
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=test;Username=postgres;Password=password"
  },
------ 以下省略
}

SQL Server から PostgreSQL を使うように変更するのはそれだけです。

接続文字列に設定したユーザーが、データベースを作成できる権限を持っているユーザーであれば、これでデータベースも勝手に作ります。もし、そうでなければ事前にデータベースを作っておきます。

ただし、それで Run して、ユーザーの登録をしようとするとエラーになります。Data ディレクトリに Migration フォルダーが作成されていて、SQL Server 用のものが事前に作られているためです。 それで、それらのファイルを削除してからするとちゃんとしたエラーメッセーが出ます。メーセージの通りにマイグレーションをしてやると正常に動くようになります。

A database operation failed while processing the request.
NpgsqlException: Exception while reading from stream 
IOException: Unable to read data from the transport connection: 既存の接続はリモート ホストに強制的に切断されました。. 
SocketException: 既存の接続はリモート ホストに強制的に切断されました。 
Use migrations to create the database for ApplicationDbContext
In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:
PM> Add-Migration [migration name] 
PM> Update-Database
Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:
> dotnet ef migrations add [migration name] 
> dotnet ef database update

コードファーストで開発する場合は、SQL Server なのか PostgreSQL なのかを気にしなくても開発できると思います。

細かい点でいうと作成されたテーブルをみると下の図のようにカラム名が Pascal ケースになっています。

AdWordのアカウントのリンクの画像

SQL Server では、カラム名の大文字小文字を区別しないのでPascalケースは便利なのですが、PostgreSQL では、列名の大文字小文字を区別し、SQL文の中の列名は小文字に変換されます。それで、カラム名が「AspNetUsers」だと SQL 文の中では、常に二重引用符でくくって "AspNetUsers" としないといけなくなって結構手間になります。そのまま Pascal ケースで使うのか、それとも fluent API か attributes を使ってスネークケースに変換して使うのか悩ましいところです。自分は EF Core で使うのが主で、生の SQL をあまり使わないので、Pascal ケースのままでもいいのではないかと思っています。

データベースファーストの場合も、SQL Server を使うのと殆ど違いはなく、Microsoft.EntityFrameworkCore.SqlServer.Design の代わりに Npgsql.EntityFrameworkCore.PostgreSQL.Design を追加し、

dotnet cli の場合は、

dotnet ef dbcontext scaffold "Host=localhost;Database=mydatabase;Username=myuser;Password=mypassword" Npgsql.EntityFrameworkCore.PostgreSQL

Powershell の場合は、

Scaffold-DbContext "Host=localhost;Database=mydatabase;Username=myuser;Password=mypassword" Npgsql.EntityFrameworkCore.PostgreSQL

というように、Microsoft.EntityFrameworkCore.SqlServerNpgsql.EntityFrameworkCore.PostgreSQL に置き換えてやれば、EF model を作成できます。

なお、DbContext のプロパティ名はカラム名を Pascal ケースに変換したものになります。

以上のように EF Core で使う限りにおいては、SQL Server を使うのと差はなく、実際に使っても今のところ大きな問題はでていません。

PostgreSQL を使うメリット、デメリット

SQL Server よりも便利な点としては、where 句で下のように正規表現を使うとサーバー側で処理をしてくれるという点です。( 参照 ドキュメント)

var customersStartingWithA = context.Customers.Where(c => Regex.IsMatch(c.CompanyName, "^A"));

まだ使っていませんが、circle 等の幾何データ型、配列型、JSONB型のマッピングは可能なようなので、EF Core でも、これらのデータの取得と更新は可能なようです。

ただし、PostgreSQL の独自の機能を本格的に使うためには、EF Core ではなくて、Npgsql を直接使う必要があり少し手間がかかります。

でも、少し調べてみると、.NET で PostgreSQL を Document Db として使うためのソフトとして Marten というソフトが開発されています。まだ使い始めたばかりですが、結構使いやすい感じです。

PostgreSQL のデメリットといえば、慣れの問題かも分かりませんが pgAdmin よりは、Visual Studio Management Studio の方が使いやすいという点です。

PostgreSQL にするか MySQL にするか

Linux で動作するオープンソースの RDB といえば、PostgreSQL よりも MySQL の方が有名です。

まず、.NET Core 対応という点で比較すると、PostgreSQL の方は、Npgsql の .NET Core 対応はアルファ版が出てもう1年以上になります。その後も開発が進められてきており、かなり安定してきていると思います。一方、MySQL の方は .NET Core に Connector/Net 7 で対応予定で現在開発版が公開されています。

最近の状況では、ZDNet Japan の「PostgreSQL」がスタートアップ企業に選ばれる理由という記事が参考になると思います。以下に最近のデータを載せておきます。

Hacker News Hiring Trends 2016年11月 Hacker News Hiring Trendsの画像

indeed Job Trends indeed Job Trendsのリンクの画像

ASP.NET Core を格安 VPS で使う

2016年10月1日

この Web サイトを Ubuntu サーバーで運用を始めて6ヶ月になりました。その間に特に問題はありませんでした。むしろ、Linux サーバーに慣れてきて、テキストベースの設定が結構便利だと思うようになりました。

それで、格安 VPS を使って、ASP.NET Core アプリを動かしてみました。使ったのは、「さくらの VPS」1G 月額972円で、Webサイトは pp22.net です。サイトの中身は、まだ、このサイトのコピーです。

さくらの VPS の設定及び ASP.NET Core アプリを Linux で公開する手順については以下のページにメモをしています。

pp22.net の Web サイトが動作し始めたので、WebPagetest を使ってスピードテストをしてみました。結果は殆ど差がなく、格安 VPS でも、Web アプリの作り方次第では十分使えることが分かりました。

creativeweb.jp creativeweb.jpのスピードテストの結果の図

pp22.net creativeweb.jpのスピードテストの結果の図

なお、pp22.net の方がロード時間が短いのは、下部に Google アドセンスの「関連コンテンツ ユニット」が付いていないことが影響していると思います。

格安 VPS を実際に使ってみて、ASP.NET Core を使う上での課題は、データベースをどうするかということです。このサイトは、ファイルベースなので問題なくインストールできたのですが、通常の Web アプリではデータベースが必須になる場合が多いと思います。

ASP.NET Core と親和性が高いのは SQL Sever Express ですが、Linux 版が出るのは来年の中頃です。Linux サーバー以外に Windows サーバーを立てて、そこに SQL Sever Express をインストールすれば動作に問題はないのですが、格安 VPS を使うという趣旨ではどうかと思います。

Linux で一番使われている RDB である MySQL の場合は、公式ドライバー MySQL Connector/NET がまだ開発中で安定性に問題があります。

そういうことで、現時点で格安にしようとすれば PostgreSQL ということになると思って現在テスト中です。PostgreSQL は、.NET Core への対応を積極的に行ってきたこと、また、JSONサポートも積極的に進めているのでスタートアップ向けにはいいのではないかと思っています。

RDB でなくててもいいのであれば、次のようなドライバーが .NET Core に対応しています。ただし、ServiceStack.Redis の方は、.NET Core 対応についてはアルファ版です。

また、クラウドのストレージを利用する場合は、次のようなツールが .NET Core に対応しているので、主なものは使えるようになっています。

企業の人が知っておくべきインターネット広告のこと

2016年9月29日

最近(2016年9月23日)、大手広告代理店電通が、インターネット広告で、虚偽報告など不正な取引があったことを認めたという事件がありました。

インターネット広告の闇とか書いている人がいますが、インターネット広告に闇なんてあるんでしょうか?単に企業の人がインターネット広告に無知なだけでないのでしょうか。日本では、企業が広告を掲載する時には、ネットメディアと直接取引をあまりせずに、電通など広告代理店が双方の中間に立って取引をしまうため、インターネット広告のことを知らなさすぎなだけではないでしょうか。

広告掲載のチェックについて

インターネット広告には、大きく分けてクリック課金タイプ、インプレッション課金タイプ、成果報酬タイプがあります。

成果報酬タイプは、実際に購入や契約など成果が合ったときに課金が発生するタイプです。これは誤魔化しようがありませんが、トヨタのように実店舗で購入する商品の場合は無理で、EC サイトを運営する企業向きです。

インプレッション課金タイプは、Webサイトの広告枠にバナーを掲載しますというようなもので、表示回数に応じて料金を支払います。

電通の不正があったのは、「運用型広告」と呼ばれるもので、その代表的なものが Google AdWords です。ウェブサイトやキーワード、トピック、ユーザー層を指定して、広告を表示させることができます。料金はクリック課金とインプレッション課金を選択できますが、クリック課金タイプの方が8割ぐらいを占めます。クリック課金の場合は、広告がクリックされると料金が発生します。

確認の方法ですが、管理画面を見れば簡単にわかりますが、広告代理店は管理画面を見せようとしない場合が多いと思います。でも、Google AdWords を使うのであれば、Google Analytics に Google AdWords のアカウントをリンクさせましょう。すごく便利です。

AdWordのアカウントのリンクの画像

なぜ、Google AdWords が使われているのか?細かい分析が容易にできるからです。インターネット広告に闇があるなんてうそです。

Google AdWords 以外の運用型広告でも、Google Analytics を使えば、Google AdWords を使うほどは細かな分析はできなくても、広告掲載のチェックぐらいはできます。

なぜ不正がおこるのか

なぜ不正がおこるのかの前に、Google AdWords に広告を出すのは自分でもできます。なぜ、それを広告代理店に出すのでしょうか。電通の社員は、大ヒットをする広告を作りたくて入社しているわけで、インターネット広告のような手間のかかる作業をしたいとは思わないでしょう。それは、クライアント企業の広告担当者も同じだと思います。

自分が Google Adsense をしていて、気づいたことの一つが、下の図をみてください、3月はクリック単価が高くて、それも下旬になるほど高くなるということです。(注: 下の図はクリック単価ではなくて、RPM(表示回数 1,000 回あたりの見積もり収益額)ですが、クリック単価の影響が大きいです。)

AdWordのアカウントのリンクの画像

日本では、3月末が決算なので、予算を使い切りたいか売り上げのノルマを達成したためではないでしょうか。3月に売り上げが増加するのならば理解できるのですが、そうとは思えないので、高い単価で広告を出すのは企業にとっては明らかに損をする行為です。それでもしてしまうのが広告マンです。

テレビや新聞の広告であれば、枠で料金が決まっているので予算の使い切りは簡単だし効果の説明も楽です。しかし、「運用型広告」では、クリック単価を決めて入札をする必要があり、相手のある話なので、自分の思い通りにはなりません。相手より単価が低ければ、広告が表示されないので予算が消化できません。広告の期間中はまんべんなく広告を表示したいと思ってもそれほど簡単なことではありません。

しかし、発注側の広告マンとしては格好をつけたいでしょう。広告代理店から細かい資料をもらうと説明が面倒なので、わかっていても単純な資料しか要求しないということもあるのでしょう。

雇用の流動性が必要

Google は、ソフトウェア開発者の会社です。Google AdWords にも Google Analytics にも API があります。コントロールパネルでちまちまと作業をしなくても、プログラムを書いて作業を自動化することができます。

また、インターネット広告からホームページに流入した見込み客が顧客になってくれるようなホームページを作成する必要があります。

こういう仕事を従来の広告マンに要求しても無理です。それよりも IT 系のエンジニアやデザイナーが向いている仕事です。例えば、マイクロソフトのエバンジェリストになったちょまどさんなんかは最適だと思います。彼女は、漫画家兼プログラマーです。

日本の場合は、企業の方は新卒一括採用で個性のある人をあまり雇用しようとしないし、エンジニアやデザイナーの方も新しいことをやりたがらない人が多いように思います。日本の IT 業界の従事者の待遇が世界で最低だという理由はこういう所にあるんだろうと思っています。

インターネット広告の分野でも、まだまだやれることはたくさん残されているので、チャレンジしてみたらいいと思います。