Showing posts with label database. Show all posts
Showing posts with label database. Show all posts

Tuesday, March 31, 2015

さらば、愛しき論理削除。MySQLで大福帳型データベースを実現するツール「daifuku」を作ってみた

先のエントリ「論理削除はなぜ「筋が悪い」か」で書いたとおり、データベースに対して行われた操作を記録し、必要に応じて参照したり取り消したりしたいという要求は至極妥当なものですが、多くのRDBは、そのために簡単に使える仕組みを提供していません

ないのなら、作ってみようホトトギス

というわけで作った。


daifukuは、RDBに対して加えられた変更をトランザクション単位RDB内JSONとして記録するためのストアドやトリガを生成するコマンドです。
% daifuku dbname tbl1 tbl2 > setup.sql
のように実行すると、指定されたテーブル(ここではtbl1tbl2)にセットすべきトリガや、更新ログを記録するためのテーブル「daifuku_log」を生成するCREATE TABLEステートメントなど、必要なSQL文をsetup.sqlファイルに出力します。

次に出力内容を確認し、mysqlのルートユーザ権限でSQLを実行すると、準備は完了。
% mysql -u root < setup.sql
あとは、トランザクションの先頭でdaifuku_begin()プロシージャを呼び出せば、以降同一のトランザクション内で加えられた変更は、全てdaifuku_logテーブルに単一のJSON形式の配列として記録されます。

daifuku_begin()には、任意の文字列を渡し、RDBの変更とあわせて記録することができるので、更新の意図や操作を行った者の名前等を記録することにより、監査や障害分析を柔軟に行うことが可能になります。

また、記録されるトランザクションログのid(daifuku_log.id)は@daifuku_idというセッション変数に保存されるので、アプリケーションではその値をアプリケーション側のテーブルに記録することで、操作ログをUIに表示したり、Undo機能注2を実装したりすることも可能でしょう。

実際に使ってみた例が以下になります。トランザクション内で行ったdirect_messageテーブルとnotificationテーブルに対する操作がJSON形式でdaifuku_logテーブルに保存されていることが確認できます注3
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> call daifuku_begin('');
Query OK, 1 row affected (0.00 sec)

mysql> insert into direct_message (from_user,to_user,body) values (2,1,'WTF!!!');
Query OK, 1 row affected (0.01 sec)

mysql> insert into notification (user,body) values (2,'@yappo sent a new message');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from daifuku_log\G
*************************** 1. row ***************************
    id: 4
  info: 
action: [
    [
        "insert",
        "direct_message",
        {},
        {
            "id":        "2",
            "from_user": "2",
            "to_user":   "1",
            "body":      ["V1RGISEh"]
        }
    ],[
        "insert",
        "notification",
        {},
        {
            "id":   "2",
            "user": "2",
            "body": ["QHlhcHBvIHNlbnQgYSBuZXcgbWVzc2FnZQ=="]
        }
    ]
]
1 row in set (0.00 sec)

大規模なコンシューマ向けウェブサービスでは、この種のトリガは使いづらいかもしれませんが、バックオフィス向けのソフトウェア等では工数削減と品質向上に役立つ可能性があると思います。

ってことで、それでは、have fun!


補遺:他のアプローチとの比較

同様の機能をアプリケーションロジックとして、あるいは手書きのトリガとして実装することも不可能ではありませんが、トリガを自動生成する daifuku のアプローチの方が、アプリケーション開発に必要な工数、バグ混入の可能性、データベースのロック時間の極小化等の点において優れていると考えられます。

また、過去の任意のタイミングにおけるデータベースの状態を参照する必要がある場合は、範囲型による時間表現を用いたデータベース設計を行うべきでしょうが、要件が操作の記録やUndoである場合には、そのような過剰な正規化は不要であり悪影響のほうが大きいです注4。ってかMySQLには範囲型ないし。


