PostgreSQL の仕様の穴 (CVE-2018-1058) で遊んでみた

[CVE-2018-1058](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1058) は PostgreSQL 10.3 等で文書化された脆弱性で、マニュアルの [5.8.6. Usage Patterns](https://www.postgresql.org/docs/10/static/ddl-schemas.html#DDL-SCHEMAS-PATTERNS) などに対策のための記述が追加されました。

詳しい内容は Wiki の [A Guide to CVE-2018-1058: Protect Your Search Path](https://wiki.postgresql.org/wiki/A_Guide_to_CVE-2018-1058:_Protect_Your_Search_Path) に記述されており (日本語だと[OSSデータベース取り取り時報 第32回](http://gihyo.jp/dev/serial/01/oss-db-various-news/0032#sec2)が良いです)、面白い挙動だと思ったので実際の動きを確認してみました。

## 再現手順

### 1. Windows 10 に PostgreSQL 10.3 をインストール
ロケールを C にして適当にインストールしました。

### 2. C:\Program Files\PostgreSQL\10\bin を PATH に追加

### 3. postgres さんでログイン
```powershell
PS> psql -Upostgres
```

### 4. alice ちゃんのアカウントをつくる
```sql
postgres=# CREATE USER alice WITH PASSWORD 'Shino'; -- シノ大好き！
```
インストーラーが作るデータベース・クラスタは `initdb -A md5` となっているため、パスワード認証必須みたいです。

### 5. テーブル a をつくる
```sql
postgres=# CREATE TABLE a (full_name varchar(255));
```

### 6. 適当に insert する
```sql
postgres=# INSERT INTO a VALUES ('humpty dumpty');
```

### 7. upper 関数を使って確認
```sql
postgres=# SELECT upper(full_name) FROM a;
     upper
---------------
 HUMPTY DUMPTY
(1 行)
```
これがあるべき結果！

### 8. alice ちゃんでログイン
```powershell
PS> psql -Ualice postgres
```

### 9. いたずらで罠関数版の upper を作る
```sql
postgres=> CREATE FUNCTION upper(varchar) RETURNS text AS $$
postgres$>     -- 日本の文字大好き！ 全角にしよう！
postgres$>     SELECT translate($1, 'abcdefghijklmnopqrstuvwxyz', 'ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ');
postgres$> $$ LANGUAGE SQL IMMUTABLE;
```

### 10. postgres さんでさっきと同じクエリを実行
```sql
postgres=# SELECT upper(full_name) FROM a;
           upper
---------------------------
 ＨＵＭＰＴＹ ＤＵＭＰＴＹ
(1 行)
```

さっきと違って全角になってる！！　罠関数を踏んでしまった！

普通の upper は引数が text 型なので varchar 型とは暗黙の型変換が行われますが、alice ちゃんの upper は引数が元から varchar 型なので、より良く一致するのですね。

### 11. postgres さんで問題を解決
```sql
postgres=# -- 容疑者探し
postgres=# \df+ public.*
                                                                                                                                関数一覧
 スキーマ | 名前  | 結果のデータ型 |  引数のデータ型   |  型  | 関数の変動性分類 | 並列実行 | 所有者 | セキュリティ | アクセス権限 | 手続き言語 |                                                  ソースコード                                                   | 説明
----------+-------+----------------+-------------------+------+------------------+----------+--------+--------------+--------------+------------+-----------------------------------------------------------------------------------------------------------------+------
 public   | upper | text           | character varying | 通常 | IMMUTABLE        | 危険     | alice  | 起動ロール   |              | sql        |                                                                                                                +|
          |       |                |                   |      |                  |          |        |              |              |            |     -- 日本の文字大好き！ 全角にしよう！                                                                       +|
          |       |                |                   |      |                  |          |        |              |              |            |     SELECT translate($1, 'abcdefghijklmnopqrstuvwxyz', 'ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ');+|
          |       |                |                   |      |                  |          |        |              |              |            |                                                                                                                 |
(1 行)

postgres=# DROP FUNCTION public.upper; -- アリスちゃんのいたずらの後始末
DROP FUNCTION

postgres=# REVOKE CREATE ON SCHEMA public FROM PUBLIC; -- 明示的に許可していないユーザーは public に CREATE できなくしよう
REVOKE
```

この対策が取れない場合、search_path を `'"$user", public'` ではなく `"$user"` とする方法があります。

### 12. alice ちゃんでいたずらできないのを確認
```sql
postgres=> CREATE FUNCTION upper(varchar) RETURNS text AS $$
postgres$>     SELECT translate($1, 'abcdefghijklmnopqrstuvwxyz', 'ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ');
postgres$> $$ LANGUAGE SQL IMMUTABLE;
ERROR:  permission denied for schema public
```

### 13. alice ちゃん用の遊び場を作成
```sql
postgres=# CREATE SCHEMA AUTHORIZATION alice;
```

### 14. alice ちゃんが自室で遊べることを確認
```sql
postgres=> CREATE FUNCTION upper(varchar) RETURNS text AS $$
postgres$>     SELECT translate($1, 'abcdefghijklmnopqrstuvwxyz', 'ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ');
postgres$> $$ LANGUAGE SQL IMMUTABLE;
CREATE FUNCTION
postgres=> CREATE TABLE b (first varchar(256), last text);
CREATE TABLE
postgres=> INSERT INTO b VALUES ('humpty', 'dumpty');
INSERT 0 1
postgres=> SELECT upper(first), upper(last) FROM b;
    upper     | upper
--------------+--------
 ＨＵＭＰＴＹ | DUMPTY
(1 行)
```

## あとがき

デフォルトの権限を見直さず alice ちゃんに不要な権限を与えちゃったのがいけなかったですね。とはいえデフォルト値の `initdb -A trust` とか、public に一般ユーザが CREATE できるとかは好ましいとは思えず、最初から安全にしてくれても良いのに、と思いました。11 に期待できるでしょうか。

私は 10.3 の提供開始当初、アップデートしさえすれば安全になると思い込んでいました。Windows Server の Meltdown 脆弱性パッチでも同じ勘違いをしていました。リリースノート等はきちんと読むべきですね。

私に商用環境の構築経験はないため、もしかすると上記では不十分な点もあるかもしれません。業務でご参考になさる場合には、私と同じ轍を踏まないよう、公式文書も合わせてご確認ください。

ここまでご覧いただきありがとうございました。