security/fix-swagger-info-leak-m02 (#29283)

Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
This commit is contained in:
L1nSn0w 2025-12-15 11:24:06 +08:00 committed by GitHub
parent 7fead6a9da
commit 355a2356d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 61 additions and 8 deletions

View File

@ -626,7 +626,17 @@ QUEUE_MONITOR_ALERT_EMAILS=
QUEUE_MONITOR_INTERVAL=30 QUEUE_MONITOR_INTERVAL=30
# Swagger UI configuration # Swagger UI configuration
SWAGGER_UI_ENABLED=true # SECURITY: Swagger UI is automatically disabled in PRODUCTION environment (DEPLOY_ENV=PRODUCTION)
# to prevent API information disclosure.
#
# Behavior:
# - DEPLOY_ENV=PRODUCTION + SWAGGER_UI_ENABLED not set -> Swagger DISABLED (secure default)
# - DEPLOY_ENV=DEVELOPMENT/TESTING + SWAGGER_UI_ENABLED not set -> Swagger ENABLED
# - SWAGGER_UI_ENABLED=true -> Swagger ENABLED (overrides environment check)
# - SWAGGER_UI_ENABLED=false -> Swagger DISABLED (explicit disable)
#
# For development, you can uncomment below or set DEPLOY_ENV=DEVELOPMENT
# SWAGGER_UI_ENABLED=false
SWAGGER_UI_PATH=/swagger-ui.html SWAGGER_UI_PATH=/swagger-ui.html
# Whether to encrypt dataset IDs when exporting DSL files (default: true) # Whether to encrypt dataset IDs when exporting DSL files (default: true)

View File

@ -1221,9 +1221,19 @@ class WorkflowLogConfig(BaseSettings):
class SwaggerUIConfig(BaseSettings): class SwaggerUIConfig(BaseSettings):
SWAGGER_UI_ENABLED: bool = Field( """
description="Whether to enable Swagger UI in api module", Configuration for Swagger UI documentation.
default=True,
Security Note: Swagger UI is automatically disabled in PRODUCTION environment
to prevent API information disclosure. Set SWAGGER_UI_ENABLED=true explicitly
to enable in production if needed.
"""
SWAGGER_UI_ENABLED: bool | None = Field(
description="Whether to enable Swagger UI in api module. "
"Automatically disabled in PRODUCTION environment for security. "
"Set to true explicitly to enable in production.",
default=None,
) )
SWAGGER_UI_PATH: str = Field( SWAGGER_UI_PATH: str = Field(
@ -1231,6 +1241,23 @@ class SwaggerUIConfig(BaseSettings):
default="/swagger-ui.html", default="/swagger-ui.html",
) )
@property
def swagger_ui_enabled(self) -> bool:
"""
Compute whether Swagger UI should be enabled.
If SWAGGER_UI_ENABLED is explicitly set, use that value.
Otherwise, disable in PRODUCTION environment for security.
"""
if self.SWAGGER_UI_ENABLED is not None:
return self.SWAGGER_UI_ENABLED
# Auto-disable in production environment
import os
deploy_env = os.environ.get("DEPLOY_ENV", "PRODUCTION")
return deploy_env.upper() != "PRODUCTION"
class TenantIsolatedTaskQueueConfig(BaseSettings): class TenantIsolatedTaskQueueConfig(BaseSettings):
TENANT_ISOLATED_TASK_CONCURRENCY: int = Field( TENANT_ISOLATED_TASK_CONCURRENCY: int = Field(

View File

@ -22,8 +22,8 @@ login_manager = flask_login.LoginManager()
@login_manager.request_loader @login_manager.request_loader
def load_user_from_request(request_from_flask_login): def load_user_from_request(request_from_flask_login):
"""Load user based on the request.""" """Load user based on the request."""
# Skip authentication for documentation endpoints # Skip authentication for documentation endpoints (only when Swagger is enabled)
if dify_config.SWAGGER_UI_ENABLED and request.path.endswith((dify_config.SWAGGER_UI_PATH, "/swagger.json")): if dify_config.swagger_ui_enabled and request.path.endswith((dify_config.SWAGGER_UI_PATH, "/swagger.json")):
return None return None
auth_token = extract_access_token(request) auth_token = extract_access_token(request)

View File

@ -131,12 +131,28 @@ class ExternalApi(Api):
} }
def __init__(self, app: Blueprint | Flask, *args, **kwargs): def __init__(self, app: Blueprint | Flask, *args, **kwargs):
import logging
import os
kwargs.setdefault("authorizations", self._authorizations) kwargs.setdefault("authorizations", self._authorizations)
kwargs.setdefault("security", "Bearer") kwargs.setdefault("security", "Bearer")
kwargs["add_specs"] = dify_config.SWAGGER_UI_ENABLED
kwargs["doc"] = dify_config.SWAGGER_UI_PATH if dify_config.SWAGGER_UI_ENABLED else False # Security: Use computed swagger_ui_enabled which respects DEPLOY_ENV
swagger_enabled = dify_config.swagger_ui_enabled
kwargs["add_specs"] = swagger_enabled
kwargs["doc"] = dify_config.SWAGGER_UI_PATH if swagger_enabled else False
# manual separate call on construction and init_app to ensure configs in kwargs effective # manual separate call on construction and init_app to ensure configs in kwargs effective
super().__init__(app=None, *args, **kwargs) super().__init__(app=None, *args, **kwargs)
self.init_app(app, **kwargs) self.init_app(app, **kwargs)
register_external_error_handlers(self) register_external_error_handlers(self)
# Security: Log warning when Swagger is enabled in production environment
deploy_env = os.environ.get("DEPLOY_ENV", "PRODUCTION")
if swagger_enabled and deploy_env.upper() == "PRODUCTION":
logger = logging.getLogger(__name__)
logger.warning(
"SECURITY WARNING: Swagger UI is ENABLED in PRODUCTION environment. "
"This may expose sensitive API documentation. "
"Set SWAGGER_UI_ENABLED=false or remove the explicit setting to disable."
)