注1: 名前の由来は大福帳型データベースです。
注2: トランザクション単位のrevertではなくdaifuku_logテーブルに記録されたログを逆順に適用していった際に以前のテーブルの状態に戻ることを保証したい場合は、分離レベルをシリアライザブルに設定しておく必要があります。この点において、更新ログを主、現時点での状態を示すテーブルを従とするアプローチに対し劣位であることは、先の記事でも触れたとおりです。
注3: 文字列型等、制御文字やUTF-8の範囲外の値を含む可能性のある型のデータについては、base64エンコードが行われ、それを示すために配列としてログに記録されます。詳しくはman daifukuをご参照ください。
注4: 操作=トランザクションとは元来リレーションに1対1でマッピングされづらいものなので、過去の操作を取り扱うことが主目的の場合には、無理に正規化を行わない方が都合が良いケースが多いと考えられます。

Thursday, March 26, 2015

論理削除はなぜ「筋が悪い」か

論理削除が云々について - mike-neckのブログ」を読んで。

データベース設計において、「テーブルの書き換えをするな、immutableなマスタと更新ログによって全てを構成しろ」というこの記事の主張はモデリング論として全く正しい

だが、残念なことに、ディスクやメモリが貴重な資源だった時代の技術であるRDBは、そのようなモデリングに基づいて設計されたデータベースには必ずしも適していない

第一の問題は、RDBに対してなされる様々な「更新」(トランザクション)は不定形(どのテーブルをどのように修正するかはアプリケーション依存)だという点。不定形な「更新」を時系列にそってRDBに記録していくのは、設計と並走性の点において困難あるいは煩雑なコーディングが必要になる(というか、そのような「イベント」による「変化」はREDOログに書き、その更新された「状態」をテーブルに反映していくというのがRDBの「一般的な使われ方」)。

第二の問題は、ほとんどのデータベースアクセスは、更新が蓄積された結果である「現在の状態」を問い合わせるものになるが、immutableなマスタと更新ログによって構成されるデータベース設計においては、そのような問い合わせに効率的に応答するのが難しいという点である。

従って、現実的なデータベース設計においては、多くのテーブルが「現在の状態」をもつ、immutableなマスタと更新ログから合成可能な「現在の状態を表現するビュー」を実体化したものとして表現されることになる。

ふりかえって、論理削除とはなにか。「現在の状態を表現する実体化ビュー」に、過去の状態(かつて存在したデータであることを意味する「削除済」)をフラグとして付与したものである。

「現在の状態を表現する」ことを前提にしたビューであるところのテーブルに、過去の状態の一部だけを表現するフラグを追加するのが「筋が悪い」設計だというのは明らかだ。

過去の状態を参照すべき要件があるなら、そのテーブルが表現する情報についてはimmutableなマスタと更新ログを用意し、その射影として表現されるビューとして「現在の状態」を実現すべきである。そのようなビューは自動生成できるならしても良いし、手動で更新するなら、ストアドを使うか、あるいはアプリケーションのデータベースアクセス層において手続きをまとめる方法を考えるべきだろう。

ただ、immutableなマスタ、更新ログと「現時点のビュー」の3テーブルを準備・運用するというのはそれなりにコストがかかるので、筋が悪い「削除フラグ」(より一般化すれば状態フラグ)を使うかどうかはケースバイケースで判断するのもひとつの見識である。


まとめ:
  • データベース設計にあたっては、テーブルを「現在の状態」を表現するものとして設計するか、それとも「immutableなマスタと更新ログ、および現時点の状態を表現するビュー」として設計するかという、2つの選択肢がある
  • 前者は簡潔で性能が高くなるが、過去の情報を参照できないという問題がある
  • 「削除フラグ」というのは、「現在の状態」を表現するテーブルに過去の状態の一部を表現する機能を場当たり的に足したもの(なので筋が悪い)
  • それよりも、過去の情報を参照する要件がある場合(もしくはそのような要件が発生すると想定される場合)は、テーブル単位で「immutableなマスタと更新ログ」という設計を採用しつつ、現在の情報をビューとして表現することを考えた方がよい

こういった点を理解・検討した上で、「それでも削除フラグの方が楽だろう」という判断を下しているなら良いと思います。


追記: Re: 論理削除はなぜ「筋が悪い」か - Blog by Sadayuki Furuhashiで挙げられていた疑問点について
効率的にSELECTや更新ができるスキーマを作ろうとすると、VIEWやFUNCTIONなど、側に実装するコードが増えてくる。それらのコードは、上記のようにDB側に実装しても良い(するべき)だろうか?それともアプリケーションに実装するべきだろうか?
アプリケーション要件によるでしょう。

