
يُظهر ثغرة حرجة في قابلية توقع مفتاح توقيع JWT في خادم PowerJob، مما يسمح باشتقاق المفتاح دون اتصال وتزوير الرموز للاستيلاء على صلاحيات المسؤول وتجاوز مصادقة OpenAPI.
يتم اشتقاق مفتاح توقيع JWT (HS256) في خادم PowerJob عن طريق فك ترميز BASE64 لـ ثابت مضمّن في الكود مقترنًا بـ MD5 لعنوان JDBC الافتراضي (DefaultSecretProvider / JwtServiceImpl). عنوان JDBC عام في النشر الافتراضي (docker-compose / application-daily.properties)، لذلك يمكن للمهاجم اشتقاق المفتاح بالكامل دون اتصال وتزوير JWT صالح لأي مستخدم (بما في ذلك المسؤول)؛ كما يمكن تزوير رمز تطبيق OpenAPI لتجاوز مصادقة OpenAPI.
application-daily.properties يكشف jdbc:mysql://powerjob-mysql:3306/powerjob-daily?... كنص صريحpowerjob-server-auth/.../jwt/impl/DefaultSecretProvider.java:31-45: return md5(environment.getProperty("spring.datasource.core.jdbc-url"));powerjob-server-auth/.../jwt/impl/JwtServiceImpl.java:44-102: ثابت BASE_SECURITY مضمّن في الكود؛ genSecretKey(secret) = Decoders.BASE64.decode(BASE_SECURITY.concat(secret)) ← Keys.hmacShaKeyForusername/encryptedToken) لديها فحص ثانوي لحسابات PWJB (DigestUtils.rePassword(password, username))، لكن:
appId/password/encryptType) مباشرة لتجاوز المصادقة (OpenApiSecurityServiceImpl:87-105)؛البيئة: خادم PowerJob محلي (127.0.0.1:7700، عنوان JDBC jdbc:mysql://127.0.0.1:3307/powerjob_daily?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true). المهاجم يشتق كل شيء دون اتصال من المعلومات العامة فقط:
# ① اشتقاق encryptedToken (= rePassword(password, username))؛ يمكن حسابه مع كلمة المرور الافتراضية
rePassword("powerjob_admin", "ADMIN")
= ADMIN_b82a450701f6a71723fb99931fe35e18_b # مطابق بايتًا ببايت لقيمة DB user_info.token_login_verify_info ✅
# ② اشتقاق مفتاح توقيع HS256 = BASE64(BASE_SECURITY + MD5(JDBC_URL))
secret = BASE_SECURITY + md5(jdbc_url) # مفتاح بطول 101 بايت
# ③ تزوير JWT ويب للمسؤول (الادعاءات: username=PWJB_ADMIN، encryptedToken=نتيجة ①، sub=PowerJob)
النتيجة الفعلية (الرمز المزوّر يجتاز المصادقة):
[*] /namespace/list بدون رمز :
{"success":false,"data":null,"message":"PowerJobAuthException: UserNotLoggedIn","code":"-100"}
[*] /namespace/list مع JWT مزوّر :
{"success":true,"data":{"index":0,"pageSize":10,"totalPages":1,"totalItems":1,"data":[{"id":1,"code":"default_namespace","name":"default_namespace","dept":null,"tags":null,"extra":null,"status":1,"statusStr":"ENABLE","gmtCreate":"2026-08-07T13:07:32.000+00:00","gmtCreateStr":"2026-08-07 21:07:32","gmtModified":"2026-08-07T13:07:32.000+00:00","gmtModifiedStr":"2026-08-07 21:07:32","showName":"default_namespace(default_namespace)","token":"14e4ebff-9a79-4845-ac66-8b20c4dff2ee","componentUserRoleInfo":{"observer":[],"qa":[],"developer":[],"admin":[]},"creatorShowName":null,"modifierShowName":null}]},"message":null}
← الرمز المزوّر يصادق كمسؤول ويعيد بيانات حقيقية (الخط الأساسي مرفوض مع UserNotLoggedIn).
النقاط الرئيسية:
encryptedToken المشتق دون اتصال يطابق القيمة المخزنة في قاعدة البيانات بايتًا ببايت (ADMIN_b82a450701f6a71723fb99931fe35e18_b).BASE_SECURITY + MD5 لعنوان JDBC الافتراضي العام ← يمكن اشتقاقه دون اتصال بدون أي بيانات اعتماد.encryptedToken الخاص بـ PWJB يمكن إعادة حسابه دون اتصال مع كلمة المرور الافتراضية (powerjob_admin، النتيجة PJ-02) ← استيلاء كامل على حساب المسؤول.encryptedToken، لكن مسار رمز تطبيق OpenAPI (الادعاءات appId/password/encryptType، OpenApiSecurityServiceImpl:87-105) لا يزال يمكن تزويره بشكل مستقل لتجاوز مصادقة OpenAPI.حتى إذا تم تغيير كلمة المرور الافتراضية، تستمر الثغرة عبر نواقل هجوم أخرى:
encryptedToken لأي مستخدم صالح بوسائل أخرى (مثل حقن SQL، كشف نسخ احتياطية لقاعدة البيانات، تسرب السجلات، أو كشف معلومات من نقاط نهاية أخرى) يمكنه دمج هذا الرمز مع مفتاح التوقيع القابل للتنبؤ لتزوير JWT لذلك المستخدم المحدد.encryptedToken تمامًا، لأن الرمز المزوّر يحتوي الآن على encryptedToken مشروع مشتق من السجل الفعلي في قاعدة البيانات.إثبات المفهوم: powerjob_jwt_key.py 192.168.49.128:7700


encryptedToken لجميع الحسابات.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N = 9.1 حرج (مقترنًا بكلمة المرور الافتراضية / OpenAPI)