ستصطدم بفك تشفير Base64 في Go في كل مكان — فحص JWT، مرفقات الملفات الثنائية، حمولات API من خدمات السحاب. تتعامل حزمة encoding/base64القياسية في Go مع كل هذا، لكن اختيار نوع الترميز الخاطئ (القياسي مقابل الآمن للـ URL، بحشو أو بلا حشو) هو المصدر الأكثر شيوعاً لأخطاء “illegal base64 data”. الميزة الكبيرة في Go هي أن الحزمة تأتي مدمجة في المكتبة القياسية ولا تحتاج إلى تثبيت أي تبعيات خارجية، كما أن رسائل الخطأ واضحة وتحدد موضع البايت الإشكالي بالضبط مما يُسهّل التصحيح. يغطي هذا الدليل StdEncoding، وURLEncoding، وRawURLEncoding، وفك التشفير بالبث باستخدام base64.NewDecoder، وفحص حمولات JWT، وأربعة أخطاء شائعة تعثر بها الجميع تقريباً في المرة الأولى. لفك التشفير الفوري في المتصفح، فك تشفير Base64 في ToolDeck يُنجز المهمة فوراً دون كتابة سطر واحد من الكود.
- ✓encoding/base64 جزء من المكتبة القياسية لـ Go — لا حاجة لـ go get
- ✓استخدم RawURLEncoding لرموز JWT ومعظم الـ APIs الحديثة (بلا حشو، أبجدية آمنة للـ URL)
- ✓يستخدم StdEncoding علامتي + و/ مع حشو =؛ أما URLEncoding فيستبدلهما بـ - و_ مع الإبقاء على الحشو
- ✓يُغلِّف base64.NewDecoder أي io.Reader لفك تشفير بث دون تحميل البيانات في الذاكرة
- ✓تحقق دائماً من الخطأ المُعاد — الحشو غير الصحيح والأبجدية الخاطئة تُنتج خطأ illegal base64 data
ما هو فك تشفير Base64؟
يُمثِّل ترميز Base64 البيانات الثنائية كنص ASCII باستخدام 64 محرفاً قابلاً للطباعة (A–Z، a–z، 0–9، بالإضافة إلى محرفين إضافيين). يعكس فك التشفير هذه العملية — فيحول تمثيل ASCII هذا إلى البايتات الأصلية. كل 4 محارف Base64 تُفكَّك إلى 3 بايتات بالضبط. وُجِد هذا النظام لأن كثيراً من طبقات النقل (البريد الإلكتروني، ترويسات HTTP، حقول JSON) مصممة للنصوص لا للبيانات الثنائية الخام. عند الحاجة إلى تضمين صورة أو ملف مضغوط أو مفتاح تشفير داخل حقل JSON أو ترويسة HTTP، يُحوَّل المحتوى الثنائي إلى سلسلة Base64 أولاً ثم يُرسَل كنص عادي. وهكذا تبدو العملية كاملةً من الترميز وحتى العودة للأصل:
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// Raw bytes → Base64 encoded → decoded back to raw bytes
original := []byte("service_token:xK9mP2qR")
// Encoded: "c2VydmljZV90b2tlbjp4SzltUDJxUg=="
encoded := base64.StdEncoding.EncodeToString(original)
decoded, _ := base64.StdEncoding.DecodeString(encoded)
fmt.Println(string(decoded) == string(original)) // true
}فك تشفير Base64 في Go باستخدام encoding/base64
تأتي حزمة encoding/base64 مع Go — بلا اعتماديات خارجية. توفر أربعة خيارات ترميز جاهزة للاستخدام. الدالة الأكثر استخداماً للمدخلات النصية هي DecodeString، التي تُعيد شريحة بايتات وخطأً. النمط الصحيح في Go هو التحقق دائماً من قيمة الخطأ قبل استخدام النتيجة — تجاهل الخطأ باستخدام _ شائع في أمثلة التعلم لكنه خطأ في كود الإنتاج، لأن أي محرف غير صالح في المدخلات يُعيد خطأً صامتاً لو أُهمل.
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
// Standard Base64 — the alphabet uses + and / with = padding
encoded := "eyJob3N0IjoiZGItcHJvZCF1cy1lYXN0LTEiLCJwb3J0Ijo1NDMyfQ=="
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
log.Fatalf("decode error: %v", err)
}
fmt.Println(string(decoded))
// {"host":"db-prod.us-east-1","port":5432}
}يعمل أسلوب Decode على شرائح البايتات بدلاً من السلاسل النصية، ويكتب النتيجة في مخزن مؤقت مُخصَّص مسبقاً. تحتاج إلى تحديد حجم المخزن بشكل صحيح — استخدم base64.StdEncoding.DecodedLen(len(src)) للحصول على الحجم الأقصى (قد يكون أكبر بضعة بايتات من الطول المُفكَّك الفعلي بسبب الحشو).
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
src := []byte("eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9")
dst := make([]byte, base64.RawStdEncoding.DecodedLen(len(src)))
n, err := base64.RawStdEncoding.Decode(dst, src)
if err != nil {
log.Fatalf("decode: %v", err)
}
fmt.Println(string(dst[:n]))
// {"host":"db-prod","port":5432}
}DecodedLen حداً أعلى وليس الطول الدقيق. استخدم دائماً قيمة الإعادة n من Decode لتقطيع النتيجة بشكل صحيح: dst[:n]. الفرق بين الحد الأعلى والطول الفعلي يتراوح عادةً بين 0 و2 بايت حسب عدد محارف الحشو في السلسلة المشفرة.StdEncoding مقابل URLEncoding — اختيار المتغير الصحيح
هنا تكمن المشكلة. تتضمن encoding/base64 في Go أربعة كائنات ترميز، واختيار الخاطئ منها كفيل بإعطائك خطأً. يعود الفرق إلى شيئين: الأبجدية والحشو. الأبجدية القياسية تستخدم المحرفين + و/ اللذين يتعارضان مع بنية عناوين URL عند الإدراج في سلاسل الاستعلام، لذا طورت RFC 4648 §5 متغيراً آمناً للـ URL يستبدلهما بـ - و_ دون الحاجة إلى percent-encoding. أما الحشو فهو محرف = المُضاف في نهاية السلسلة لإكمال مضاعفات 4 — بعض الأنظمة تحذفه (Raw variants) وبعضها يتطلبه.
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// JWT header payload — URL-safe, no padding
jwtHeader := "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMjMtMDkifQ"
// Wrong: StdEncoding fails on URL-safe input without padding
_, err1 := base64.StdEncoding.DecodeString(jwtHeader)
fmt.Println("StdEncoding error:", err1)
// StdEncoding error: illegal base64 data at input byte 43
// Correct: RawURLEncoding — no padding, URL-safe alphabet
decoded, err2 := base64.RawURLEncoding.DecodeString(jwtHeader)
fmt.Println("RawURLEncoding ok:", err2, "→", string(decoded))
// RawURLEncoding ok: <nil> → {"alg":"RS256","kid":"2023-09"}
}المتغيرات الأربعة بعبارات بسيطة:
من تجربتي: إذا جاءت البيانات من JWT أو تدفق OAuth أو SDK لمزود سحابي، ابدأ بـ RawURLEncoding. وإذا جاءت من مرفقات بريد إلكتروني أو نماذج ويب قديمة، جرِّب StdEncoding. رسالة الخطأ تخبرك دائماً بموضع البايت الدقيق الذي فشل فيه التشفير.
فك تشفير Base64 من ملف واستجابة API
قراءة ملف مشفر بـ Base64
أحياناً تُخزَّن الملفات الثنائية (الصور، ملفات PDF، الشهادات) على القرص مشفرةً بـ Base64. هذا شائع في بيئات CI/CD حيث تُحزَّم الشهادات والمفاتيح الخاصة كسلاسل Base64 في متغيرات البيئة أو ملفات الإعداد بدلاً من تخزينها كملفات ثنائية مباشرة. اقرأ الملف، احذف أي مسافات زائدة في النهاية، ثم افك التشفير:
package main
import (
"encoding/base64"
"fmt"
"log"
"os"
"strings"
)
func main() {
raw, err := os.ReadFile("certificate.pem.b64")
if err != nil {
log.Fatalf("read file: %v", err)
}
// Strip newlines — Base64 files often have line breaks every 76 chars
cleaned := strings.ReplaceAll(strings.TrimSpace(string(raw)), "\n", "")
decoded, err := base64.StdEncoding.DecodeString(cleaned)
if err != nil {
log.Fatalf("decode: %v", err)
}
if err := os.WriteFile("certificate.pem", decoded, 0600); err != nil {
log.Fatalf("write: %v", err)
}
fmt.Printf("decoded %d bytes → certificate.pem\n", len(decoded))
}فك تشفير حقل Base64 من استجابة JSON لـ API
كثيراً ما تُعيد APIs السحابية البيانات الثنائية (مفاتيح التشفير، الكتل الموقَّعة، الصور المصغرة) كسلاسل Base64 داخل JSON. تحقق من توثيق الـ API لمعرفة نوع الترميز المستخدم — بعض الخدمات كـ AWS Secrets Manager تستخدم StdEncoding مع حشو، بينما تستخدم خدمات أخرى كـ Google Cloud RawURLEncoding بلا حشو. افك تشفير JSON أولاً، ثم افك تشفير الحقل المستهدف:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
)
type SecretResponse struct {
Name string `json:"name"`
Payload string `json:"payload"` // Base64-encoded secret value
Version int `json:"version"`
}
func fetchAndDecodeSecret(secretURL string) ([]byte, error) {
resp, err := http.Get(secretURL)
if err != nil {
return nil, fmt.Errorf("http get: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
var secret SecretResponse
if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil {
return nil, fmt.Errorf("decode json: %w", err)
}
value, err := base64.StdEncoding.DecodeString(secret.Payload)
if err != nil {
return nil, fmt.Errorf("decode base64: %w", err)
}
return value, nil
}
func main() {
value, err := fetchAndDecodeSecret("https://api.example.com/secrets/db-password")
if err != nil {
log.Fatalf("fetch secret: %v", err)
}
fmt.Printf("secret value: %s\n", value)
}fmt.Errorf("decode base64: %w", err) بدلاً من فقدان السياق. تتضمن رسالة الخطأ الأصلية من encoding/base64 موضع البايت الذي وقع فيه الفشل، وهو مفيد أثناء التصحيح.بث الملفات الكبيرة المشفرة بـ Base64
تحميل ملف مشفر بـ Base64 بحجم 500 ميغابايت في الذاكرة باستخدام os.ReadFile ثم استدعاء DecodeString يستهلك نحو 750 ميغابايت من الذاكرة العشوائية (السلسلة المشفرة بالإضافة إلى البايتات المُفكَّكة). يُغلِّف base64.NewDecoder أي io.Reader ويفك التشفير في أجزاء، مما يُبقي استخدام الذاكرة ثابتاً تقريباً. هذا النمط مفيد بشكل خاص في سياقات الخوادم حيث تعالج طلبات متعددة في وقت واحد — تحميل الملف بأكمله لكل طلب سيُنهك الذاكرة المتاحة بسرعة، بينما يتيح البث معالجة ملفات ضخمة بموارد ثابتة ومحدودة. دمجه مع io.Copy يُنشئ خط بث سلس:
package main
import (
"encoding/base64"
"fmt"
"io"
"log"
"os"
)
func streamDecodeFile(srcPath, dstPath string) error {
src, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("open source: %w", err)
}
defer src.Close()
dst, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("create dest: %w", err)
}
defer dst.Close()
decoder := base64.NewDecoder(base64.StdEncoding, src)
written, err := io.Copy(dst, decoder)
if err != nil {
return fmt.Errorf("stream decode: %w", err)
}
fmt.Printf("written %d bytes to %s\n", written, dstPath)
return nil
}
func main() {
if err := streamDecodeFile("backup.tar.b64", "backup.tar"); err != nil {
log.Fatal(err)
}
}base64.NewDecoder بيانات Base64 نظيفة وغير منقطعة. إذا كان الملف المصدر يحتوي على فواصل أسطر (شائع في ملفات PEM والملفات المشفرة بـ MIME)، فغلِّف قارئ المصدر بقارئ يحذف الأسطر، أو عالج الملف مسبقاً لإزالة أسطر جديدة قبل البث.فك تشفير Base64 من سطر الأوامر
يلجأ معظم مطوري Go إلى سطر الأوامر أولاً عند التصحيح. كل نظام macOS وLinux يأتي مع base64؛ أما على Windows فلدى PowerShell ما يعادله. لفحص حمولات API بسرعة، هذه الأدوات أسرع من كتابة سكريبت Go. ملاحظة مهمة: في macOS تحتاج إلى استخدام الراية -D بدلاً من --decode بسبب اختلاف نسخة أداة base64المُثبَّتة افتراضياً. على Linux تعمل كلا الصيغتين. إذا كنت تتعامل مع مدخلات آمنة للـ URL (تحتوي على - و_)، يمكنك استبدالها قبل التمرير عبر الأنبوب: sed 's/-/+/g; s/_/\//g' ثم تمرير الناتج إلى أداة base64 القياسية.
# Decode a Base64 string (Linux / macOS)
echo "eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9" | base64 --decode
# {"host":"db-prod","port":5432}
# Decode and pretty-print with jq (pipe the JSON output)
echo "eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9" | base64 --decode | jq .
# {
# "host": "db-prod",
# "port": 5432
# }
# Decode a Base64-encoded file to binary
base64 --decode < encrypted_payload.b64 > encrypted_payload.bin
# macOS uses -D flag instead of --decode
echo "c2VjcmV0LXRva2Vu" | base64 -Dلفحص رموز JWT دون أي أدوات مثبتة، الصق الرمز في فك تشفير Base64 في ToolDeck — قسِّمه على النقاط وافك تشفير كل جزء.
الأداء: encoding/base64 سريع بالفعل لمعظم الحالات
على عكس Python حيث تُشكِّل مقارنة orjson مقابل json نقاشاً مهماً للأداء، فإن encoding/base64 في Go محسَّن بالأسمبلي وسريع فعلاً لمعظم أحمال العمل. المكتبة القياسية تُعالج مئات الميغابايتات في الثانية على الأجهزة الحديثة، وهو أداء يتجاوز ما تحتاجه الغالبية العظمى من التطبيقات. ومع ذلك، إذا كنت تعالج الملايين من السجلات في حلقة محكمة أو تبني خادماً يستقبل آلاف الطلبات في الثانية، filippo.io/base64 يوفر فك تشفير معجَّلاً بـ SIMD مع API متوافق تماماً.
go get filippo.io/base64
package main
import (
"fmt"
"log"
"filippo.io/base64"
)
func main() {
// Drop-in replacement — same API as encoding/base64
encoded := "eyJob3N0IjoiY2FjaGUtcHJvZCIsInBvcnQiOjYzNzl9"
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
log.Fatalf("decode: %v", err)
}
fmt.Println(string(decoded))
// {"host":"cache-prod","port":6379}
}يظهر تحسن الأداء بوضوح على amd64 مع دعم AVX2 — تُظهر اختبارات الأداء تحسناً في السرعة بمعدل 2–4 أضعاف على المدخلات الكبيرة. لفك تشفير استجابات API اليومية (بضع مئات من البايتات في كل مرة)، التزم بالمكتبة القياسية. قبل إضافة أي اعتمادية خارجية لاعتبارات الأداء، تحقق باستخدام pprof أن فك تشفير Base64 هو فعلاً عنق الزجاجة — في الغالب تكون إدخال/إخراج الشبكة أو تسلسل JSON هي السبب الحقيقي للتأخير. تذكر أيضاً أن إضافة تبعية خارجية لها تكلفة صيانة طويلة المدى تفوق في كثير من الأحيان مكسب الأداء الهامشي في حالات الاستخدام العادية وليس الحالات الحدية فقط.
فك تشفير حمولة JWT بتنسيق Base64 في Go
فحص JWT ضروري في كل خدمة خلفية تقريباً. في تجربتي، تدور معظم جلسات التصحيح حول سؤال “ما الذي يحتوي عليه هذا الرمز فعلاً؟” — ويمكنك الإجابة على ذلك دون استيراد مكتبة JWT كاملة. تذكر أن هذا الفك تشفير بدون التحقق من التوقيع: أنت تقرأ المحتوى فقط دون التأكد من صحة الرمز. JWT يستخدم دائماً RawURLEncoding كما هو محدد في RFC 7519 — الحشو محذوف والأبجدية آمنة للـ URL. في كود الإنتاج، يجب إجراء التحقق من التوقيع بشكل منفصل باستخدام مكتبة متخصصة. الرمز يتكون من ثلاثة أجزاء مشفرة بـ Base64url مفصولة بنقاط. الجزء الأوسط هو الحمولة التي تهمك فعلاً:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
"strings"
)
type JWTPayload struct {
Subject string `json:"sub"`
Issuer string `json:"iss"`
Expiry int64 `json:"exp"`
Roles []string `json:"roles"`
}
func decodeJWTPayload(token string) (*JWTPayload, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid JWT: expected 3 segments, got %d", len(parts))
}
// JWT uses RawURLEncoding — URL-safe alphabet, no = padding
raw, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("decode payload: %w", err)
}
var payload JWTPayload
if err := json.Unmarshal(raw, &payload); err != nil {
return nil, fmt.Errorf("unmarshal payload: %w", err)
}
return &payload, nil
}
func main() {
token := "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c3ItNDQyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJhdWRpdG9yIl19.SIGNATURE"
payload, err := decodeJWTPayload(token)
if err != nil {
log.Fatalf("jwt: %v", err)
}
fmt.Printf("Subject: %s\n", payload.Subject)
fmt.Printf("Issuer: %s\n", payload.Issuer)
fmt.Printf("Roles: %v\n", payload.Roles)
// Subject: usr-442
// Issuer: auth.example.com
// Roles: [admin auditor]
}الأخطاء الشائعة
صادفت هذه الأخطاء الأربعة في مراجعات كود حقيقية — الخطآن الأولان يظهران تقريباً في كل مرة يُضيف فيها أحدهم مزود مصادقة جديداً. ما يجمعها هو أنها جميعاً تُنتج خطأ وقت التشغيل لا خطأ وقت الترجمة، مما يعني أن الكود يُترجَم بنجاح لكنه يفشل على مدخلات حقيقية في بيئة الإنتاج. اختبار الكود مع مدخلات متنوعة في مرحلة التطوير هو الطريقة الوحيدة لاكتشافها مبكراً.
المشكلة: تستخدم رموز JWT ورموز OAuth الأبجدية الآمنة لـ URL (- و_). تمريرها إلى StdEncoding.DecodeString يفشل بخطأ 'illegal base64 data'.
الحل: تحقق من مصدر المدخلات: الرموز من أنظمة المصادقة تستخدم RawURLEncoding؛ أما المرفقات الثنائية فتستخدم StdEncoding.
// JWT header — correct encoding
token := "eyJhbGciOiJSUzI1NiJ9"
decoded, err := base64.RawURLEncoding.DecodeString(token)
// decoded: {"alg":"RS256"}
// err: nil// JWT header — URL-safe, no padding token := "eyJhbGciOiJSUzI1NiJ9" decoded, err := base64.StdEncoding.DecodeString(token) // err: illegal base64 data at input byte 19
المشكلة: تكتب Decode في مخزن مؤقت مُخصَّص مسبقاً وتُعيد عدد البايتات المكتوبة فعلياً. تُعيد DecodedLen حداً أعلى، لذا قد تحتوي ذيل المخزن على بايتات غير صالحة.
الحل: قطِّع النتيجة دائماً باستخدام dst[:n] — وليس dst[:len(dst)].
dst := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
n, err := base64.StdEncoding.Decode(dst, src)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(dst[:n])) // correct — only the decoded bytesdst := make([]byte, base64.StdEncoding.DecodedLen(len(src))) base64.StdEncoding.Decode(dst, src) fmt.Println(string(dst)) // may include trailing zero bytes
المشكلة: غالباً ما تحتوي سلاسل Base64 المنسوخة من الطرفيات والبريد الإلكتروني وملفات الإعداد على أسطر جديدة أو مسافات في النهاية. تمريرها مباشرة إلى DecodeString يفشل عند محرف المسافة.
الحل: استدعِ strings.TrimSpace (وstrings.ReplaceAll للأسطر الجديدة المضمنة) قبل فك التشفير.
encoded := "c2VydmljZV9rZXk6eEtNcDI=\n" cleaned := strings.TrimSpace(encoded) decoded, err := base64.StdEncoding.DecodeString(cleaned) // decoded: "service_key:xKMp2" // err: nil
// Value read from a config file with a trailing newline encoded := "c2VydmljZV9rZXk6eEtNcDI=\n" decoded, err := base64.StdEncoding.DecodeString(encoded) // err: illegal base64 data at input byte 24
المشكلة: استدعاء string(decoded) على البيانات الثنائية (الصور، الحمولات المضغوطة) يُنتج سلاسل UTF-8 غير صالحة. يمكن لسلاسل Go أن تحمل بايتات اعتباطية، لكن بعض العمليات ستُفسد المحتوى.
الحل: احتفظ بالبيانات الثنائية كـ []byte طوال خط الأنابيب. استدعِ string(decoded) فقط عندما يكون المحتوى المُفكَّك مضموناً أنه نص.
decoded, err := base64.StdEncoding.DecodeString(pngBase64)
if err != nil {
log.Fatal(err)
}
// Write bytes directly — no string conversion
os.WriteFile("image.png", decoded, 0644)decoded, _ := base64.StdEncoding.DecodeString(pngBase64)
// Treating binary PNG as a string loses data
imageStr := string(decoded)
os.WriteFile("image.png", []byte(imageStr), 0644) // may corruptمقارنة الأساليب
جميع المتغيرات مُضمَّنة في المكتبة القياسية — لا اعتماديات خارجية لأي منها. يعود الاختيار بين هذه المتغيرات إلى سؤالين بسيطين: من أين جاءت البيانات وهل تحتوي على حشو؟ البيانات القادمة من JWT وOAuth والـ APIs السحابية الحديثة كـ AWS وGoogle Cloud تستخدم تقريباً دائماً RawURLEncoding. أما بيانات مرفقات البريد الإلكتروني ونماذج الويب القديمة فتستخدم StdEncoding. إذا كان المصدر غير معروف، ابدأ بـ RawURLEncoding لأن معظم الـ APIs الحديثة تتبع هذا المعيار. يمكنك التحقق بسرعة من نوع الترميز بالنظر إلى السلسلة: إذا احتوت على + أو / فهي StdEncoding، وإذا احتوت على - أو _ فهي URLEncoding، وإذا لم تنتهِ بـ = فهي Raw (بلا حشو).
لرموز JWT وتدفقات OAuth: RawURLEncoding. لمرفقات البريد الإلكتروني وبيانات MIME: StdEncoding. للملفات الثنائية الكبيرة من القرص أو الشبكة: غلِّف قارئاً في base64.NewDecoder — يُبقي استخدام الذاكرة ثابتاً بغض النظر عن حجم الملف. تحتاج أبجدية مخصصة؟ base64.NewEncoding(alphabet) يُنشئ كائن ترميز جديد لحالات الاستخدام الخاصة.
الأسئلة الشائعة
كيف أفك تشفير Base64 في Go؟
استورد encoding/base64 ثم استدعِ base64.StdEncoding.DecodeString(s). تُعيد الدالة ([]byte, error) — تحقق دائماً من الخطأ. إذا كانت السلسلة تستخدم محارف آمنة للـ URL (- و_ بدلاً من + و/)، استخدم base64.URLEncoding.DecodeString بدلاً من ذلك. لرموز JWT ومعظم واجهات برمجية حديثة، RawURLEncoding (بلا حشو) هو الخيار الصحيح.
package main
import (
"encoding/base64"
"fmt"
"log"
)
func main() {
encoded := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
if err != nil {
log.Fatalf("decode: %v", err)
}
fmt.Println(string(decoded))
// {"alg":"HS256","typ":"JWT"}
}ما الفرق بين StdEncoding و URLEncoding في Go؟
يستخدم StdEncoding الأبجدية القياسية لـ Base64 مع محارف + و/ وحشو = — كما هو محدد في RFC 4648 §4. أما URLEncoding فيستبدل + بـ - و/ بـ _ مما يجعل المخرجات آمنة في الـ URLs وترويسات HTTP دون الحاجة إلى percent-encoding — كما هو محدد في RFC 4648 §5. استخدم URLEncoding لرموز JWT ورموز OAuth وأي بيانات مضمنة في سلاسل الاستعلام.
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// Standard: may contain + / and = characters
std := base64.StdEncoding.EncodeToString([]byte("hello/world"))
fmt.Println(std) // "aGVsbG8vd29ybGQ="
// URL-safe: replaces + with - and / with _
url := base64.URLEncoding.EncodeToString([]byte("hello/world"))
fmt.Println(url) // "aGVsbG8vd29ybGQ=" (same — diff shows with different bytes)
// JWT headers never have padding — use RawURLEncoding
raw := base64.RawURLEncoding.EncodeToString([]byte("hello/world"))
fmt.Println(raw) // "aGVsbG8vd29ybGQ" (no trailing =)
}كيف أصلح أخطاء "illegal base64 data" في Go؟
يعني هذا الخطأ أن المدخلات تحتوي على محارف خارج الأبجدية المتوقعة، أو أن الحشو خاطئ. ثلاثة أسباب شائعة: استخدام StdEncoding على مدخلات آمنة للـ URL (استبدله بـ URLEncoding)، أو استخدام مرمِّز بحشو على مدخلات بلا حشو (استبدله بـ RawStdEncoding/RawURLEncoding)، أو وجود مسافات/أسطر جديدة. احذف المسافات باستخدام strings.TrimSpace قبل فك التشفير.
package main
import (
"encoding/base64"
"fmt"
"log"
"strings"
)
func main() {
// Input from a webhook payload — has newlines stripped from wire format
raw := " aGVsbG8gd29ybGQ= \n"
cleaned := strings.TrimSpace(raw)
decoded, err := base64.StdEncoding.DecodeString(cleaned)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(decoded)) // hello world
}كيف أفك تشفير ملف Base64 كبير بأسلوب البث في Go؟
استخدم base64.NewDecoder(base64.StdEncoding, reader) الذي يُغلِّف أي io.Reader ويفك التشفير أثناء القراءة. مرِّره عبر io.Copy للكتابة إلى الوجهة دون تخزين الملف بأكمله في الذاكرة. هذا هو النمط القياسي لفك تشفير المرفقات الثنائية المشفرة بـ Base64 أو حمولات البيانات الكبيرة.
package main
import (
"encoding/base64"
"io"
"log"
"os"
)
func main() {
src, err := os.Open("attachment.b64")
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.Create("attachment.bin")
if err != nil {
log.Fatal(err)
}
defer dst.Close()
decoder := base64.NewDecoder(base64.StdEncoding, src)
io.Copy(dst, decoder)
}هل يمكنني فك تشفير حمولة JWT بتنسيق Base64 في Go دون مكتبة JWT؟
نعم. رمز JWT هو ثلاثة أجزاء مشفرة بـ Base64url مفصولة بنقاط. قسِّم السلسلة على "." وفك تشفير الجزء الثاني (الفهرس 1) باستخدام base64.RawURLEncoding.DecodeString — فترويسات JWT وحمولاته تستخدم الأبجدية الآمنة للـ URL وبلا حشو. جزء التوقيع (الفهرس 2) ثنائي ولا يُحتاج عادةً إلا للتحقق.
package main
import (
"encoding/base64"
"fmt"
"log"
"strings"
)
func main() {
token := "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3ItOTAxIiwicm9sZSI6ImFkbWluIn0.SIG"
parts := strings.Split(token, ".")
if len(parts) < 2 {
log.Fatal("invalid JWT format")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
log.Fatalf("decode payload: %v", err)
}
fmt.Println(string(payload))
// {"sub":"usr-901","role":"admin"}
}ما الترميز الذي يجب استخدامه لفك تشفير بيانات Base64 من استجابة HTTP API؟
راجع توثيق الـ API أو افحص السلسلة المشفرة. إذا كانت تحتوي على محارف + أو / وتنتهي بـ =، استخدم StdEncoding. إذا كانت تستخدم - و_ بدون =، استخدم RawURLEncoding. عند الشك، جرِّب RawURLEncoding أولاً — معظم الـ APIs الحديثة (OAuth2، JWT، Google Cloud، AWS) تستخدم Base64 الآمن للـ URL بلا حشو.
package main
import (
"encoding/base64"
"strings"
)
// Detect encoding variant from the encoded string
func decodeAPIPayload(encoded string) ([]byte, error) {
// URL-safe characters without padding — common in modern APIs
if !strings.Contains(encoded, "+") && !strings.Contains(encoded, "/") {
return base64.RawURLEncoding.DecodeString(encoded)
}
// Standard Base64 with padding
return base64.StdEncoding.DecodeString(encoded)
}أدوات ذات صلة
- مشفر Base64 — شفِّر البيانات الثنائية أو النصوص إلى Base64 في المتصفح، مفيد لإنشاء تجارب اختبار للصقها في اختبارات الوحدة في Go.
- فك تشفير JWT — قسِّم وافك تشفير الأجزاء الثلاثة لـ JWT مرة واحدة، مع فحص الحمولة حقلاً بحقل — لا حاجة لكود Go عندما تريد فقط قراءة رمز أثناء التصحيح.
- فك تشفير URL — افك تشفير percent-encoding للسلاسل المشفرة بـ URL، مفيد عندما تخلط استجابات API بين بيانات Base64url ومعاملات استعلام مشفرة بـ percent-encoding.
- منسق JSON — بعد فك تشفير حمولة JWT بتنسيق Base64 أو استجابة API، الصق JSON هنا لتنسيقه والتحقق من بنيته فوراً.