المصادقة الآمنة في Go: كتابة كود بدون ثغرات

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا المصادقة في Go ليست مزحة؟

\n\n

Go هي اللغة التي يتم اختيارها للسرعة والموثوقية. ولكن حتى أسرع خادم يتحول إلى قرع إذا كان من الممكن سرقة كلمات المرور من خلاله. المصادقة الآمنة في تطبيقات Go ليست مجرد "تثبيت مكتبة ونسيان الأمر". إنها علم كامل: كيفية تخزين كلمات المرور، وكيف لا تفشل في JWT، وكيف تحمي من CSRF عندما يكون كل شيء مشتعلًا.

\n\n

في هذه المقالة، لن نلقي محاضرات حول النظرية. سنكتب كودًا. كودًا حقيقيًا وعمليًا يمكنك إدراجه فورًا في مشروعك. سنتناول ثلاثة أركان للأمان: تجزئة كلمات المرور (bcrypt)، العمل مع رموز JWT، والحماية من تزوير الطلبات عبر المواقع (CSRF).

\n\n

تجزئة كلمات المرور بشكل صحيح: لماذا md5 شر

\n\n

الخطوة الأولى نحو الأمان هي أبدًا، اسمع، أبدًا لا تخزن كلمات المرور بنص واضح. حتى لو كنت تثق في قاعدة بياناتك. يمكن أن يحدث الاختراق لأي شخص (اسأل Yahoo أو Adobe). التجزئة الصحيحة هي درعك.

\n\n

في Go، الخيار القياسي هو golang.org/x/crypto/bcrypt. لماذا bcrypt بالتحديد؟ إنه مقاوم لهجمات القوة الغاشمة (يتضمن "ملح" ويبطئ العمليات الحسابية). يتم حساب MD5 أو SHA-1 في ميكروثانية. Bcrypt يستغرق مئات الميلي ثانية. بالنسبة للمهاجم، هذه كارثة، وبالنسبة للمستخدم، فهي غير ملحوظة.

\n\n

مثال: تسجيل مستخدم

\n\n
package main\n\nimport (\n    "fmt"\n    "golang.org/x/crypto/bcrypt"\n)\n\nfunc hashPassword(password string) (string, error) {\n    bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)\n    return string(bytes), err\n}\n\nfunc checkPasswordHash(password, hash string) bool {\n    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))\n    return err == nil\n}\n\nfunc main() {\n    password := "mySecretPass123"\n    hash, _ := hashPassword(password)\n\n    fmt.Println("كلمة المرور:", password)\n    fmt.Println("التجزئة:  ", hash)\n\n    match := checkPasswordHash(password, hash)\n    fmt.Println("متطابقة:", match)\n}
\n\n

لاحظ: bcrypt.DefaultCost هو 10 تكرارات. للإنتاج، يمكن زيادته إلى 12-14، لكن لا تبالغ وإلا سينهار الخادم تحت الحمل. اختبر على أجهزتك.

\n\n

التحقق من كلمة المرور عند تسجيل الدخول

\n\n

الأمر بسيط: نأخذ كلمة المرور من النموذج، نأخذ التجزئة من قاعدة البيانات، نقارن باستخدام bcrypt.CompareHashAndPassword. إذا أعادت nil، فالمستخدم صحيح. إذا كان هناك خطأ، نرسل 401.

\n\n
func loginHandler(w http.ResponseWriter, r *http.Request) {\n    // نحصل على password من النموذج\n    storedHash := getUserHashFromDB(r.FormValue("email"))\n    if !checkPasswordHash(r.FormValue("password"), storedHash) {\n        http.Error(w, "كلمة مرور غير صحيحة", http.StatusUnauthorized)\n        return\n    }\n    // ننشئ جلسة أو رمزًا\n}
\n\n

JWT: مصادقة خفيفة مع خدعة

\n\n

بعد أن يدخل المستخدم كلمة المرور، يجب إصدار "تصريح" له. إحدى الطرق الشائعة هي JWT (JSON Web Token). هذا رمز مضغوط يحتوي على بيانات المستخدم وتوقيع. لكن الشيطان يكمن في التفاصيل: التنفيذ غير الصحيح لـ JWT قد يجعل تطبيقك عرضة للثغرات.

\n\n

التوليد الصحيح لـ JWT في Go

\n\n

نستخدم المكتبة github.com/golang-jwt/jwt/v5. لا تكتب أبدًا حلاً مخصصًا للتشفير - استخدم فقط المكتبات الموثوقة.

\n\n
import (\n    "time"\n    "github.com/golang-jwt/jwt/v5"\n)\n\nvar jwtKey = []byte("my_secret_key_which_must_be_long_and_random")\n\ntype Claims struct {\n    Username string `json:"username"`\n    jwt.RegisteredClaims\n}\n\nfunc generateToken(username string) (string, error) {\n    expirationTime := time.Now().Add(15 * time.Minute)\n    claims := &Claims{\n        Username: username,\n        RegisteredClaims: jwt.RegisteredClaims{\n            ExpiresAt: jwt.NewNumericDate(expirationTime),\n        },\n    }\n\n    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n    return token.SignedS                

المدونات

توصيات الكتب