Commit 84188820 by Qiang Xue

Merge pull request #6623 from softark/docs-guide-ja-security

Docs guide ja security [ci skip]
parents 91f43754 96844416
...@@ -109,10 +109,10 @@ All Rights Reserved. ...@@ -109,10 +109,10 @@ All Rights Reserved.
------------ ------------
* [認証](security-authentication.md) * [認証](security-authentication.md)
* **翻訳中** [権限付与](security-authorization.md) * [権限付与](security-authorization.md)
* **翻訳中** [パスワードを扱う](security-passwords.md) * [パスワードを扱う](security-passwords.md)
* **TBD** [Auth クライアント](security-auth-clients.md) * **TBD** [Auth クライアント](security-auth-clients.md)
* **翻訳中** [ベストプラクティス](security-best-practices.md) * [ベストプラクティス](security-best-practices.md)
キャッシュ キャッシュ
......
セキュリティのベストプラクティス
================================
下記において、一般的なセキュリティの指針を復習し、Yii を使ってアプリケーションを開発するときに脅威を回避する方法を説明します。
基本的な指針
------------
どのようなアプリケーションが開発されているかに関わらず、セキュリティに関しては二つの大きな指針が存在します。
1. 入力をフィルタする。
2. 出力をエスケープする。
### 入力をフィルタする
入力をフィルタするとは、入力値は決して安全なものであると見なさず、取得した値が実際に許可さていれる値に含まれるか否かを常にチェックしなければならない、ということを意味します。
つまり、並べ替えが三つのフィールド `title``created_at` および `status` によって実行され、フィールドの値がインプットによって提供されるものであることを知っている場合、取得した値を受信するその場でチェックする方が良い、ということです。
基本的な PHP の形式では、次のようなコードになります。
```php
$sortBy = $_GET['sort'];
if (!in_array($sortBy, ['title', 'created_at', 'status'])) {
throw new Exception('Invalid sort value.');
}
```
Yii においては、たいていの場合、同様のチェックを行うために [フォームのバリデーション](input-validation.md) を使うことになるでしょう。
### 出力をエスケープする
データを使用するコンテキストに応じて、出力をエスケープしなければなりません。
つまり、HTML のコンテキストでは、`<``>` などの特殊な文字をエスケープしなければなりません。
JavaScript や SQL のコンテキストでは、対象となる文字は別のセットになります。
全てを手動でエスケープするのは間違いを生じやすいことですから、Yii は異なるコンテキストに応じたエスケープを実行するためのさまざまなツールを提供しています。
SQL インジェクションを回避する
------------------------------
SQL インジェクションは、次のように、エスケープされていない文字列を連結してクエリテキストを構築する場合に発生します。
```php
$username = $_GET['username'];
$sql = "SELECT * FROM user WHERE username = '$username'";
```
正しいユーザ名を提供する代りに、攻撃者は `'; DROP TABLE user; --` のような文字列をあなたのアプリケーションに与えることが出来ます。
結果として構築される SQL は次のようになります。
```sql
SELECT * FROM user WHERE username = ''; DROP TABLE user; --'
```
これは有効なクエリで、空のユーザ名を持つユーザを探してから、`user` テーブルを削除します。
おそらく、ウェブサイトは破壊されて、データは失われることになります (定期的なバックアップは設定済みですよね、ね? )。
Yii においては、ほとんどのデータベースクエリは、PDO のプリペアドステートメントを適切に使用する [アクティブレコード](db-active-record.md) を経由して実行されます。
プリペアドステートメントの場合は、上で説明したようなクエリの改竄は不可能です。
それでも、[生のクエリ](db-dao.md)[クエリビルダ](db-query-builder.md) を必要とする場合はあります。
その場合には、データを渡すための安全な方法を使わなければなりません。
データをカラムの値として使う場合は、プリペアドステートメントを使うことが望まれます。
```php
// query builder
$userIDs = (new Query())
->select('id')
->from('user')
->where('status=:status', [':status' => $status])
->all();
// DAO
$userIDs = $connection
->createCommand('SELECT id FROM user where status=:status')
->bindValues([':status' => $status])
->queryColumn();
```
データがカラム名やテーブル名を指定するために使われる場合は、事前定義された一連の値だけを許可するのが最善の方法です。
```php
function actionList($orderBy = null)
{
if (!in_array($orderBy, ['name', 'status'])) {
throw new BadRequestHttpException('名前とステータスだけを並べ替えに使うことが出来ます。')
}
// ...
}
```
それが不可能な場合は、テーブル名とカラム名をエスケープしなければなりません。
Yii はそういうエスケープのための特別な文法を持っており、それを使うと、サポートされている全てのデータベースに対して同じ方法でエスケープすることが出来ます。
```php
$sql = "SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();
```
この文法の詳細は、[テーブルとカラムの名前を引用符で囲む](db-dao.md#quoting-table-and-column-names) で読むことが出来ます。
XSS を回避する
--------------
XSS すなわちクロスサイトスクリプティングは、ブラウザに HTML を出力する際に、出力が適切にエスケープされていないと発生します。
例えば、ユーザ名を入力できるフォームで `Alexander` の代りに `<script>alert('Hello!');</script>` と入力した場合、ユーザ名をエスケープせずに出力している全てのページでは、JavaScript `alert('Hello!');` が実行されて、ブラウザにアラートボックスがポップアップ表示されます。
ウェブサイト次第では、そのようなスクリプトを使って、無害なアラートではなく、あなたの名前を使ってメッセージを送信したり、さらには銀行取引を実行したりすることが可能です。
XSS の回避は、Yii においてはとても簡単です。一般に、二つのケースがあります。
1. データを平文テキストとして出力したい。
2. データを HTML として出力したい。
平文テキストしか必要でない場合は、エスケープは次のようにとても簡単です。
```php
<?= \yii\helpers\Html::encode($username) ?>
```
HTML である場合は、HtmlPurifier から助けを得ることが出来ます。
```php
<?= \yii\helpers\HtmlPurifier::process($description) ?>
```
HtmlPurifier の処理は非常に重いので、キャッシュを追加することを検討してください。
CSRF を回避する
---------------
TBD: what's CSRF, how it works, intro
1. HTTP の規格に従うこと、すなわち、GET はアプリケーションの状態を変更すべきではない。
2. Yii の CSRF 保護を有効にしておくこと。
TBD: how CSRF protection works
ファイルの曝露を回避する
------------------------
デフォルトでは、サーバのウェブルートは、`index.php` がある `web` ディレクトリを指すように意図されています。
共有ホスティング環境の場合、それをすることが出来ずに、全てのコード、構成情報、ログをサーバのウェブルートの下に置かなくてはならないことがあり得ます。
そういう場合には、`web` 以外の全てに対してアクセスを拒否することを忘れないでください。
それも出来ない場合は、アプリケーションを別の場所でホストすることを検討してください。
実運用環境ではデバッグ情報とデバッグツールを無効にする
------------------------------------------------------
デバッグモードでは、Yii は極めて多くのエラー情報を出力します。これは確かに開発には役立つものです。
しかし、実際の所、これらの饒舌なエラー情報は、攻撃者にとっても、データベース構造、構成情報の値、コードの断片などを曝露してくれる重宝なものです。
実運用のアプリケーションにおいては、決して `index.php``YII_DEBUG``true` にして走らせてはいけません。
実運用環境では Gii を決して有効にしてはいけません。
Gii を使うと、データベース構造とコードに関する情報を得ることが出来るだけでなく、コードを Gii によって生成したもので書き換えることすら出来てしまいます。
デバッグツールバーは本当に必要でない限り実運用環境では使用を避けるべきです。
これはアプリケーションと構成情報の全ての詳細を曝露することが出来ます。
どうしても必要な場合は、あなたの IP だけに適切にアクセス制限されていることを再度チェックしてください。
セキュリティ
============
> Note|注意: この節はまだ執筆中です。
十分なセキュリティは、どのようなアプリケーションであっても、健全さを保って成功するためには欠くことが出来ないものです。
不幸なことに、理解が不足しているためか、実装の難易度が高すぎるためか、セキュリティのことになると手を抜く開発者がたくさんいます。
Yii によって駆動されるアプリケーションを可能な限り安全にするために、Yii はいくつかの優秀な使いやすいセキュリティ機能を内蔵しています。
ハッシュとパスワードの検証
--------------------------
ほとんどの開発者はパスワードを平文テキストでは保存できないということを知っていますが、パスワードを `md5``sha1` でハッシュしてもまだ安全だと思っている開発者がたくさんいます。
かつては、前述のハッシュアルゴリズムを使えば十分であった時もありましたが、現代のハードウェアをもってすれば、そのようなハッシュはブルートフォースアタックを使って非常に簡単に復元することが可能です。
最悪のシナリオ (アプリケーションに侵入された場合) であっても、ユーザのパスワードについて強化されたセキュリティを提供することが出来るように、ブルートフォースアタックに対する耐性が強いハッシュアルゴリズムを使う必要があります。
現在、最善の選択は `bcrypt` です。
PHP では、[crypt 関数](http://php.net/manual/ja/function.crypt.php) を使って `bcrypt` ハッシュを生成することが出来ます。
Yii は `crypt` を使ってハッシュを安全に生成し検証することを容易にする二つのヘルパ関数を提供します。
ユーザが初めてパスワードを提供するとき (例えば、ユーザ登録の時) には、パスワードをハッシュする必要があります。
```php
$hash = Yii::$app->getSecurity()->generatePasswordHash($password);
```
そして、ハッシュを対応するモデル属性と関連付けて、後で使用するためにデータベースに保存します。
ユーザがログインを試みたときは、送信されたパスワードは、前にハッシュされて保存されたパスワードと照合して検証されます。
```php
if (Yii::$app->getSecurity()->validatePassword($password, $hash)) {
// よろしい、ユーザをログインさせる
} else {
// パスワードが違う
}
```
擬似乱数データを生成する
------------------------
擬似乱数データはさまざまな状況で役に立ちます。
例えば、メール経由でパスワードをリセットするときは、トークンを生成してデータベースに保存し、それをユーザにメールで送信します。
そして、ユーザはこのトークンを自分がアカウントの所有者であることの証拠として使用します。
このトークンがユニークかつ推測困難なものであることは非常に重要なことです。
さもなくば、攻撃者がトークンの値を推測してユーザのパスワードをリセットする可能性があります。
Yii のセキュリティヘルパは擬似乱数データの生成を単純な作業にしてくれます。
```php
$key = Yii::$app->getSecurity()->generateRandomString();
```
暗号論的に安全な乱数データを生成するためには、`openssl` 拡張をインストールしている必要があることに注意してください。
暗号化と復号化
--------------
Yii は秘密鍵を使ってデータを暗号化/復号化することを可能にする便利なヘルパ関数を提供しています。
データを暗号化関数に渡して、秘密鍵を持つ者だけが復号化することが出来るようにすることが出来ます。
例えば、何らかの情報をデータベースに保存する必要があるけれども、(たとえアプリケーションのデータベースが第三者に漏洩した場合でも) 秘密鍵を持つユーザだけがそれを見ることが出来るようにする必要がある、という場合には次のようにします。
```php
// $data と $secretKey はフォームから取得する
$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey);
// $encryptedData をデータベースに保存する
```
そして、後でユーザがデータを読みたいときは、次のようにします。
```php
// $secretKey はユーザ入力から取得、$encryptedData はデータベースから取得
$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey);
```
データの完全性を確認する
------------------------
データが第三者によって改竄されたり、更には何らかの形で毀損されたりしていないことを確認する必要がある、という場合があります。
Yii は二つのヘルパ関数の形で、データの完全性を確認するための簡単な方法を提供しています。
秘密鍵とデータから生成されたハッシュをデータにプレフィクスします。
```php
// $secretKey はアプリケーションまたはユーザの秘密、$genuineData は信頼できるソースから取得
$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey);
```
データの完全性が毀損されていないかチェックします。
```php
// $secretKey はアプリケーションまたはユーザの秘密、$data は信頼できないソースから取得
$data = Yii::$app->getSecurity()->validateData($data, $secretKey);
```
todo: XSS prevention, CSRF prevention, cookie protection, refer to 1.1 guide
プロパティを設定することによって、CSRF バリデーションをコントローラ および/または アクション単位で無効にすることも出来ます。
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $enableCsrfValidation = false;
public function actionIndex()
{
// CSRF バリデーションはこのアクションおよびその他のアクションに適用されない
}
}
```
特定のアクションに対して CSRF バリデーションを無効にするためには、次のようにします。
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function beforeAction($action)
{
// ... ここで、何らかの条件に基づいて `$this->enableCsrfValidation` をセットする ...
// 親のメソッドを呼ぶ。プロパティが true なら、その中で CSRF がチェックされる。
return parent::beforeAction($action);
}
}
```
クッキーを安全にする
--------------------
- validation
- httpOnly is default
参照
----
以下も参照してください。
- [ビューのセキュリティ](structure-views.md#security)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment