知っておくべき10個のAPIセキュリティ・ベスト・プラクティス
この記事は弊社のフルスタックエンジニア:Ashish Dhakalが作成した記事です。
マイクロ・サービス・アーキテクチャの台頭により、APIは最新のウェブ・アプリケーションのバックボーンとなっている。しかし、APIが提供する利便性とともに、APIはセキュリティ上の課題ももたらしており、積極的に対処する必要がある。この記事では、goとginを例に、APIのセキュリティを強化するために必要なテクニックをいくつか紹介する。
APIセキュリティについて
APIセキュリティは、APIを通じてクライアントとサーバ間でやり取りされるデータの完全性、機密性、可用性を保護します。堅牢なセキュリティ対策を導入することは、ユーザーの認証、機密データの保護、不正アクセスの防止など、潜在的なリスクを軽減するために極めて重要です。
1. 入力検証とサニタイズ
入力検証は、SQLインジェクション、クロスサイトスクリプティング(XSS)、コマンドインジェクションなどの様々な攻撃を防ぐために非常に重要です。Ginでは、バインディングやバリデータのようなライブラリを使って、入力されたリクエストデータを検証できます。
例:
type User struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
func main() {
r := gin.Default()
r.POST("/login", func(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Validate user input further and process login
if validationErr := validator.Validate.Struct(&user); validationErr != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
})
}
2. 認証
強力な認証メカニズムを実装することは、APIエンドポイントへのアクセスを制御するために不可欠である。認証にはJWT(JSON Web Tokens)やFirebaseのような技術を使用する。
func createToken(user User) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["username"] = user.Username
// Add other claims like expiration time, etc.
tokenString, err := token.SignedString([]byte("secret"))
if err != nil {
return "", err
}
return tokenString, nil
}
3. 認可/RBAC(役割ベースのアクセス制御):
ユーザーの役割に基づいて特定のエンドポイントへのアクセスを制限するために、役割ベースのアクセス制御を実施する。APIエンドポイント用のカスタム権限ミドルウェアを作成し、使用することができる。システムに複数のタイプのユーザー[管理者、スタッフ、閲覧者]がいる場合は、RBACを実装する必要がある。
func StaffUserAccessMiddleware(c *gin.Context) {
userId := c.GetHeader("user_id")
// Check if user has staff role
if !IsStaff(userId) {
// Unauthorized access, return 401 status
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
c.Next()
}
func IsStaff(userId) bool {
// checkif the user has specified role or not
staffUser := db.Table("user_roles").Where("user_id = ? AND Role = 'staff'", user_Id)
if staffUser {
return true
}
return false
}
func main() {
r := gin.Default()
// Protected route with authorization middleware
r.GET("/staff-only", StaffUserAccessMiddleware, func(c *gin.Context) {
c.String(http.StatusOK, "autorized")
})
}
4. 安全なパスワード保管
ユーザーのパスワードを保存するときは、決して平文で保存しないこと。データベースに保存する前に、bcryptのような強力な暗号化ハッシュ関数を使用して、パスワードを安全にハッシュ化してください。
例:
// hash pasword
func hashText(text string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(text), bcrypt.DefaultCost)
}
func saveUser(c *gin.Context) {
password := "abcabc"
// get hash for plain password
hashPassword := hashText(password)
db.Save("password = ?", hashPassword)
}
5. SQLインジェクションの防止
SQLインジェクションはサイバー攻撃の一種で、悪意のあるSQLコードがウェブアプリケーションの入力フィールドに挿入され、アプリケーションを騙して意図しないSQLコマンドを実行させます。これは、不正アクセス、データ漏洩、さらにはデータ操作につながる可能性があります。
文字列の連結を使って動的にSQLクエリーを構築することは避ける。代わりに、SQLインジェクション攻撃を防ぐために、パラメータ化されたクエリやプリペアド・ステートメントを使用してください。
例:
// function to fetch user by ID (vulnerable to SQL injection)
func getUser(id string) (user, error) {
// Construct SQL query (Vulnerable to SQL injection)
query := fmt.Sprintf("SELECT id, username FROM users WHERE id = %s", userID)
rows, err := db.Raw(query).Err
return rows, err
}
// function to fetch user by ID (secure against SQL injection)
func getUser(id string) (user, error) {
// Prepare SQL statement with placeholders
query := "SELECT id, username FROM users WHERE id = ?"
rows, err := db.Query(query, id)
return rows, err
}
6. ロギング
ロギングが適切に設定されていない場合、ユーザー認証情報、APIキー、その他の機密データなどの機密情報が不注意でログに記録される可能性がある。ログが適切に保護されていない場合、あるいは、ログが権限のない者によってアクセスされた場合、これらの情報が漏洩する可能性がある。
例:
7. CSRF トークン
セッションごとに一意なトークンを生成し、受信リクエストでそれを検証することで CSRF 防御を実装する。CSRF 攻撃は、ウェブ・アプリケーションがユーザの認証情報に対して持つ固有の信頼を悪用するため、特に危険です。
func main() {
r := gin.Default()
// Middleware to generate and verify CSRF tokens
csrfMiddleware := csrf.Middleware(csrf.Options{
Secret: "xxx",
TokenLength: 32,
})
// Protected route using CSRF middleware
r.POST("/protected", csrfMiddleware, func(c *gin.Context) {
c.String(200, "CSRF token verified!")
})
}
8. XSSに対する防御メカニズムの実装
XSS(クロスサイト・スクリプティング)は、ウェブ・アプリケーションにおけるセキュリティの脆弱性です。攻撃者が悪意のあるスクリプト(通常はJavaScriptで記述)を、他のユーザーが閲覧するウェブページに注入することで発生します。これらのスクリプトは、被害者のブラウザのコンテキスト内で実行され、攻撃者は機密情報を盗んだり、ページの外観を操作したり、その他の悪意のあるアクションを実行したりすることができます。
XSS 脆弱性は、(クッキーやセッショントークンのような)機密情報の窃取、ユーザアカウントへの不正アクセス、ウェブサ イトの改ざん、さらにはウェブアプリケーション全体の完全な侵害など、深刻な結果をもたらす可能性があります。XSS 攻撃を防ぐために、ウェブ・アプリケーションに適切な入力検証、出力エンコーディング、その他のセキュリ ティ対策を実装する必要があります。
例:
I. 入力検証:
入力検証ライブラリのバリデーターを使用して、ユーザーの入力が期待される基準を満たしていることを確認します。入力バリデーションについては、先に説明したとおりである。
II. 出力エンコーディング:
func main() {
router := gin.Default()
router.GET("/data", func(c *gin.Context) {
userInput := "<script>alert('XSS attack');</script>"
encodedInput := html.EscapeString(userInput)
c.JSON(http.StatusOK, gin.H{"data": encodedInput})
})
router.Run(":8080")
}
III. コンテンツ・セキュリティ・ポリシー(CSP):
func main() {
router := gin.Default()
router.Use(func(c *gin.Context) {
c.Header("Content-Security-Policy", "default-src 'self'")
c.Next()
})
// Define routes and handlers here
router.Run(":8080")
}
9. レート制限とスロットリング
レート制限の欠如は、ブルートフォース攻撃のようなセキュリティの脆弱性にAPIをさらす可能性があります。 レート制限は、リクエストのレートを制限することで、このようなリスクを軽減するのに役立ちます。
10. グレースフル・エラー処理
APIのための標準化されたエラー・レスポンス・フォーマットのセットを定義する。これには、一貫したHTTPステータスコード(例えば、クライアントエラーは4xx、サーバーエラーは5xx)と、明確で簡潔なエラーメッセージが含まれる。情報漏洩を防ぐため、スタックトレースや詳細なシステム内部情報など、機密情報をエラーメッセージで明らかにすることは避ける。.
結論
セキュアなAPIを開発することは、一回限りの作業ではなく、ベストプラクティスの順守と警戒を必要とする継続的なプロセスです。この記事で概説したセキュリティガイドラインに従うことで、セキュリティ侵害のリスクを大幅に低減し、APIの機密性、完全性、可用性を確保することができる。
参考文献:
https://datadome.co/guides/api-protection/api-security-risks-how-to-mitigate/
https://cloud.google.com/sql/docs/mysql/best-practices
協業開発及び開発パートナーをお探しのお客様へ
弊社は、ネパールに海外拠点を持ち、生成AI、モバイルアプリ、システム開発を中心に事業を展開する企業です。
自社サービスの開発経験を活かし、クライアント様と共に事業を創造することを重視し、創業以来、スタートアップから中小企業、大手企業、自治体まで、幅広い開発実績があります。プロダクトはユーザーが使いやすいように設計しており、企画から開発、保守運用まで対応しています。開発技術を厳選し限定することで、セキュリティ、プロダクトの品質向上に努めており、事業開発に関する課題を深く理解し、最適なご提案が可能です
お問い合わせはこちらから:
お問い合わせフォーム:https://readytowork.jp/
直通番号:080-8940-7169