暗号化
暗号化とは、データを第三者に読み取られないように変換する技術です。
暗号化には主に以下の2種類があります。
共通鍵暗号
- 仕組み: 暗号化と復号に同じ鍵を使用
- 例: AES
- 特徴: 高速だが、クライアントとサーバー間の鍵の安全な共有が課題
公開鍵暗号
- 仕組み: 暗号化に公開鍵、復号に秘密鍵を使用
- 例: RSA
- 特徴: 鍵の共有が容易ですが、処理速度が遅い
Webアプリケーションにおける暗号化の役割
データ通信の暗号化
WebではTLS(Transport Layer Security)が一般的です。TLSは、クライアントとサーバー間のデータ通信を暗号化して、盗聴や改ざんを防ぎます。
現在のWebアプリケーションでは、HTTP通信を暗号化するHTTPSが標準です。
TLSの仕組み
- クライアントがサーバーに接続をリクエスト(ハンドシェイク)
- サーバーは証明書を提示して自身を証明
- クライアントは証明書を検証し、共通鍵を生成
- 公開鍵を使って共通鍵を暗号化し、サーバーに送信
- サーバーは秘密鍵で共通鍵を復号し、暗号化通信を開始
パスワードのハッシュ化
Webアプリケーションでユーザーのパスワードを保存するとき、平文で保存してはいけません。
代わりに、ハッシュ化を利用します。
ハッシュ関数
- 入力データを固定長のハッシュ値に変換
- 同じ入力は必ず同じハッシュ値になる
- 一方向性
- ハッシュ値から元のデータを復元できない
推奨されるハッシュアルゴリズム
- bcrypt: ソルトを自動生成し、安全性が高い
- argon2: パスワードハッシュの最新アルゴリズム
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の仕組み
- 暗号鍵とデータをハッシュ関数に入力
- HMAC値をデータと共に送信
- 受信側で同じ手順でハッシュ値を生成し、比較
- ハッシュ値が一致すればデータが改ざんされていないことを確認
- ハッシュ値が異なればデータが改ざんされた可能性がある
- 使用例: Webトークン(JWT)の署名検証
JWT(JSON Web Token)
JWTは、認証情報を安全にやり取りするためのフォーマットです。
署名(HMACまたはRSA)を使用して改ざんを防止します。
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コードが埋め込まれるのを防ぎます
import "html/template"
func renderTemplate(w http.ResponseWriter, data string) {
tmpl := template.Must(template.New("example").Parse("{{.}}"))
tmpl.Execute(w, data) // 自動的にエスケープされる
}
クロスサイトリクエストフォージェリ(CSRF)対策
- フォームやAPIリクエストにCSRFトークンを含めることで、意図しないリクエストを防ぎます
- サーバー側でトークンの一致を検証します
<input type="hidden" name="_csrf" value="CSRF_TOKEN">