【Goで学ぶコンピューターサイエンス】暗号化とセキュリティの基礎

## 暗号化
暗号化とは、データを第三者に読み取られないように変換する技術です。  
暗号化には主に以下の2種類があります。

### **共通鍵暗号**
- **仕組み**: 暗号化と復号に同じ鍵を使用
- **例**: AES
- **特徴**: 高速だが、クライアントとサーバー間の鍵の安全な共有が課題

### **公開鍵暗号**
- **仕組み**: 暗号化に公開鍵、復号に秘密鍵を使用
- **例**: RSA
- **特徴**: 鍵の共有が容易ですが、処理速度が遅い

### Webアプリケーションにおける暗号化の役割

#### **データ通信の暗号化**
WebではTLS（Transport Layer Security）が一般的です。TLSは、クライアントとサーバー間のデータ通信を暗号化して、盗聴や改ざんを防ぎます。  
現在のWebアプリケーションでは、HTTP通信を暗号化する**HTTPS**が標準です。

#### **TLSの仕組み**
1. クライアントがサーバーに接続をリクエスト（ハンドシェイク）
2. サーバーは証明書を提示して自身を証明
3. クライアントは証明書を検証し、共通鍵を生成
4. 公開鍵を使って共通鍵を暗号化し、サーバーに送信
5. サーバーは秘密鍵で共通鍵を復号し、暗号化通信を開始

### パスワードのハッシュ化

Webアプリケーションでユーザーのパスワードを保存するとき、平文で保存してはいけません。  
代わりに、**ハッシュ化**を利用します。

#### **ハッシュ関数**
- 入力データを固定長のハッシュ値に変換
- 同じ入力は必ず同じハッシュ値になる
- 一方向性
  - ハッシュ値から元のデータを復元できない

#### **推奨されるハッシュアルゴリズム**
- **bcrypt**: ソルトを自動生成し、安全性が高い
- **argon2**: パスワードハッシュの最新アルゴリズム

#### **Goでのパスワードハッシュ化の例**
```go
package main

import (
	"fmt"
	"golang.org/x/crypto/bcrypt"
)

func main() {
	password := "securepassword123"
	
	// パスワードのハッシュ化
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		fmt.Println("Error hashing password:", err)
		return
	}
	fmt.Println("Hashed Password:", string(hashedPassword))
	
	// ハッシュと元のパスワードを比較
	err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
	if err != nil {
		fmt.Println("Password does not match")
	} else {
		fmt.Println("Password matches")
	}
}
```
```
Hashed Password: $2a$10$vuPUBtc3p0jFsamrUx1EsuRWKd2.JJME6A3RfEuKnS3VhwancP/KW
Password matches
```

### データ改ざんの防止

#### **HMAC（Hash-based Message Authentication Code）**
データが改ざんされていないことを確認するために使用されます。

#### HMACの仕組み
1. 暗号鍵とデータをハッシュ関数に入力
3. HMAC値をデータと共に送信
4. 受信側で同じ手順でハッシュ値を生成し、比較
5. ハッシュ値が一致すればデータが改ざんされていないことを確認
6. ハッシュ値が異なればデータが改ざんされた可能性がある
- **使用例**: Webトークン（JWT）の署名検証

#### **JWT（JSON Web Token）**
JWTは、認証情報を安全にやり取りするためのフォーマットです。  
署名（HMACまたはRSA）を使用して改ざんを防止します。

```go
package main

import (
	"fmt"
	"time"

	"github.com/golang-jwt/jwt/v4"
)

func main() {
	secretKey := []byte("secret-key")

	// JWTトークンの生成
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"username": "engineer_tacky",
		"exp":      time.Now().Add(time.Hour * 1).Unix(), // 1時間後に有効期限切れ
	})
	signedToken, err := token.SignedString(secretKey)
	if err != nil {
		fmt.Println("Error creating token:", err)
		return
	}
	fmt.Println("JWT Token:", signedToken)
}
```
```
JWT Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzQ0ODU2MTEsInVzZXJuYW1lIjoiZW5naW5lZXJfdGFja3kifQ.41omonoDo1QbWiV_mGieSed_svmftVrh0T-3yWtEDks
```

### セキュリティ対策

#### **HTTPSの導入**
- 全ての通信をHTTPSで保護
- Let's Encryptを使えば無料で証明書を取得可能(Certbotで更新を自動化できる)

#### SQLインジェクション対策
- プリペアドステートメントを使用して、SQL文をパラメータ化
```
SELECT * FROM users WHERE id = ?
```
- ORMの活用(GORMなど)
  - 直接SQL文を書く頻度を減らし、安全性を向上できます

#### クロスサイトスクリプティング（XSS）対策
- ユーザーが入力したデータを画面に表示する際、適切にエスケープ処理を行い、HTMLタグやJavaScriptコードが埋め込まれるのを防ぎます
```go
import "html/template"

func renderTemplate(w http.ResponseWriter, data string) {
  tmpl := template.Must(template.New("example").Parse("{{.}}"))
  tmpl.Execute(w, data) // 自動的にエスケープされる
}
```

#### クロスサイトリクエストフォージェリ（CSRF）対策
- フォームやAPIリクエストにCSRFトークンを含めることで、意図しないリクエストを防ぎます
- サーバー側でトークンの一致を検証します
```html
<input type="hidden" name="_csrf" value="CSRF_TOKEN">
```