SQLインジェクション ってなに？

# SQLインジェクション ってなに？

SQLインジェクション（SQL Injection）とは、ウェブアプリケーションのセキュリティ脆弱性を利用して、悪意のあるユーザーがSQLクエリを操作し、データベースに対して不正な操作を行う攻撃手法です。
この攻撃により、攻撃者はデータベース内の機密情報を取得したり、データを削除・改ざんしたり、さらにはデータベース自体を操作したりすることができます。

## 例え話

図書館を想像してみてください。
図書館には大きなカードボックスがあって、みんなの名前と借りた本の情報が書いてあります。

1. **通常の使い方**：
   図書館の人に「自分の今まで借りた本を教えてください」とお願いすると、図書館の人はカードを探して「あなたは『ドラえもん』を借りています」と教えてくれます。

2. **SQLインジェクションの使い方**：
   悪い人は「自分」**または**「みんなの情報を全部見せて」とお願いします。
   
   図書館の人は混乱して、あなたの情報だけじゃなくて、みんなの秘密の情報まで全部見せてしまうみたいなイメージです。

### コンピュータでの例

1. **お店のウェブサイトで**：
   - 通常は「商品ID: 42」で検索すると、その商品だけが表示されます
   - 悪い人は「商品ID: 42 または全ての商品とお客さんの情報も見せて」と入力します
   - 最悪のケース、全ての秘密の情報を見せてしまいます

2. **ログインするとき**：
   - 通常は「ユーザー名とパスワード」を入れます
   - 悪い人は「どんなユーザー名でも、パスワードはなんでもOK」というような特別な言葉を入れます
   - すると、パスワードを知らなくても入れてしまうことがあります

**はい、こんな感じになりますが 詳しく説明↓↓↓**

## 詳細解説

### 基本的なSQL文の例

データベースへの一般的な問い合わせは以下のような形です：

```sql
SELECT * FROM users WHERE username = 'tanaka' AND password = 'secret123';
```

この問い合わせは「usersテーブルからユーザー名が'tanaka'でパスワードが'secret123'のデータを取得する」という意味です。

### 悪用の例

攻撃者は入力フィールドに以下のような値を入力します：

```
' OR '1'='1' --
```

すると、最終的なSQL文はこうなります：

```sql
SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '何か';
```

この文は：
1. `username = ''` (空のユーザー名) OR
2. `'1'='1'` (これは常に真)
3. `--` 以降はコメントアウトされるので実行されない

つまり「全てのユーザー」を返すという結果になり、認証がバイパスされる。

## SQLインジェクションの種類

### 1. クラシカルSQLインジェクション（Classic SQL Injection）
**概要**  
ユーザー入力をそのままSQL文に組み込むことで、不正なSQLコードを実行させる基本的な手法です。

**例**  
```sql
SELECT * FROM users WHERE username = 'admin' AND password = 'password';
```
ユーザーが `admin' --` という入力をすると、以下のようにSQL文が変わり、パスワードチェックが回避されます。  
```sql
SELECT * FROM users WHERE username = 'admin' -- ' AND password = 'password';
```
※ `--` はSQLのコメント記号なので、それ以降の条件（パスワードチェック）が無視される。

---

### 2. ブラインドSQLインジェクション（Blind SQL Injection）

ブラインドSQLインジェクションは、**エラーメッセージやデータの直接的な表示がない場合に、システムの動作や応答時間の違いを利用してデータベースの情報を推測する攻撃手法** です。

通常のSQLインジェクションは、エラーメッセージや取得したデータを見て攻撃を行いますが、ブラインドSQLインジェクションでは、サーバーのレスポンス（成功・失敗の違い、応答時間の変化）から間接的に情報を得るのが特徴です。

**ブラインドSQLインジェクションの種類**

ブラインドSQLインジェクションには、**ブールベース（Boolean-based）** と **時間ベース（Time-based）** の2種類があります。


#### **1. ブールベースSQLインジェクション（Boolean-based SQL Injection）**
**概要**  
データベースのクエリの結果が「真（TRUE）」か「偽（FALSE）」かによって、Webページの挙動が変化することを利用してデータを推測する手法。

**例**  
```sql
SELECT * FROM users WHERE username = 'admin' AND password = 'password';
```
通常、正しい `username` と `password` を入力するとログインできます。  
しかし、次のように `username` に `admin' AND 1=1 --` を入力すると…

```sql
SELECT * FROM users WHERE username = 'admin' AND 1=1 -- ' AND password = 'password';
```
これは常に `TRUE` となるため、データが取得できる可能性があります。  
逆に `admin' AND 1=0 --` を入力すると `FALSE` となるため、データは取得できません。