が、アプリケーション要件に関わらず正しく動作する方法が何かという問に対しては、RDB側でトリガ(あるいはストアド)として実装するのが安全だというのが答えになるでしょう。また、アプリケーションが操作可能な処理は「immutableなマスタ」と更新ログテーブルへの追記のみとし、両者への操作から「現在の状態を表現するビュー」を生成すべきであって、「現在の状態を表現するビュー」をアプリケーションが操作し、その変更内容をトリガでログに出力するという方式は避けるべきという話になるかと思います。

なぜか。そうしないと分離レベルや主キーの発番処理に起因する込み入った条件が色々…続きはお近くのDBAにご相談ください。
テーブルのスキーマは停止時間なしで変更する手法をいくつか思いつくが(PostgreSQLなら)、上記のレコードを削除する操作などはアプリケーションの変更を伴うので難しい(アプリケーションとDBのスキーマをアトミックに変更できない)
カラムのアクセス権は無停止で変更できませんか? つまり、アプリケーションを拡張した結果として削除フラグの存在が冗長になったのであれば、そのフラグの更新をストアドあるいはトリガで行われるように変更し、書き込み権限をドロップすればいいでしょう。

こんな感じかと思います。

Tuesday, July 1, 2014

The JSON SQL Injection Vulnerability

tl;dr

Many SQL query builders written in Perl do not provide mitigation against JSON SQL injection vulnerability.

Developers should not forget to either type-check the input values taken from JSON (or any other hierarchical data structure) before passing them to the query builders, or should better consider migrating to query builders that provide API immune to such vulnerability.

Note: 問題の発見者による日本語での説明はこちらです.

Background

Traditionally, web applications have been designed to take HTML FORMs as their input. But today, more and more web applications are starting to receive their input using JSON or other kind of hierarchically-structured data containers thanks to the popularization of XMLHttpRequest and smartphone apps.

Designed in the old days, a number of Perl modules including SQL::Maker have been using unblessed references to define SQL expressions. The following example illustrate how the operators are being specified within the users' code. The first query being generated consists of an equality match. The second query is generated through the use of a hashref to specify the operator used for comparison.

use SQL::Maker;
my $maker = SQL::Maker->new(…);

# generates: SELECT * FROM `user` WHERE `name`=?
$maker->select('user', ['*'], {name => $query->param('name')}); 

# generates: SELECT * FROM `fruit` WHERE `price`<=?
$maker->select('fruit', ['*'], {price => {'<=', $query->param('max_price')}});

This approach did not receive any security concern at the time it was invented, when the only source of input were HTML FORMs, since it is represented as a set of key-value pairs where all values are scalars. In other words, it is impossible to inject SQL expressions via HTML FORMs due to the fact that there is a guarantee by the query parser that the right hand expression of foo (i.e. $query->param('foo')) is not a hashref.


JSON SQL Injection

But the story has changed with JSON. JSON objects are represented as hashrefs in Perl, and thus a similar code receiving JSON is vulnerable against SQL operator injection.

Consider the code below.

use SQL::Maker;
my $maker = SQL::Maker->new(…);

# find an user with given name
$maker->select('user', ['*'], {name => $json->{'name'}}); 

The intention of the developer is to execute an SQL query that fetches the user information by using an equality match. If the input is {"name": "John Doe"} the condition of the generated query would be name='John Doe', and a row related to the specified person would be returned.

But what happens if the name field of the JSON was an object? If the supplied input is {"name": {"!=", ""}}, then the query condition becomes name!='' and the database will return all rows with non-empty names. Technically speaking, SQL::Maker accepts any string supplied at the key part as the operator to be used (i.e. there is no whitelisting); so the attack is not limited to changing the operator. (EDIT: Jun 3 2014)

Similar problem exists with the handling of JSON arrays; if the name field of the JSON is an array, then the IN operator would be used instead of the intended = operator.

It should be said that within the code snippet exists an operator injection vulnerability, which is referred hereafter as JSON SQL injection. The possibility of an attacker changing the operator may not seem like an issue of high risk, but there are scenarios in which an unexpected result-set of queries lead to unintended information disclosures or other hazardous behaviors of the application.

To prevent such attack, application developers should either assert that the type of the values are not references (representing arrays/hashes in JSON), or forcibly convert the values to scalars as shown in the snippet below.

