「SQLアンチパターン」の気づき（アプリケーション開発1）

## はじめに

こんにちは。小川です。
以前から気になっていた「SQLアンチパターン」という本を読みました。
とても良い本で今まで気づかずにやっていたことがSQLアンチパターンなんだと知ることができました。
勉強になったので、学んだことを言語化して量が多いのでいくつかの記事に分けて紹介していこうと思います。
今回は第Ⅳ部のアプリケーション開発のアンチパターンについて記載します。
この記事は[「SQLアンチパターン」の気づき（クエリ3）](https://qiita.com/pesysyon/items/82640b7e620fec80d14c)の続きになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780649/edfcea05-fe0b-f2b8-c4c9-0881241eae1e.png)

## 19章.リーダブルパスワード（読み取り可能パスワード）

#### アンチパターン：パスワードを平文で格納する
平文のパスワードを格納することは、データベース設計における重大なセキュリティ欠陥であり、権限のない人に特権的アクセスを与えるセキュリティリスクを生じさせます。

#### デメリット
パスワードは、よく文字列型の列としてAcountsテーブルに格納されます。
```sql
CREATE TABLE Acounts (
    accountid       SERIAL          PRIMARY KEY,
    account_name    VARCHAR(20)     NOT NULL,
    email           VARCHAR(100)    NOT NULL,
    password        VARCHAR(30)     NOT NULL
);
```
新規作成アカウントのために行を挿入する際にも、パスワードを文字列で記述します。
```sql
INSERT INTO Acounts (accountid, account_name, email, password)
VALUES (123, 'billkarwin', 'bill@example.com', 'xyzzy')
```

パスワードを平文で格納するのは安全ではありませんし、ネットワーク上の平文パスワードをやりとりするのは尚更危険です。
攻撃者から見ればいくらでもパスワードを盗むチャンスがあります。
以下に例を挙げます。

- アプリケーション層からデータベースに送信されたSQLを傍受するのは簡単です。Wiresharkを起動しておけば簡単にみれます。
- SQLクエリのログを探す方法もあります。データベースが実行したSQL文の記録を含むログファイルにアクセスされる可能性です。
- バックアップファイルやバックアップメディアからもデータを読み取れます。

#### 解決策：ソルトを付けてパスワードハッシュを格納する
- ハッシュ関数を利用する
パスワードを、一方向性の暗号化ハッシュ関数を用いて暗号化します。
このハッシュ関数は、入力文字列を、入力内容が識別不可能なハッシュと呼ばれる文字列に変換します。
  ```sql
  SHA2('xyzzy', 256) = 'fjie8q@eu90gjq3r@84389q7tgjr8y34ut8-g@hrq8br'
  ```
- SQLでのハッシュの使用
以下に示す例は、Acountsテーブルを再定義したものです。SHA-256を用いてパスワードをハッシュ化すると、常に64文字になります。
  ```sql
  CREATE TABLE Acounts (
    accountid       SERIAL          PRIMARY KEY,
    account_name    VARCHAR(20)     NOT NULL,
    email           VARCHAR(100)    NOT NULL,
    password_hash   CHAR(64)        NOT NULL
  );
  ```
  このテーブルに対してユーザーデータをINSERTする場合、SQLのハッシュ関数を用いてINSERTします。
  ```sql
  INSERT INTO Acounts (accountid, account_name, email, password)
  VALUES (123, 'billkarwin', 'bill@example.com', SHA2('xyzzy', 256))
  ```
  ログインイベントが発火した際には、ユーザー入力に同じハッシュ関数を適用することで、その結果をデータベースに格納されたハッシュ関数と比較することができます。
  ```sql
  SELECT CASE WHEN password_hask = SHA2('xyzzy', 256) THEN 1 ELSE 0 END
  FROM Acounts
  WHERE account_id = 123;
  ```

- ハッシュにソルトを加える
  ハッシュを格納していても、攻撃者にデータベースのアクセスを許してしまえば、攻撃者はトライアンドエラーでパスワードを解読しようとします。
  辞書に載っているようなありふれた言葉がパスワードに使われていれば、攻撃者のデータベースで同じハッシュを見つけ出すことは難しくありません。

  この辞書型攻撃を防ぐ方法は、暗号化前のパスワードに「ソルト」と呼ばれる文字列を付加することです。
  ```sql
  SHA2('password', 256)
  ='fjpeq483qpu8ghj@8804u28gj82-548u2jg82-g8hj2-jhg82h2q8'
  ```
  この状態だと、同じ256ハッシュでハッシュ化されたパスワードと一致してしまうかもしれません。
  ```sql
  SHA2('password' || 'G0y6cfj3iqw84j3q8gp', 256)
  ='fj9q3fjq348-fu8q438q3@jg4q358u8-w3gjq83g5qw8-j5858304'
  ```
  ソルトを付け加えることで、ハッシュ化されたパスワードが完全に別の値になります。

- SQLからパスワードを隠す
  これまでの方法でデータベースのハッシュパスワードを、攻撃者の持つよく使われるパスワードをハッシュ化したものを参照する攻撃を防ぐことができました。

  まだSQLクエリのログを探す方法もあります。データベースが実行したSQL文の記録を含むログファイルにアクセスされる可能性を対策できていません。

  依然としてSQLのクエリの中でパスワードが平文として使用されているためです。

  これを防ぐための方法は、SQLクエリ内部で生のパスワードを使用しないことです。
  言い換えれば、アプリケーション内(PHPやDjango等)でハッシュパスワードを計算し、SQLクエリではハッシュのみを用いるようにすることです。
  

## まとめ
今回は第Ⅳ部のアプリケーション開発のアンチパターンについて一部まとめてみました。
この記事はこの本を読んで、私がこういう理解をしましたということを言語化してみました。
とても勉強になる本なので皆様もぜひ読んでみてください。

以上、小川でした。