#### **攻撃の流れ**
1. `admin' AND 1=1 --` を入力 → ログイン成功（TRUE）なら、`admin` ユーザーは存在すると推測。
2. `admin' AND 1=0 --` を入力 → ログイン失敗（FALSE）なら、条件式が機能していると確認。
3. `admin' AND (SELECT LENGTH(password) FROM users WHERE username='admin') > 5 --` を入力 →  
   ページの変化を見て `password` の長さを推測。
4. さらに `admin' AND (ASCII(SUBSTRING(password,1,1)) > 100) --` などを使い、  
   パスワードの各文字のASCII値を推測し、パスワード全体を取得。

📌 **ポイント**  
- **エラーメッセージが表示されなくても、ページの挙動の違いを利用して情報を抜き取ることができる！**

#### **2. 時間ベースSQLインジェクション（Time-based SQL Injection）**
**概要**  
SQLの `SLEEP()` や `BENCHMARK()` 関数を使い、意図的に処理時間を遅延させることで、条件の真偽を判定する手法。

**例**  
以下のSQL文を実行すると、条件が `TRUE` なら5秒遅延し、`FALSE` ならすぐに応答します。

```sql
SELECT * FROM users WHERE username = 'admin' AND IF(1=1, SLEEP(5), 0);
```
✅ `1=1`（真）の場合 → **5秒遅延**  
❌ `1=0`（偽）の場合 → **遅延なし**

#### **攻撃の流れ**
1. `admin' AND IF(1=1, SLEEP(5), 0) --` を入力 → 5秒遅延なら条件が `TRUE` と判断。
2. `admin' AND IF(1=0, SLEEP(5), 0) --` を入力 → 遅延なしなら条件が `FALSE` と判断。
3. `admin' AND IF((SELECT LENGTH(password) FROM users WHERE username='admin') > 5, SLEEP(5), 0) --`  
   → 5秒遅延なら `password` の長さは5文字より大きい。
4. `admin' AND IF(ASCII(SUBSTRING(password,1,1)) > 100, SLEEP(5), 0) --`  
   → 1文字目のASCIIコードを判定し、1文字ずつパスワードを特定。

📌 **ポイント**  
- **エラーメッセージを一切表示しないWebアプリでも、レスポンス時間を測ることでデータを盗み出せる！**
- **ログにエラーメッセージが残らないため、攻撃が発見されにくい！**

#### **ブラインドSQLインジェクションの危険性**
- **データ漏洩**：ユーザー名やパスワードなどの機密情報が推測される。
- **検出が難しい**：エラーメッセージを出さず、通常のリクエストと区別しづらい。
- **時間がかかるが確実に攻撃できる**：1文字ずつ推測するため、完全な情報を得るまでに時間がかかるが、確実に情報を得ることが可能。

---

### 3. エラーベースSQLインジェクション（Error-based SQL Injection）
**概要**  
エラーメッセージの内容からデータベースの情報を取得する手法。

**例**  
```sql
SELECT * FROM users WHERE id = 1 UNION SELECT 1, database(), 3, 4;
```
もしこのSQLが実行されると、エラーメッセージの中に `database()` の結果（現在のデータベース名）が表示される可能性がある。

---

### 4. ユニオンベースSQLインジェクション（Union-based SQL Injection）
**概要**  
`UNION` を使用して、攻撃者が意図的に追加したデータを取得する手法。

**例**  
```sql
SELECT id, username, password FROM users WHERE id = 1 UNION SELECT 1, 'hacker', 'password';
```
このSQLが実行されると、データベース内に本来存在しない `('hacker', 'password')` という行が結果として表示される可能性がある。

---

### 5. スタックドクエリSQLインジェクション（Stacked Query SQL Injection）
**概要**  
複数のSQLクエリを `;` で区切って連続実行させることで、データの改ざんや削除を行う手法。

**例**  
```sql
SELECT * FROM users WHERE username = 'admin'; DROP TABLE users;
```
もし `admin'; DROP TABLE users; --` という入力を受け付けると、`users` テーブルが削除される可能性がある。

---

このようにSQLインジェクションには多くの種類があり、それぞれ異なる手法でデータを不正に取得・改ざんします。防ぐためには、適切なプログラミング（プレースホルダーの使用）やWAFの導入が重要です。

## SQLインジェクション攻撃の検出

### 1. WAF（Webアプリケーションファイアウォール）
WAFを使用して、SQLインジェクション攻撃のパターンを検出してブロックします。

### 2. ログの監視
不審なデータベースクエリや失敗したログイン試行をモニタリングします。

### 3. 自動テスト
ツールなど使用して、自分のアプリケーションの脆弱性を定期的にテストします。

SQLインジェクションはウェブアプリケーションに対する最も一般的で危険な攻撃の一つです。
適切な対策を講じることで、アプリケーションとユーザーデータを保護することができます。

