mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-14 04:05:53 +08:00
fix: clickjacking (#18516)
Signed-off-by: -LAN- <laipz8200@outlook.com> Co-authored-by: -LAN- <laipz8200@outlook.com>
This commit is contained in:
parent
a1158cc946
commit
3737e0b087
@ -483,3 +483,6 @@ OTEL_MAX_EXPORT_BATCH_SIZE=512
|
|||||||
OTEL_METRIC_EXPORT_INTERVAL=60000
|
OTEL_METRIC_EXPORT_INTERVAL=60000
|
||||||
OTEL_BATCH_EXPORT_TIMEOUT=10000
|
OTEL_BATCH_EXPORT_TIMEOUT=10000
|
||||||
OTEL_METRIC_EXPORT_TIMEOUT=30000
|
OTEL_METRIC_EXPORT_TIMEOUT=30000
|
||||||
|
|
||||||
|
# Prevent Clickjacking
|
||||||
|
ALLOW_EMBED=false
|
||||||
|
@ -1068,3 +1068,6 @@ OTEL_MAX_EXPORT_BATCH_SIZE=512
|
|||||||
OTEL_METRIC_EXPORT_INTERVAL=60000
|
OTEL_METRIC_EXPORT_INTERVAL=60000
|
||||||
OTEL_BATCH_EXPORT_TIMEOUT=10000
|
OTEL_BATCH_EXPORT_TIMEOUT=10000
|
||||||
OTEL_METRIC_EXPORT_TIMEOUT=30000
|
OTEL_METRIC_EXPORT_TIMEOUT=30000
|
||||||
|
|
||||||
|
# Prevent Clickjacking
|
||||||
|
ALLOW_EMBED=false
|
||||||
|
@ -66,6 +66,7 @@ services:
|
|||||||
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
|
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
|
||||||
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
||||||
CSP_WHITELIST: ${CSP_WHITELIST:-}
|
CSP_WHITELIST: ${CSP_WHITELIST:-}
|
||||||
|
ALLOW_EMBED: ${ALLOW_EMBED:-false}
|
||||||
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
|
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
|
||||||
MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
|
MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
|
||||||
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
|
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
|
||||||
@ -552,7 +553,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./volumes/opengauss/data:/var/lib/opengauss/data
|
- ./volumes/opengauss/data:/var/lib/opengauss/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"]
|
test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1" ]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 10
|
retries: 10
|
||||||
|
@ -474,6 +474,7 @@ x-shared-env: &shared-api-worker-env
|
|||||||
OTEL_METRIC_EXPORT_INTERVAL: ${OTEL_METRIC_EXPORT_INTERVAL:-60000}
|
OTEL_METRIC_EXPORT_INTERVAL: ${OTEL_METRIC_EXPORT_INTERVAL:-60000}
|
||||||
OTEL_BATCH_EXPORT_TIMEOUT: ${OTEL_BATCH_EXPORT_TIMEOUT:-10000}
|
OTEL_BATCH_EXPORT_TIMEOUT: ${OTEL_BATCH_EXPORT_TIMEOUT:-10000}
|
||||||
OTEL_METRIC_EXPORT_TIMEOUT: ${OTEL_METRIC_EXPORT_TIMEOUT:-30000}
|
OTEL_METRIC_EXPORT_TIMEOUT: ${OTEL_METRIC_EXPORT_TIMEOUT:-30000}
|
||||||
|
ALLOW_EMBED: ${ALLOW_EMBED:-false}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# API service
|
# API service
|
||||||
@ -542,6 +543,7 @@ services:
|
|||||||
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
|
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
|
||||||
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
|
||||||
CSP_WHITELIST: ${CSP_WHITELIST:-}
|
CSP_WHITELIST: ${CSP_WHITELIST:-}
|
||||||
|
ALLOW_EMBED: ${ALLOW_EMBED:-false}
|
||||||
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
|
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
|
||||||
MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
|
MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
|
||||||
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
|
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
|
||||||
@ -1028,7 +1030,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./volumes/opengauss/data:/var/lib/opengauss/data
|
- ./volumes/opengauss/data:/var/lib/opengauss/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"]
|
test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1" ]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 10
|
retries: 10
|
||||||
|
@ -29,6 +29,8 @@ NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=60000
|
|||||||
|
|
||||||
# CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
# CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
||||||
NEXT_PUBLIC_CSP_WHITELIST=
|
NEXT_PUBLIC_CSP_WHITELIST=
|
||||||
|
# Default is not allow to embed into iframe to prevent Clickjacking: https://owasp.org/www-community/attacks/Clickjacking
|
||||||
|
NEXT_PUBLIC_ALLOW_EMBED=
|
||||||
|
|
||||||
# Github Access Token, used for invoking Github API
|
# Github Access Token, used for invoking Github API
|
||||||
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=
|
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=
|
||||||
|
@ -29,7 +29,7 @@ const OPTION_MAP = {
|
|||||||
iframe: {
|
iframe: {
|
||||||
getContent: (url: string, token: string) =>
|
getContent: (url: string, token: string) =>
|
||||||
`<iframe
|
`<iframe
|
||||||
src="${url}${basePath}/chatbot/${token}"
|
src="${url}${basePath}/chat/${token}"
|
||||||
style="width: 100%; height: 100%; min-height: 700px"
|
style="width: 100%; height: 100%; min-height: 700px"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
allow="microphone">
|
allow="microphone">
|
||||||
|
@ -25,6 +25,7 @@ export NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED}
|
|||||||
|
|
||||||
export NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS}
|
export NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS}
|
||||||
export NEXT_PUBLIC_CSP_WHITELIST=${CSP_WHITELIST}
|
export NEXT_PUBLIC_CSP_WHITELIST=${CSP_WHITELIST}
|
||||||
|
export NEXT_PUBLIC_ALLOW_EMBED=${ALLOW_EMBED}
|
||||||
export NEXT_PUBLIC_TOP_K_MAX_VALUE=${TOP_K_MAX_VALUE}
|
export NEXT_PUBLIC_TOP_K_MAX_VALUE=${TOP_K_MAX_VALUE}
|
||||||
export NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
|
export NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
|
||||||
export NEXT_PUBLIC_MAX_TOOLS_NUM=${MAX_TOOLS_NUM}
|
export NEXT_PUBLIC_MAX_TOOLS_NUM=${MAX_TOOLS_NUM}
|
||||||
|
@ -3,10 +3,26 @@ import { NextResponse } from 'next/server'
|
|||||||
|
|
||||||
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com'
|
const NECESSARY_DOMAIN = '*.sentry.io http://localhost:* http://127.0.0.1:* https://analytics.google.com googletagmanager.com *.googletagmanager.com https://www.google-analytics.com https://api.github.com'
|
||||||
|
|
||||||
|
const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => {
|
||||||
|
// prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking
|
||||||
|
// Chatbot page should be allowed to be embedded in iframe. It's a feature
|
||||||
|
if (process.env.NEXT_PUBLIC_ALLOW_EMBED !== 'true' && !pathname.startsWith('/chat'))
|
||||||
|
response.headers.set('X-Frame-Options', 'DENY')
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
export function middleware(request: NextRequest) {
|
export function middleware(request: NextRequest) {
|
||||||
|
const { pathname } = request.nextUrl
|
||||||
|
const requestHeaders = new Headers(request.headers)
|
||||||
|
const response = NextResponse.next({
|
||||||
|
request: {
|
||||||
|
headers: requestHeaders,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production'
|
const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production'
|
||||||
if (!isWhiteListEnabled)
|
if (!isWhiteListEnabled)
|
||||||
return NextResponse.next()
|
return wrapResponseWithXFrameOptions(response, pathname)
|
||||||
|
|
||||||
const whiteList = `${process.env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}`
|
const whiteList = `${process.env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}`
|
||||||
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
|
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
|
||||||
@ -33,7 +49,6 @@ export function middleware(request: NextRequest) {
|
|||||||
.replace(/\s{2,}/g, ' ')
|
.replace(/\s{2,}/g, ' ')
|
||||||
.trim()
|
.trim()
|
||||||
|
|
||||||
const requestHeaders = new Headers(request.headers)
|
|
||||||
requestHeaders.set('x-nonce', nonce)
|
requestHeaders.set('x-nonce', nonce)
|
||||||
|
|
||||||
requestHeaders.set(
|
requestHeaders.set(
|
||||||
@ -41,17 +56,12 @@ export function middleware(request: NextRequest) {
|
|||||||
contentSecurityPolicyHeaderValue,
|
contentSecurityPolicyHeaderValue,
|
||||||
)
|
)
|
||||||
|
|
||||||
const response = NextResponse.next({
|
|
||||||
request: {
|
|
||||||
headers: requestHeaders,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
response.headers.set(
|
response.headers.set(
|
||||||
'Content-Security-Policy',
|
'Content-Security-Policy',
|
||||||
contentSecurityPolicyHeaderValue,
|
contentSecurityPolicyHeaderValue,
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return wrapResponseWithXFrameOptions(response, pathname)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user