FlawOpen

flawopen.com/Teardowns/cve-2026-104873-langgraph-sdk-auth-action-bypass

● CVE-2026-104873 · CVSS v4 7.6[1] · उच्च
सुरक्षा अनुसंधान · FlawOpen

LangGraph CVE-2026-104873: प्राधिकरण बायपास

LangGraph SDK 0.1.45–0.4.3 ने तीन Python डेकोरेटर में action फ़िल्टर अनदेखे किए। 0.4.4 पर अपग्रेड करें।[1]

CVSS वेक्टरCVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N[1]
💡 आसान भाषा में (ELI5)

इमारत का स्वागत-कक्ष केवल डिलीवरी लेता है, लेकिन मार्ग-प्रणाली हर आगंतुक को वहीं भेजती है। यदि कक्ष सिर्फ़ प्रवेश जाँचता है, तो आगंतुक उन कमरों तक पहुँच सकता है जिन्हें दूसरी चौकी बंद रखती। SDK ने resource handler दर्ज करते समय actions= अनदेखा किया, इसलिए उसे पढ़ने, बदलने या मिटाने के अनुरोध भी मिल सकते थे। जोखिम तभी है जब handler अनुमति-जाँच न करे।[1]

इस पेज के मुख्य शब्द

Resource authorization decorator
resource संचालन की अनुमति दर्ज करने वाला Python decorator।[1], [2]
Action filter
actions= handler के operations चुनता है।[1], [2]
Fallback handler
विशिष्ट मेल न होने पर उपयोग होने वाला सामान्य handler।[1], [2]
CWE-863
गलत authorization: जाँच अपेक्षित अनुमति लागू नहीं करती।[1]

फ्रेमवर्क मैपिंग

मूल कारण विश्लेषण (Root Cause)

0.1.45–0.4.3 में decorators actions= लेते थे, लेकिन handler को resource wildcard * के अंतर्गत दर्ज करते थे। LangGraph इसे fallback से पहले चुन सकता था। यदि function केवल अपेक्षित action जाँचता, तो अन्य actions fallback जाँच के बिना पहुँच सकते थे। दूसरे व्यक्ति के resource तक पहुँच तभी संभव थी जब application छोड़ी गई जाँचों पर निर्भर हो और handler action तथा ownership स्वयं न जाँचे। 0.4.4 सूची जाँचकर हर action अलग दर्ज करता है; actions= न देने पर wildcard जानबूझकर रहता है। Advisory में CWE-863 और CVSS 4.0 7.6 High है। यह configuration-निर्भर authorization flaw है, बिना authentication bypass नहीं और हर deployment की समस्या नहीं। प्रभावित decorators हैं: @auth.on.threads / @auth.on.assistants / @auth.on.crons. Fix की महत्वपूर्ण सीमा: SDK बदलाव घोषित actions के लिए registration सीमित करता है, लेकिन application द्वारा global default-deny handler न लगाने पर unmatched custom-auth paths फिर भी अनुमति पा सकते हैं। 0.4.4 changelog के अनुसार langgraph-api 0.10+ startup पर uncovered paths की चेतावनी देता है; इसे detection मानें, enforcement नहीं।[1], [2], [5]

हमले का चरण-दर-चरण प्रवाह

चरण 1

सेटिंग

SDK 0.1.45–0.4.3 और actions= वाला प्रभावित decorator।[1], [4]

चरण 2

पंजीकरण

Handler केवल एक action के लिए हो सकता है।[1]

चरण 3

वाइल्डकार्ड पंजीकरण

SDK उसे सभी resource actions से जोड़ता है।[1], [2]

चरण 4

जाँच

Handler action और ownership न जाँच सकता है।[1]

चरण 5

सुधार

0.4.4 पर जाएँ; action, fallback और ownership जाँचें।[1], [2], [3], [5]

सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन

कमज़ोर अंश पुराना व्यवहार दिखाता है: _ResourceOn.__call__ actions= के बावजूद * उपयोग करता था। सुधार सूची जाँचकर हर action अलग दर्ज करता है। यह source excerpt है, पूरे module का विकल्प नहीं। SDK अपग्रेड करें और handler में action तथा ownership जाँचें। स्रोत कोड का स्थान: libs/sdk-py/langgraph_sdk/auth/__init__.py, method _ResourceOn.__call__.[1], [2]

सोर्स अंश: कार्रवाई के अनुसार अनुमति-प्रक्रिया पंजीकरण

कमज़ोर कार्यान्वयन
def __call__(self, fn=None, *, resources=None, actions=None):
    if fn is not None:
        _validate_handler(fn)
        return typing.cast("_ActionHandler", _register_handler(
            self.auth, self.resource, "*", fn
        ))

    def decorator(handler):
        _validate_handler(handler)
        return typing.cast("_ActionHandler", _register_handler(
            self.auth, self.resource, "*", handler
        ))

    return decorator
सुरक्षित और सुदृढ़ फ़िक्स
def __call__(self, fn=None, *, resources=None, actions=None):
    def decorator(handler):
        _validate_handler(handler)
        if resources is None:
            resource_list = [self.resource]
        elif isinstance(resources, str):
            resource_list = [resources]
        elif isinstance(resources, Sequence):
            resource_list = list(resources)
        else:
            raise TypeError("resources must be a string or sequence of strings")
        if resource_list != [self.resource]:
            raise ValueError("use @auth.on(...) for other resources")

        if actions is None:
            action_list = ["*"]
        elif isinstance(actions, str):
            action_list = [actions]
        elif isinstance(actions, Sequence):
            action_list = list(actions)
        else:
            raise TypeError("actions must be a string or sequence of strings")
        if not action_list:
            raise ValueError("actions must not be empty")
        if not all(isinstance(action, str) for action in action_list):
            raise TypeError("actions must be strings")

        valid_actions = {
            value.action for value in vars(self).values()
            if isinstance(value, _ResourceActionOn)
        }
        invalid_actions = (
            sorted(set(action_list) - valid_actions) if actions is not None else []
        )
        if invalid_actions:
            raise ValueError("invalid action for this resource")
        if len(action_list) != len(set(action_list)):
            raise ValueError("actions must not contain duplicates")

        for action in action_list:
            if (self.resource, action) in self.auth._handlers:
                raise ValueError("handler already registered for action")
        for action in action_list:
            _register_handler(self.auth, self.resource, action, handler)
        return handler

    if fn is not None:
        return decorator(typing.cast("_ActionHandler", fn))
    return decorator

इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट

स्रोत