## **SQLインジェクションの対策 防御方法**

### **1. プレースホルダー（プリペアドステートメント）を使う**

#### ✅ **安全なコード**
**Ruby on Rails**

```ruby
User.where("email = ?", user_params[:email])
```
- `?` を使うことで、入力値が自動的にエスケープされるため、SQL インジェクションが防止されます。

#### ❌ **危険なコード**
```ruby
User.where("email = '#{params[:email]}'")
```
- 文字列を直接埋め込むと、SQL インジェクションのリスクがあります。

**Django の ORM を利用の場合**

```python
from myapp.models import User

# ユーザー入力
email = "user@example.com"

# ✅ 安全なクエリ
user = User.objects.get(email=email)
```
---

### **2. クエリのメソッドチェーンを使う**
Rails では `find_by` や `where` などのメソッドチェーンを使うことで、安全にデータを取得できます。

#### ✅ **安全なコード**
```ruby
User.find_by(email: user_params[:email])
```
- `find_by` は自動的にエスケープ処理を行うため、SQL インジェクションを防げます。

#### ❌ **危険なコード**
```ruby
User.where("email = '#{params[:email]}'").first
```
- 直接 SQL を埋め込むのは危険。

---

### **3. sanitize 系メソッドを使う**
Rails には、SQL のエスケープを手動で行うための `sanitize_sql_for_conditions`、`sanitize_sql_array`、`sanitize` などのメソッドが用意されています。  
これらは、手動で SQL クエリを作成する必要がある場合に便利です。

#### ✅ **安全なコード**
```ruby
safe_sql = ActiveRecord::Base.sanitize_sql(["email = ?", user_params[:email]])

User.where(safe_sql)
```
- `sanitize_sql` を使うことで、ユーザー入力が安全に処理されます。

#### ❌ **危険なコード**
```ruby
User.where("email = '#{params[:email]}'")
```
- 直接変数を埋め込むのは危険。

---

### **4. ストロングパラメーター（Strong Parameters）を活用**
Rails では **ストロングパラメーター** を使うことで、不正なパラメーターの混入を防ぐことができます。

#### ✅ **安全なコード**
```ruby
params.require(:user).permit(:email, :password)
```
- 許可されたパラメーターのみを受け取るため、不正なデータをブロックできます。

---

### **5. SQL の LIKE クエリを使うときの注意**
`LIKE` クエリを使う場合、`%` や `_` などのワイルドカードを適切に処理しないと、意図しない SQL が実行される可能性があります。

#### ❌ **危険なコード**
```ruby
User.where("email LIKE '%#{params[:email]}%'")
```
- `params[:email]` に `%` を含めると、すべてのデータが取得される可能性がある。

#### ✅ **安全なコード**
Rails には `sanitize_sql_like` メソッドがあり、ワイルドカードを適切に処理できます。

```ruby
User.where("email LIKE ?", "%#{ActiveRecord::Base.sanitize_sql_like(user_params[:email])}%")
```
- `sanitize_sql_like` を使うことで、`%` や `_` を安全に処理できます。

---

### **6. `order` などでの SQL インジェクション防止**
`order` メソッドにユーザー入力を直接渡すと、SQL インジェクションのリスクがあります。

#### ❌ **危険なコード**
```ruby
User.order("created_at #{params[:order]}") # 'ASC' or 'DESC' のみ許可すべき
```
- ユーザーが `'; DROP TABLE users; --` などを入力すると、データベースを破壊できる可能性がある。

#### ✅ **安全なコード**
```ruby
if %w[ASC DESC].include?(user_params[:order].upcase)
  User.order("created_at #{user_params[:order].upcase}")
else
  User.order("created_at DESC") # デフォルトの値
end
```
- 許可する値を限定することで、SQL インジェクションを防ぐ。

---

### **7. エラーメッセージの制限**
本番環境では **詳細なエラーメッセージを表示しない** ようにすることで、攻撃者に情報を与えないようにします。

#### ✅ **安全な設定**
`config/environments/production.rb` でエラーメッセージを制限する：
```ruby
config.consider_all_requests_local = false
```
- これにより、デバッグ情報や SQL のエラーメッセージが本番環境で表示されなくなります。

---

## **まとめ**

Rails（ActiveRecord）などのフレームワークを正しく使用すれば、SQL インジェクションのリスクを減らせますが、**手動で SQL を組み立てる場合や `LIKE`・`order` を使う際など どのようなSQLクエリが発行されるかなど注意が必要です。**  
また、本番環境では詳細なエラーメッセージを表示しないことなど、攻撃者に情報を与えないようにするのも重要です。  

安全なコーディングを心がけましょう！ 🚀