use SQL::Maker;
my $maker = SQL::Maker->new(…);

# find an user with given argument that is forcibly converted to string
$maker->select('user', ['*'], {name => $json->{'name'} . ''}); 


Programmers Deserve a Better API

As explained, the programming interface provided by the SQL builders including SQL::Maker is per spec. as such, and thus it is the responsibility of the users to assert correctness of the types of the data being supplied.

But it should also be said that the programming interface is now inadequate in the sense that it is prone to the vulnerability. It would be better if we could use a better, safer way to build SQL queries.

To serve such purpose, we have done two things:


SQL::QueryMaker and the Strict Mode of SQL::Maker

SQL::QueryMaker is a module that we have developed and released just recently. It is not a fully-featured query builder but a module that concentrates in building query conditions. Instead of using unblessed references, the module uses blessed references (i.e. objects) for representing SQL expressions / exports global functions for creating such objects. And such objects are accepted by the most recent versions of SQL::Maker as query conditions.

Besides that, we have also introduced strict mode to SQL::Maker. When operating under strict mode, SQL::Maker will not accept unblessed references as its arguments, so that it would be impossible for attackers to inject SQL operators even if the application developers forgot to type-check the supplied JSON values.

The two together provides a interface immune to JSON SQL injection. The code snippet shown below is an example using the features. Please consult the documentation of the modules for more detail.

use SQL::Maker;
use SQL::QueryMaker;

my $maker = SQL::Maker->new(
   …,
   strict => 1,
);

