
pac4j-check v0.2.0
ماسح ضوئي غير متصل لـ CVE-2026-29000 (CVSS 10.0) في org.pac4j:pac4j-jwt. يفحص ملفات jar/fat-jars مباشرةً، لذا يعمل حيث لا يستطيع mvn dependency:tree العمل. ملف jar واحد، بدون تبعيات، Java 8+.
pac4j-check
فحص دون اتصال بالإنترنت للثغرة CVE-2026-29000 (CVSS 10.0) —— بما في ذلك الحزم التي لم يدرجها التنبيه الرسمي.
English | 中文
ملف jar واحد، بحجم ~24KB، بدون أي تبعيات وقت التشغيل، يعمل بدءًا من Java 8، يعمل دون اتصال بالإنترنت بالكامل، ولا يرسل أي بيانات إلى أي مكان.
ما هي هذه الثغرة
لا يفرض JwtAuthenticator في org.pac4j:pac4j-jwt التحقق من التوقيع عند معالجة رموز JWT المشفّرة (JWE).
يكفي أن يحصل المهاجم على المفتاح العام RSA للخادم (والمفتاح العام علني بطبيعته)،
ليتمكن من إنشاء PlainJWT مغلّف داخل JWE، وكتابة قيمة subject والدور كما يشاء،
والدخول بأي هوية مستخدم، بما في ذلك المدير. دون أي بيانات اعتماد.
| البند | القيمة |
|---|---|
| CVE | CVE-2026-29000 |
| GitHub advisory | GHSA-pm7g-w2cf-q238 |
| CVSS | 10.0 (الدرجة الكاملة) |
| تاريخ النشر | 2026-03-05 |
يُدمج pac4j على نطاق واسع في Spring Security وApereo CAS وJEE وVert.x وPlay وDropwizard وغيرها.
🔴 تصحيح v0.2.0: الادعاء الجوهري في v0.1.0 كان خاطئًا
ادّعى v0.1.0 أن «قاعدة الثغرات الرسمية أدرجت حزمة واحدة فقط، بينما توجد فعليًا خمس حزم»،
وبناءً على ذلك كان يُبلّغ عن pac4j-oidc / javalin-pac4j / lagom-pac4j / ratpack-pac4j كمتأثرة.
كانت تلك إنذارات خاطئة. إدراج التنبيه الرسمي لـ pac4j-jwt فقط هو الصحيح.
أساس المراجعة (دليلان مستقلان، وكلاهما قابل لإعادة الإنتاج ذاتيًا):
| المكوّن | كيف يعلن اعتماده على pac4j-jwt | هل ينتقل إلى المستخدمين |
|---|---|---|
pac4j-oidc | نطاق test (تم التحقق إصدارًا بإصدار: 3.0.0 / 4.0.0 / 4.5.0 / 5.0.0 / 5.7.0 / 6.0.0 / 6.3.0) | ❌ |
javalin-pac4j | نطاق test | ❌ |
lagom-pac4j-parent | نطاق provided | ❌ |
ratpack-pac4j:1.4.6 | كتلة الاعتماد تلك مغلّفة بالكامل داخل تعليق XML، فهي غير موجودة أصلًا | ❌ |
- النطاق لا ينتقل —— في Maven، اعتماديات
test/providedلا تنتقل إلى المستهلكين downstream، فلن يظهر pac4j-jwt على runtime classpath الخاص بالمستخدمين. - التحقق الفعلي من المكوّن —— يحتوي
pac4j-oidc-6.0.0.jarعلى 78 مدخلًا، جميعها تحتorg/pac4j/oidc/، ولا توجد أي أصناف pac4j-jwt مدمجة (shaded) بالداخل. فلا هو ينتقل، ولا هو محمول.
أين كان الخطأ: حلّل v0.1.0 كل ملف pom على حدة، لكنه نظر فقط إلى «من كتب الإحداثية pac4j-jwt»،
ولم ينظر إلى scope —— فاعتبر «مكتوب في pom» بمنزلة «سيصل إلى المستخدم».
إذا كنت قد رقّيت pac4j بسبب تقرير v0.1.0، فذلك الترقية لم تكن ضرورية (والترقية نفسها غير ضارة). لا حاجة لأي إجراء إلا إذا كان
pac4j-jwtبإصدار متأثر موجودًا فعلًا في تطبيقك.
لماذا ما زلنا بحاجة إلى أداة مخصصة
لتحديد ما إذا كان pac4j-jwt المتأثر موجودًا فعلًا على جهاز معيّن، يفشل mvn dependency:tree في حالتين ——
إذ لا يوجد على جهاز الإنتاج سوى fat-jar جاهز (بلا مصدر ولا pom)؛ أو أنه مدمج (shaded) داخل أحد الـ SDK،
فلا يظهر في شجرة الاعتماديات أصلًا. تفحص هذه الأداة المكوّن نفسه مباشرة، دون الاعتماد على بيئة البناء.
| المكوّن | عدد الإصدارات المتأثرة | التنبيه الرسمي |
|---|---|---|
org.pac4j:pac4j-jwt | 114 | ✅ الوحيد المُدرج، وهذا صحيح |
الاستخدام
java -jar pac4j-check.jar ./myapp.jar # فحص ملف jar/war واحد
java -jar pac4j-check.jar /opt/apps # فحص مجلد (بشكل تكراري)
java -jar pac4j-check.jar /opt/apps --json # إخراج JSON، مناسب للربط بأنابيب المعالجة
java -jar pac4j-check.jar ./app.jar --gbk # عند ظهور رموز مشوّهة للصينية في طرفية Windows
رموز الخروج: 0 = لم يُعثر على متأثر · 1 = محلّ شك/تعذّر التحديد · 2 = تم العثور على متأثر. يمكن ربطه مباشرة بـ CI.
مثال على الإخراج
[CRITICAL] pac4j-jwt 5.4.3
位置 :demo-app.jar!/BOOT-INF/lib/pac4j-jwt-5.4.3.jar
版本来源:pom.properties(可靠)
部署形态:Spring Boot fat-JAR
结论 :命中 CVE-2026-29000 —— JWE 处理路径未强制校验签名,
拿到服务器 RSA 公钥即可伪造任意身份(含管理员)登录
处置 :升级 pac4j-jwt 至 5.7.9
كان v0.1.0 يُبلّغ هنا أيضًا عن سطر إضافي
[CRITICAL] pac4j-oidc—— كان ذلك إنذارًا خاطئًا، وقد حُذف في v0.2.0، والسبب موضّح في تصحيح المقدمة.
ماذا تفعل
- تفكيك Spring Boot fat-JAR بشكل تكراري (في الذاكرة، دون فك ضغط على القرص)، مع تتبّع المسار المتداخل المحدد
- التعرّف على الحالات المدمجة (shaded) داخل jar المضيف —— وهي الحالة التي لا يستطيع
mvn dependency:treeكشفها - تتبّع سلسلة الإدخال —— لتخبرك أي مكوّن جرّ pac4j-jwt إلى الداخل
- إعطاء الحكم وهدف الترقية المحدد وفق النطاقات الرسمية
قواعد الحكم وحدوده (يُرجى قراءتها قبل الاستخدام)
يعتمد الحكم دائمًا على التنبيه الرسمي GHSA-pm7g-w2cf-q238:
pac4j-jwt < 4.5.9 -> 升 4.5.9
pac4j-jwt >= 5.0.0-RC1 且 < 5.7.9 -> 升 5.7.9
pac4j-jwt >= 6.0.4.1 且 < 6.3.3 -> 升 6.3.3
التحقق الذاتي: شغّلت هذه الأداة الحكم على جميع إصدارات pac4j-jwt الـ 147 على Maven Central،
فكان عدد الإصابات 114، وهو مطابق تمامًا لمجموع عدد الإصدارات في نطاقات التنبيه الرسمي الثلاثة (13+33+68).
هذا التأكيد مكتوب في الاختبارات (OfficialRangeCrossCheckTest)، وأي عدم تطابق يُفشل البناء.
⚠️ لكن يجب أن تعرف ما الذي يتحقق منه هذا فعلًا: إنه يتحقق من صحة خوارزمية النطاقات الإصدارية، ولا يستطيع التحقق من «أي المكوّنات يجب أن تدخل جدول الحكم» —— فالإنذار الخاطئ في v0.1.0 حدث تحديدًا في الشق الثاني، بينما كان هذا التحقق الذاتي أخضر آنذاك. نطاق نجاح التحقق ≠ نطاق صحة الاستنتاج.
قيدان يجب توضيحهما
-
يغطي فقط groupId واحدًا هو
org.pac4j. هناك أبحاث من أطراف ثالثة تقول إن المكوّنات المتأثرة 19 مكوّنًا و1,020 إصدارًا، لكنها لم تنشر القائمة الكاملة. ما تعيد هذه الأداة بناءه بشكل مستقل هو الجزء الخاص بـ org.pac4j، ولا تدّعي تغطية الكل. لم تُدرَج groupIds أخرى (مثلorg.apereo.casالخاص بـ Apereo CAS). -
يُصنّف
pac4j-jwt6.0.0 ~ 6.0.4 كـ «محلّ شك» وليس «متأثر». تقول الجهة الرسمية إن 6.x متأثر بدءًا من6.0.4.1؛ بينما تقول أبحاث طرف ثالث إن الثغرة أُدخلت منذ1.9.2—— وإن صحّ ذلك، فيجب احتساب هذه الإصدارات الخمسة أيضًا. لم نتحقق بشكل مستقل من استنتاج الطرف الثالث، لذلك نُبرزها منفصلة ونوصي بترقية تحوّطية، بدلًا من الحكم عليها بالخطورة مباشرة.
لماذا كل هذا التحوّط: خطأ قاعدة الحكم ليس «إنذارًا خاطئًا»، بل يدفع المستخدم إلى فعل خاطئ. نفضّل وسم الحالة كمحلّ شك على تثبيت نطاق غير مُتحقق منه كاستنتاج نهائي.
البناء
mvn package # الناتج: target/pac4j-check.jar
mvn test # 31 اختبارًا
الملاحظات
إذا وجدت خطأً في الحكم، أو إغفالًا، أو إنذارًا خاطئًا، يُرجى فتح Issue. وإذا أمكنك تقديم إحداثيات مكوّن قابلة لإعادة الإنتاج (groupId:artifactId:version)، فسيكون الإصلاح أسرع بكثير.
License
Apache License 2.0
pac4j-check (English)
Offline scanner for CVE-2026-29000 (CVSS 10.0) — including the packages the official advisory does not list.
Single jar, ~24KB, zero runtime dependencies, Java 8+, fully offline, sends nothing anywhere.
The vulnerability
JwtAuthenticator in org.pac4j:pac4j-jwt fails to enforce signature validation on certain
encrypted JWT (JWE) processing paths. An attacker holding the server's RSA public key
(which is public by design) can craft a JWE-wrapped PlainJWT with arbitrary subject and role
claims and authenticate as any user, including administrators — with no credentials.
| CVE | CVE-2026-29000 |
| GitHub advisory | GHSA-pm7g-w2cf-q238 |
| CVSS | 10.0 |
| Published | 2026-03-05 |
🔴 v0.2.0 correction: v0.1.0's central claim was wrong
v0.1.0 claimed the official advisory "lists only one package while there are five", and
flagged pac4j-oidc / javalin-pac4j / lagom-pac4j / ratpack-pac4j as affected.
Those were false positives. The advisory listing only pac4j-jwt is correct.
| Artifact | How it declares pac4j-jwt | Reaches consumers? |
|---|---|---|
pac4j-oidc | test scope (verified on 3.0.0 / 4.0.0 / 4.5.0 / 5.0.0 / 5.7.0 / 6.0.0 / 6.3.0) | ❌ |
javalin-pac4j | test scope | ❌ |
lagom-pac4j-parent | provided scope | ❌ |
ratpack-pac4j:1.4.6 | the whole block is inside an XML comment — it does not exist | ❌ |
Two independent lines of evidence, both reproducible:
- Scope does not propagate — Maven does not pass
test/provideddependencies to downstream consumers, so pac4j-jwt never reaches their runtime classpath. - Artifact inspection —
pac4j-oidc-6.0.0.jarhas 78 entries, all underorg/pac4j/oidc/, with no shaded pac4j-jwt classes.
Root cause: v0.1.0 did parse every pom, but only looked at who names the coordinate,
never at scope — mistaking "declared in a pom" for "reaches the consumer".
If you upgraded pac4j because of a v0.1.0 report, that upgrade was not required (though harmless). Action is only needed when an affected
pac4j-jwtis actually present.
Why a dedicated tool
Deciding whether an affected pac4j-jwt is actually on a given machine defeats
mvn dependency:tree in two common cases: a production box with only a packaged fat-jar
(no sources, no pom), or a copy shaded inside some vendor SDK, invisible to the dependency
tree. This tool inspects the artifacts themselves.
| Artifact | Affected versions | In official advisory |
|---|---|---|
org.pac4j:pac4j-jwt | 114 | ✅ the only one listed — and that is correct |
Usage
java -jar pac4j-check.jar ./myapp.jar
java -jar pac4j-check.jar /opt/apps
java -jar pac4j-check.jar /opt/apps --json
Exit codes: 0 clean · 1 disputed/undetermined · 2 affected.
What it does
- Recursively unpacks Spring Boot fat-JARs in memory (nothing written to disk)
- Detects pac4j-jwt shaded into a host jar — the case
mvn dependency:treecannot see - Traces the introduction chain, so you know which artifact pulled it in
- Reports a concrete upgrade target
Rules and limitations
Verdicts follow the official advisory GHSA-pm7g-w2cf-q238 exactly:
pac4j-jwt < 4.5.9 -> 4.5.9
pac4j-jwt >= 5.0.0-RC1 and < 5.7.9 -> 5.7.9
pac4j-jwt >= 6.0.4.1 and < 6.3.3 -> 6.3.3
Cross-check: running the rules over all 147 published pac4j-jwt versions yields 114
affected — exactly matching the advisory's three ranges (13+33+68). This is asserted in
OfficialRangeCrossCheckTest; a mismatch fails the build.
Two limitations, stated plainly:
- Only the
org.pac4jgroupId is covered. Third-party research reports 19 affected packages across 1,020 versions but has not published the full list. This tool independently reconstructs the org.pac4j portion and does not claim full coverage. - pac4j-jwt 6.0.0–6.0.4 are reported as DISPUTED, not AFFECTED. The advisory starts the
6.x range at
6.0.4.1; third-party research states the flaw was introduced as early as1.9.2. We have not independently verified the latter, so these versions are flagged for human review with a conservative upgrade recommendation rather than asserted as vulnerable.
A wrong verdict is not a "false positive" — it makes people take the wrong action.
License
Apache License 2.0
هل تحتاج إلى فحص أكثر تعمقًا؟
هذه الأداة تجيب على سؤال «هل أنا مصاب أم لا». أما ما يلي فهي لا تستطيع الإجابة عليه، ويمكنك التواصل معي للقيام به:
- الاعتماديات مدمجة (shaded) أو مُعاد توجيهها (relocate)، أو تعذّر الحصول على ناتج البناء أصلًا
- المطلوب الحكم على «هل ستُفعَّل هذه الثغرة فعلًا على سلسلة الاستدعاء لدينا»، وليس مجرد مطابقة الإصدار
- الحاجة إلى تخصيص وفق عملية البناء أو بيئة الشبكة الداخلية لديكم، ودمجها في خط الأنابيب الحالي
- لديك مشكلة من النوع نفسه في مكوّن آخر، ولا توجد أداة جاهزة بعد
📮 [email protected] —— اشرح الحالة، وسأعطيك ردًا مكتوبًا في صفحة واحدة خلال 24 ساعة: هل يمكن القيام به، وأين الصعوبة، وكم يستغرق تقريبًا. هذه الخطوة مجانية، ولا تتطلب منك أي التزام مسبق.