【セキュリティ】読んで損ないウェブセキュリティのススメ

# はじめに
<!-- 発端や概要を記載 -->
こんにちは、エンジニア2年目の嶋田です。
まずは、この記事を開いていただきありがとうございます！
セキュリティは現代のテクノロジー社会において非常に重要なテーマとなっています。
ウェブアプリケーションやシステムの開発において、セキュリティの脅威を理解し、対策を講じることは不可欠です。
今回は、セキュリティの中でも代表的な脅威とその対策について紹介します。

:::note info
2023.0829 更新済み(コメントで教えていただきありがとうございます！)
:::

# 目次
- [脆弱性とは何か？](#脆弱性とは何か)
- [セキュリティの基本](#セキュリティの基本)
  - [認証と認可の重要性](#認証と認可の重要性)
  - [データの保護と重要性](#データの保護と重要性) 2023.0829更新
      - [パスワードの取り扱い](#パスワードの取り扱い)
      - [クレジットカード情報の取り扱い](#クレジットカード情報の取り扱い)
      - [暗号化の役割](#暗号化の役割)
  - [セッション管理の重要性](#セッション管理の重要性)
- [主要な脆弱性の概要](#主要な脆弱性の概要)
  - [クロスサイトスクリプティング(XSS)](#クロスサイトスクリプティングxss)
  - [クロスサイトリクエストフォージェリ(CSRF)](#クロスサイトリクエストフォージェリcsrf)
  - [SQLインジェクション](#sqlインジェクション) 2023.0829更新
  - [セッションハイジャック](#セッションハイジャック)
  - [パラメータ改竄](#パラメータ改竄) 2023.0829更新
  - [データ暴露](#データ暴露)
- [最後に](#最後に)

# 脆弱性とは何か
**脆弱性**とは、システムやアプリケーションにおいて攻撃者が意図せずに不正なアクセスや操作を行う可能性を高める弱点のことを指します。脆弱性が悪用されると、機密情報が漏洩したり、システムが乗っ取られたりする危険性があります。

# セキュリティの基本
## 認証と認可の重要性
**認証**は、ユーザーがシステムにアクセスする際にその正当性を確認するプロセスです。一般的な認証手法には、ユーザー名とパスワードの入力や、二要素認証などがあります。**認可**は認証されたユーザーがどの機能やリソースにアクセスできるかを制御する仕組みです。適切な認証と認可を実施することで、不正アクセスを防ぎます。

## データの保護と重要性
**データの保護**は、データを権限のない第三者から守るための手法です。データが適切に保護されていれば、不正アクセスや漏洩のリスクを最小限に抑えることができます。重要な情報（例: パスワードやクレジットカード番号）の取り扱いについては、以下のポイントに留意することが推奨されます。

### パスワードの取り扱い
パスワードは平文ではなく、**ハッシュ化**された形で保存されるべきです。ハッシュ化によって、元のパスワードが逆算されにくくなります。安全なハッシュ関数を使用し、ソルト（ランダムなデータ）を組み合わせることでセキュリティを向上させることができます。

### クレジットカード情報の取り扱い
ウェブサイトやアプリケーションにおいては、一般的にクレジットカード情報の**非保持化**が推奨されています。これは、カード情報を一時的に利用し、必要な処理が終わったら直ちにクレジットカード番号などの情報を破棄することを意味します。安全な決済ゲートウェイを使用することで、カード情報の保護を確保することができます。
>PCI DSSについても触れたいけど知識不足です…皆さん調べてみてください…

### 暗号化の役割
データの暗号化も情報保護に重要な役割を果たします。例えば、機密性の高いデータや通信を保護するために、**暗号化**を使用します。暗号化はデータを特定の鍵を用いて変換し、不正アクセスを受けても内容が読み取れないようにします。適切な暗号化アルゴリズムと鍵管理のプラクティスを採用することが重要です。

総括すると、データの保護は個人情報や機密データの安全性を確保する上で欠かせない要素です。パスワードのハッシュ化やクレジットカード情報の非保持化、そして必要な場面での暗号化の活用によって、セキュリティレベルを向上させることが大切です。


## セッション管理の重要性
ウェブアプリケーションでは、**セッション管理**が重要です。ユーザーがログインした後も、そのセッションが正当なものであることを確認し続けることで、不正なアクセスを防ぎます。セッション固有のトークンを使用したり、タイムアウトを設定することで、セッションの安全性を高めることができます。

# 主要な脆弱性の概要
以下では、ウェブアプリケーションセキュリティの主要な脆弱性とその対策方法について説明します。

## クロスサイトスクリプティング(XSS)
**XSS**とは、攻撃者がウェブページに悪意あるスクリプトを埋め込み、他のユーザーのブラウザ上で実行させる攻撃です。これにより、攻撃者はユーザーに代わって操作を行ったり、クッキーやセッショントークンを盗み取ったりできます。

### 例え話
ユーザー間で手紙を送り合うサービスを考えてみましょう。攻撃者が手紙に紛れて毒を盛ると、受け取った人は毒入りの手紙を開封してしまうかもしれません。このように、信頼されたコンテンツに紛れて攻撃を仕掛けるのがXSSです。

### 何が問題か
XSS攻撃によって、ユーザーのプライバシーが侵害されたり、セッションが乗っ取られたりする可能性があります。

### 対策方法
- 入力データのエスケープ
ユーザーからの入力データを表示する際に、特殊文字を無害なものに変換することで、悪意あるスクリプトの実行を防止します。
```php
// ユーザー入力をエスケープして表示
echo htmlspecialchars($_POST['user_input'], ENT_QUOTES, 'UTF-8');

```
- Content Security Policy (CSP) の設定
CSPを使用して、許可されたコンテンツのみが読み込まれるようにすることで、外部からのスクリプトの実行を制限します。
```php
// CSPヘッダーを設定
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-scripts.com;");
```
- ユーザー入力のバリデーション
ユーザーからの入力を適切にバリデーションして、許可された形式や文字だけを受け入れるようにします。
```php
// ユーザー入力のバリデーション（例：数字のみ受け入れる）
if (ctype_digit($_POST['user_input'])) {
    // 処理を続行
} else {
    // エラー処理
}
```
- エスケープの無効化
ユーザーがHTMLやJavaScriptのコードを意図的に入力する必要がある場合、エスケープを無効化して表示します。
```php
// ユーザー入力のエスケープを無効化して表示
echo $_POST['user_input'];
```

## クロスサイトリクエストフォージェリ(CSRF)
**CSRF**とは、攻撃者が被害者に代わって意図しない操作を実行させる攻撃手法です。攻撃者は、被害者がログインしている状態で悪意のあるリクエストを送り込むことで、被害者の意図しないアクションを引き起こそうとします。

### 例え話
例えば、あなたが友達と一緒にカフェに行ったします。あなたが離席している間に、友達が勝手にあなたのコーヒーに砂糖を大さじ1杯分追加してしまったとします。あなたが戻ってきたとき、砂糖が入ったコーヒーが待っていて、あなたは気づかずにそのまま飲んでしまいます。これはあなたが意図しないアクション（砂糖を入れたコーヒーを飲む）が実行されてしまう状況です。CSRFも同じような考え方です。
### 何が問題か
CSRF攻撃によって、被害者は自分の意図しないアクションを実行してしまう可能性があります。たとえば、被害者が自動的に銀行口座からお金を引き出してしまう操作を行ったり、不正な投稿を行ってしまう可能性があります。
### 対策方法
- トークンの埋め込み
フォーム内にセッショントークンを埋め込むことで、送信されたリクエストが正規のものであることを確認します

```html
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">

```

- トークンの生成と比較
フォームから送信されたトークンとセッショントークンを比較して、CSRF攻撃を防止します。
```php
// セッショントークンの生成と保存
$csrf_token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $csrf_token;

// フォームから送信されたトークンの値を取得
$submitted_token = $_POST['csrf_token'];

// トークンの比較
if ($submitted_token === $_SESSION['csrf_token']) {
    // トークンが一致した場合の処理
} else {
    // トークンが一致しない場合のエラー処理
}
```

- セッショントークンの有効期限
セッショントークンの有効期限を設定し、一定時間経過したトークンは無効にすることで、攻撃者によるトークンの再利用を防止します。
```php
// セッショントークンの生成と保存（有効期限付き）
$csrf_token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $csrf_token;
$_SESSION['csrf_token_expiry'] = time() + 3600; // 有効期限を1時間に設定

// フォームから送信されたトークンの値と有効期限を比較
$submitted_token = $_POST['csrf_token'];
if ($submitted_token === $_SESSION['csrf_token'] && time() <= $_SESSION['csrf_token_expiry']) {
    // トークンが一致し、有効期限内の場合の処理
} else {
    // トークンが一致しないか、有効期限切れの場合のエラー処理
}
```

## SQLインジェクション
**SQLインジェクション**とは、攻撃者が意図せずにSQLクエリに悪意あるコードを挿入し、データベースに対する不正アクセスを行う攻撃手法です。これにより、データベースの情報が漏洩したり改ざんされたりする危険性があります。

### 例え話
あなたがメモ帳に計算式を書き込むことを考えてみてください。普段は計算をするために使うものですが、誰かが意図せず計算以外のことを書き込んでしまった場合、あなたが思っている動作とは異なる結果が出るかもしれません。同じように、SQLインジェクションでは予期せぬクエリがデータベースに送信されてしまい、意図しないデータを取得する可能性があります。

### 何が問題か
**SQLインジェクション**は、アプリケーションのデータベースへのクエリ構築において不適切な入力処理が行われると、攻撃者によって悪意あるSQLコードが挿入され、データベースへの不正なアクセスが可能となります。
例えば、以下のようなログイン処理の場合
```php
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
```
ここで、攻撃者が`$username`に`' OR '1'='1`を入力すると、クエリは次のようになります。
```php
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '$password'
```
### 対策方法
- プリペアドステートメントの使用
プリペアドステートメントは、クエリ内のパラメータを直接埋め込まず、後からバインドする方法です。これにより、入力データが適切にエスケープされ、SQLインジェクションのリスクが低減されます。

```php
// プリペアドステートメントの使用例（PHPとMySQL）
$userName = 'mysql';
$password = 'mysql';

// PDOオブジェクトの生成（データベース接続）
$pdo = new PDO('mysql:dbname=test;host=localhost', $userName, $password);

// プリペアドステートメントで SQLをあらかじめ用意しておく
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND password = :password');

// パラメータに値を設定してクエリを実行
$username = $_POST['username'];  // ユーザーからの入力
$password = $_POST['password'];  // ユーザーからの入力
$stmt->execute();

// パラメータをバインド
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);

// 結果を取得
$result = $stmt->fetch(PDO::FETCH_ASSOC);

// 結果の処理
if ($result) {
    // ログイン成功
} else {
    // ログイン失敗
}
```
プリペアドステートメントを使用することで、入力値が適切にエスケープされ、SQLインジェクション攻撃から保護されます。

## セッションハイジャック
**セッションハイジャック**とは、攻撃者が正当なユーザーのセッションを盗み取る攻撃のことです。セッションはユーザーがログインしてからログアウトするまでの間、ユーザーのアクティビティを識別するための一意のトークンです。攻撃者がセッションIDを盗み取ることで、そのセッションのコントロールを奪うことができます。

### 例え話
ある建物のカギを盗む行為に例えることができます。正当な住人がその建物に入るためにはカギが必要ですが、攻撃者がそのカギを盗むことで、無断でその建物に侵入することができます。同様に、セッションハイジャックは正当なユーザーのアカウントに無断で侵入する行為です。

### 何が問題か
セッションハイジャックが成功すると、攻撃者は正当なユーザーとしてログイン状態になり、そのユーザーの権限を悪用したり、個人情報を盗み出したりする可能性があります。これにより、ユーザーのプライバシーやデータが危険にさらされることになります。

### 対策方法
- セッションタイムアウトの設定
セッションタイムアウトの設定により、一定時間経過するとセッションが自動的に無効化されます。これにより、セッションが長時間アクティブなままになることを防ぎ、攻撃者が長時間セッションを盗み取って利用するリスクを減少させます。
```php
// セッションタイムアウトを設定する例 (秒単位)
ini_set("session.gc_maxlifetime", 1800); // 30分
```
- セッションの再作成
セッションIDはセッションを識別するためのキーであり、攻撃者がセッションIDを知ることでセッションを乗っ取る可能性があります。セッション再作成により、攻撃者が古いセッションIDを利用しても無効化され、新しいセッションIDが生成されるため、攻撃のリスクが低減します。
```php
// セッション再作成
session_regenerate_id(true);  // 新しいセッションIDを生成
$_SESSION = array();          // セッションデータを初期化
```
# パラメータ改ざん

**パラメータ改ざん**とは、ウェブアプリケーションやシステムに送信されるリクエストのパラメータを直接改ざんすることで、不正なアクセスを行う攻撃手法です。

## 例え話

あるオンラインゲームを考えてみます。プレイヤーのスコアを記録するシステムがあり、スコアはURLのクエリパラメータとして送信されます。攻撃者はスコアのパラメータを改ざんして高得点を送信し、ランキングに影響を与えることを試みます。これにより、正当なプレイヤーの努力が無駄になったり、ランキングが偽りの情報で溢れてしまうかもしれません。

## 何が問題か

パラメータ改ざんによって、ユーザーが意図しないアクセスや操作を行える可能性があります。例えば、特権ユーザーの権限を不正に取得したり、また、情報の改ざんによって経済的な被害が発生する可能性や信頼性のある情報が歪曲される可能性があります。

## 対策方法

上記で例えに挙げた『スコアのパラメータを改ざんして高得点を送信』に関しては[クロスサイトリクエストフォージェリ(CSRF)](#クロスサイトリクエストフォージェリcsrf)の対策を参考にしてください。
パラメータ改竄に関して(パラメータのidをいじって他ユーザのページに遷移できないようにする等)、GET/POSTでは根本的な解決にはなりません。

:::note warn
しかし、初心者の方は以下のGET/POSTの使い分けについて覚えておいた方がいいなと思うのでそのまま記載しておきます。(2023.08.29)
:::


GETとPOSTの使い分けが大切です。

- GETメソッド
    - リソースの取得や表示に使用される
    - ブラウザの履歴やURLにパラメータが残るため、機密情報を含む場合には避けるべき
- POSTメソッド
    - リソースの更新や送信に使用される
    - リクエストのデータがリクエストボディに格納され、URLには残らない

```html
<!-- GETメソッドでの情報取得フォーム -->
<form action="get_action.php" method="get">
    <input type="text" name="username" placeholder="ユーザー名">
    <input type="submit" value="情報取得">
</form>

<!-- POSTメソッドでの情報送信フォーム -->
<form action="post_action.php" method="post">
    <input type="text" name="password" placeholder="パスワード">
    <input type="submit" value="情報送信">
</form>
```

```php
// get_action.php - GETメソッドでの情報取得
$username = $_GET['username'];
// ユーザー名を使用した処理

// post_action.php - POSTメソッドでの情報送信
$password = $_POST['password'];
// パスワードを使用した処理
```

GETメソッドを使用して情報を取得するフォームと、POSTメソッドを使用して情報を送信するフォームがあります。重要な情報（パスワードなど）はPOSTメソッドを使用して送信され、リクエストボディに格納されるため、URLには表示されません。このように、情報の重要度に応じてGETとPOSTの使い分けを行い、機密情報がURLに含まれないようにすることがセキュリティ対策として重要です。


## データ暴露
**データ暴露**とは、本来非公開であるべきデータや情報が、意図しない形で外部に漏れる状況を指します。これは、個人情報、機密データ、企業秘密など、保護すべき情報が不正な手段で取得され、第三者に利用されるリスクを含んでいます。データ暴露は、セキュリティ対策の不備やシステムの脆弱性に起因することがあります。
### 例え話
最近流行りのAIチャットボットに、自分の仕事で開発しているプロジェクトの情報を入れてしまったとしましょう。しかし、その情報は公には知られてほしくないもの。それが誤って広まってしまうことで、思わぬトラブルが起こるかもしれません。

### 何が問題か
データ暴露が発生すると、本来秘密にすべき情報が外部に漏れてしまい、個人や組織に大きな被害をもたらす可能性があります。情報が悪用されたり、競合他社に利用されたりするリスクが高まります。

### 対策方法
- データを保存・送信する際には、機密情報を含む部分は適切な暗号化を行って保護します。
- 外部に公開する情報を選別し、最小限の情報のみを表示するようにします。例えば、プライベートなプロジェクト情報は公開しないようにします。
- データの取り扱いに関するガイドラインやルールを従業員や関係者に明確に伝え、情報漏洩を防ぐための意識を高めます。
- セキュリティの専門家による脆弱性評価や監査を定期的に実施し、リスクを最小限に抑えます。


## パラメータ改ざん
**パラメータ改ざん**とは、ウェブアプリケーションやシステムに送信されるリクエストのパラメータを直接改ざんすることで、不正なアクセスを行う攻撃攻撃手法です。

### 何が問題か
パラメータ改ざんによって、ユーザーが意図しないアクセスや操作を行える可能性があります。例えば、特権ユーザーの権限を不正に取得したり、また、情報の改ざんによって経済的な被害が発生する可能性や信頼性のある情報が歪曲される可能性があります。

### 対策方法
GETとPOSTの使い分けが大切です。
- GETメソッド
    - リソースの取得や表示に使用される
    - ブラウザの履歴やURLにパラメータが残るため、機密情報を含む場合には避けるべき
- POSTメソッド
    - リソースの更新や送信に使用される
    - リクエストのデータがリクエストボディに格納され、URLには残らない
```html
<!-- GETメソッドでの情報取得フォーム -->
<form action="get_action.php" method="get">
    <input type="text" name="username" placeholder="ユーザー名">
    <input type="submit" value="情報取得">
</form>

<!-- POSTメソッドでの情報送信フォーム -->
<form action="post_action.php" method="post">
    <input type="text" name="password" placeholder="パスワード">
    <input type="submit" value="情報送信">
</form>
```

```php
// get_action.php - GETメソッドでの情報取得
$username = $_GET['username'];
// ユーザー名を使用した処理

// post_action.php - POSTメソッドでの情報送信
$password = $_POST['password'];
// パスワードを使用した処理

```
GETメソッドを使用して情報を取得するフォームと、POSTメソッドを使用して情報を送信するフォームがあります。重要な情報（パスワードなど）はPOSTメソッドを使用して送信され、リクエストボディに格納されるため、URLには表示されません。このように、情報の重要度に応じてGETとPOSTの使い分けを行い、機密情報がURLに含まれないようにすることがセキュリティ対策として重要です。

# 最後に
セキュリティは難しいですがウェブアプリケーションやシステムの開発において欠かせない要素です。これらの脅威を理解し、適切な対策を講じることで、安全で信頼性のあるシステムを構築することができます。少しでも理解につながったら嬉しいです。私自身まだまだ勉強中なので、何か問題がありましたら教えてください！