# generates: SELECT * FROM `user` WHERE `name`=?
$maker->select('user', ['*'], {name => $json->{‘name'}}); 

# generates: SELECT * FROM `fruit` WHERE `price`<=?
$maker->select('fruit', ['*'], {price => sql_le($json->{‘max_price’})}); 


Similar Problem may Exist in Other Languages / Libraries

I would not be surprised if the same proneness exist in other modules of Perl or similar libraries available for other programming languages, since it would seem natural from the programmers' standpoint to change the behaviour of the match condition based on the type of the supplied value.

Generally speaking application developers should not except that a value within JSON is of a certain type. You should always check the type before using them. OTOH we believe that library developers should provide a programming interface immune to vulnerabilities, as we have done in the case of SQL::Maker and SQL::QueryMaker.


Note: the issue was originally reported by Mr. Toshiharu Sugiyama, my colleague working at DeNA Co., Ltd.

Tuesday, February 25, 2014

ウェブアプリの「合理的な」セキュリティ対策に関する一考察

※※※ミドルウェアを中心に、ウェブ関連の研究開発に携わっている者による雑文です※※※

ウェブの脆弱性は、ウェブアプリケーションのバグに起因するものと、ウェブブラウザのバグに起因するものの2者に大別することができる。

ウェブアプリケーションを開発/提供する仕事に従事している者には、この前者、すなわち、ウェブアプリケーションのバグに起因する脆弱性を最小限に抑え込むことを求められる注1

かといって、脆弱性がないことを保障するのは難しい。「ウェブアプリケーションにバグがあっても脆弱性とはならない(あるいは被害が限定される)ような設計」を採用するのが現実的だと考えられる。

OSにおける、プロセス間のメモリ分離やuserIDに基づいたファイルへのアクセス制御を考えてみると、OSがセキュリティを「強制」するため、アプリケーション側で不正なコードが実行されても脆弱性とならない、もしくは、影響を小さく抑え込むことができるようになっていることがわかる。

ウェブ技術における同様の例は数多いが、たとえばXSS脆弱性対策を見ると、


といったものを挙げることができる。また、SQL Injection対策を見ると、


等の手法が知られている。

これらの対策をひとつ選択し、あるいは組み合わせて使うことで、コーディングミスがあったとしても脆弱性が発現しない(もしくは発現する可能性が低い)アプリケーションを実現することができる

ただ、この種の技術には多かれ少なかれ、アプリケーション側のコードに不自由を強いるという側面がある。

たとえば、Content Security Policyには、インラインの<SCRIPT>タグを実行しづらいという制限がある(1.1で修正見込)し、例として挙げたSQL Injection対策のいずれもが現実的でないウェブアプリケーションも多いだろう。また、SQLにおける条件節の漏れによる情報漏洩のように、本質的に対策が難しい注3問題も存在する。

以上のように、共通モジュール(あるいは下位レイヤ)でアクセス方法を「強制」する仕組みを用いることで、脆弱性への耐性を高めるという情報工学における一般的なアプローチは、ウェブ技術においても有効であり、積極的に使用すべきである注4。一方で、述べたように、今後の発展が期待される分野も存在する注5



注1: 後者については、一義的にはウェブブラウザベンダーが対応すべき問題である。もちろん、ウェブアプリケーション側で緩和策が実装できるならすれば良いケースもある

注2: 最新のテンプレートエンジン事情を良く知らないので列挙はしません。また、DOM APIベースのアプローチについても本稿では割愛します。

注3: ウェブアプリケーションにおいては、アクセス制限とアクセスを単一のクエリで記述することを求められることが多いため。この点は、ケーパビリティの概念を導入したORMのようなアプローチで解決可能なのかもしれないが…

注4: 「IPA 独立行政法人 情報処理推進機構:安全なウェブサイトの作り方」では、脆弱性を9種類に類型化して説明しているが、そのほとんどは「アプリケーションプログラマがミスをしても問題ない」ような共通コード(ウェブアプリケーションフレームワークやライブラリ等)の使用により回避することが可能であるし、そのような実装が称揚されるべきである

注5: なので、研究課題として面白いと思います

Thursday, January 30, 2014

Q4M 0.9.12 has been released

Today I have released version 0.9.12 of Q4M - a message queue storage engine for MySQL.

As of the release, Q4M finally includes CMakeFiles.txt, file necessary for building Q4M together with MySQL 5.5 and above. The installation procedure is described in q4m.github.io/install.html.

Changelog is also available at the homepage. Have fun!

Thursday, November 21, 2013

パスワードが漏洩しないウェブアプリの作り方 〜 ソルトつきハッシュで満足する前に考えるべきこと

■■序論

徳丸さんのスライド「いまさら聞けないパスワードの取り扱い方」に見られるように、昨今、ウェブアプリケーションの設計要件として、サーバ内に侵入された場合でもユーザーのパスワードをできるだけ保護すべきという論調が見受けられるようになってきました。

上掲のスライドでは、その手法としてソルトつきハッシュ化を勧めています。しかしながらスライドに書かれているとおり、ソルトつきハッシュには、複雑なパスワードの解読は困難になるものの、単純なパスワードを設定してしまっているユーザーのパスワードについては十分な保護を提供できないという問題があります。そして、多くのユーザーは適切なパスワード運用ができない、というのが悲しい現実です。

ソルトつきハッシュを使った手法でこのような問題が残るのは、ウェブアプリケーションサーバに侵入した攻撃者がユーザーの認証情報をダウンロードして、認証情報をオフライン攻撃することを防ぎようがない、という前提を置いているからです。

逆の言い方をすると、攻撃者がアプリケーションサーバに侵入したとしてもユーザーの認証情報にアクセスできなければ、認証情報を奪われる可能性はないわけです。そのようなシステムを構築するにはどのようにしたらいいでしょうか。

一般的なウェブアプリケーションにおける答えは、ストアドプロシージャの利用とデータベースサーバの保護にあります注1


■■ストアドプロシージャによるパスワード認証

多くのSQLデータベースサーバはストアドプロシージャとストアドファンクションをサポートしており、これはMySQLやPostgreSQLといったオープンソースRDBMSでも例外ではありません。ストアドプロシージャやストアドファンクションには二つの役割があり、ひとつは処理手順をまとめること、もうひとつは、テーブルへのアクセスパターンを限定することです。

たとえばMySQLの場合、以下のようなDDLを実行することで、一般ユーザー権限(webapp)からは読み書きできない認証情報カラムpasssalt(ソルト値を格納)とpasshash(ハッシュ値を格納)をもつuserテーブルを作ると同時に、パスワードの一致を確認するストアドファンクション(check_pw)を提供することができます。

-- テーブル定義
CREATE TABLE user (
  username varchar(255) NOT NULL,
  passsalt varbinary(255) NOT NULL,
  passhash varbinary(255) NOT NULL,
  PRIMARY KEY (username)
) DEFAULT CHARSET=utf8;

-- パスワードを検証するストアドファンクションを定義
DELIMITER |
CREATE FUNCTION check_pw(u TEXT, p TEXT) RETURNS INT
BEGIN
  RETURN (SELECT COUNT(*) FROM user WHERE username=u and passhash=SHA1(CONCAT(passsalt,SHA1(p))));
END;
|
DELIMETER ;

-- パスワードを更新するストアドプロシージャ定義
DELIMITER |
CREATE PROCEDURE update_pw(u TEXT, oldpass NEXT,newpasssalt TEXT,newpasshash TEXT)
BEGIN
  DECLARE r INT;
  SET r = check_pw(u, oldpass);
  IF r = 0 THEN
    SIGNAL SQLSTATE '45000'
      SET MESSAGE_TEXT 'incorrect old password'
  ELSE
    UPDATE user SET passsalt=newpasssalt,newpasshash=newpasshash WHERE username=u;
  END IF;
END;
|
DELIMETER ;

-- webappユーザーにusernameカラムのみSELECT権限付与
GRANT INSERT,DELETE ON db.user TO webapp@'webapp-host';
GRANT SELECT (username) ON db.user TO webapp@'webapp-host';

-- webappユーザーにストアドの実行権付与
GRANT EXECUTE ON FUNCTION check_pw TO webapp@'webapp-host';
GRANT EXECUTE ON PROCEDURE update_pw TO webapp@'webapp-host';

実際に、このデータベースに一般ユーザー権限で接続してみると、以下のように、ユーザーの作成はできるものの認証情報は読めない一方で、パスワードの検証は可能、となっていることがわかります。

% mysql -u webapp db

-- 新規ユーザー johndoe (パスワード: johnpass)を作成
mysql> insert into user (username,passsalt,passhash) values ('johndoe','abcdefg',sha1(concat('abcdefg',sha1('johnpass'))));
Query OK, 1 row affected (0.01 sec)

-- userテーブルの全カラムを読むことはできない
mysql> select * from user;
ERROR 1142 (42000): SELECT command denied to user 'webapp'@'webapp-host' for table 'user'

-- usernameカラムだけならば読むことができる
mysql> select username from user;
+----------+
| username |
+----------+
| johndoe  |
+----------+
1 row in set (0.00 sec)

-- パスワードの検証は可能
mysql> select check_pw('johndoe','johnpass');
+--------------------------------+
| check_pw('johndoe','johnpass') |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.00 sec)

-- 間違ったパスワードを指定すると検証に失敗
mysql> select check_pw('johndoe','badpass');
+-------------------------------+
| check_pw('johndoe','badpass') |
+-------------------------------+
|                             0 |
+-------------------------------+
1 row in set (0.00 sec)

このように、SQLデータベース内にパスワードの検証/更新ロジックを持たせることで、(データベースに一般ユーザー権限でアクセスするための情報が保存された)ウェブアプリケーションサーバに攻撃者の侵入を許した場合でも、パスワードの漏洩を防ぐことができます。

また、ストアドプロシージャで監査ログを出力することで、攻撃を検知したり被害範囲を特定したりといったことも可能になるでしょう。

(2013/11/27追記: このような手法を実践するにあたっては、@isidaiさんの指摘をあわせて参照の上、各自の責任において設計、実装くださいますようお願いいたします)

■■データベースサーバの保護

SQLデータベースの機能を利用してパスワードを保護する場合に考えなければならないこととして、ウェブアプリケーションサーバが攻撃者の手に落ちた場合に、そこを踏み台としてデータベースサーバに侵入されるリスクを下げるにはどうすれば良いか、という点があります。

答えは単純で、データベースサーバをウェブアプリケーションサーバとは別のネットワークセグメントに配置し、両者の間にファイアウォールを設置すれば良い注2、ということになります。ファイアウォールの役割は、ウェブアプリケーションサーバからデータベースサーバへのアクセスを正規のTCPポートを指定するもの以外、全て遮断することです注3

このように設定することで、データベースサーバの権限管理機能にバグがない限り、ウェブアプリケーションサーバを踏み台として認証情報を奪われる可能性はなくなります。

なお、言うまでもありませんが、データベースサーバを配置するネットワークセグメントをインターネットと直接接続してはいけません。理想的には、アクセス手段を厳密に規定して管理系セグメントからのみ接続可能とすべきでしょう。


■■まとめ

ユーザーのパスワードを適切に暗号化することは重要ですが(そのまとめとして徳丸さんのスライドは優れていると思います)、サーバへの侵入を前提としてアーキテクチャを設計する際には、多層防御の手法が有効になります。

本稿では、RDBMSサーバのストアドプロシージャとファイアウォールの使用を通じて、ユーザーの認証情報を多層防御するウェブアプリケーションが簡単に構成できることを説明しました。


■■余談

実は、本稿で取り上げた問題とその解は、Unixにおいてシャドウパスワードが導入された経緯の変奏曲です。Unixのシャドウパスワードとは、誰もが閲覧可能なファイル(/etc/passwd)に一方向暗号化(ハッシュ化)されたパスワードを記載しておくのではなく、攻撃者(一般ユーザー)がアクセスし得ないファイル(/etc/shadow)に認証情報を配置し、認証が必要な場合はAPIを通してルート権限で動作するプロセスに問い合わせ、/etc/shadowの内容と比較した結果を回答してもらう、という仕組みです。


注1: 厳密に言うと、RDBMSでなくても別のサーバにAPIで問い合わせるのであればなんでもいいのですが
注2: ウェブアプリケーションとデータベースが1台のサーバに同居するケースでも、OSのユーザベースの権限分離を使って同様のことは不可能ではないと思います
注3: 大規模な構成かのように聞こえるかと思いますが、Amazon AWSのRDSを使うと必然的にこの構成になるなど、実は意外と身近なものです

Wednesday, March 27, 2013

Q4M now supports MySQL 5.5 / 5.6, an advisory to 0.9.7, 0.9.8 users

Q4M is a message queue implemented as a pluggable storage engine of MySQL.

Thanks to the helps from @laysakura; and @kamipo I have finally added to Q4M support for MySQL 5.5 / 5.6 in 0.9.8.

After releasing 0.9.8 I have noticed that a bug was introduced in 0.9.7 that may cause crashes and/or data losses when accessing tables created under previous versions of Q4M.

If you are using either 0.9.7 or 0.9.8 (that were upgraded from any previous version of Q4M), I recommend you to run the queue_compact(tbl_name) function against all existing Q4M tables to avoid the issue.  Sorry for the inconvenience.

If you are an existing user of 0.9.6 or an earlier version, please upgrade directly to 0.9.9.


--- Japanese Translation (in brief) ---

Q4M 0.9.7 と 0.9.8 には、それら以前のバージョンで作成されたテーブルデータにアクセスが発生した場合に、クラッシュあるいはデータロスが発生する可能性をもつバグが存在します。

このバグは、queue_compact(tbl_name) 関数を全ての Q4M テーブルに対し適用することで回避できますので、いずれかのバージョンをアップグレードとしてインストールした場合は、そうされることを推奨します。

また、0.9.6 以前をお使いの方がアップグレードされる際には、0.9.9 以降へ一気にアップグレードすることをお勧めします。

ご不便をおかけし申し訳ありません。

Tuesday, September 27, 2011

mysql_json - a MySQL UDF for parsing JSON

Somewhat related to (or rather not related to) やったーJavaScriptの動くMySQLできたよー - 愛と勇気と缶ビール, I have written a MySQL UDF that parses JSON strings.

The UDF introduces one function: json_get, that parses a JSON object or an array and returns one of the properties.

SELECT json_get('{"a":1}', 'a')       => 1
SELECT json_get('{"a":1}', 'b')       => NULL
SELECT json_get('[1,2,3]', 2)         => 3
SELECT json_get('{"a":[2]}', 'a', 0)  => 2

By using to the UDF it is possible to write queries accessing the properties of JSON objects stored in MySQL.

SELECT json_get(data,'title') FROM message WHERE id=1;
SELECT id,data FROM message WHERE json_get('data','from')='hidek';

Source code of the UDF can be found at github.com/kazuho/mysql_json. The installation process may vary, but I did the following for my ubuntu server.

% g++ -shared -fPIC -Wall -g mysql_json.cc -o mysql_json.so
% sudo cp mysql_json.so /usr/lib/mysql/plugin
% mysql -u root
mysql> create function json_get returns string soname 'mysql_json.so';