Skip to content

Commit e7b8ae7

Browse files
committed
fix: clickjacking
1 parent f191d37 commit e7b8ae7

File tree

6 files changed

+31
-15
lines changed

6 files changed

+31
-15
lines changed

docker/docker-compose-template.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
x-shared-env: &shared-api-worker-env
1+
x-shared-env: &shared-api-worker-env
22
services:
33
# API service
44
api:
@@ -66,6 +66,7 @@ services:
6666
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
6767
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
6868
CSP_WHITELIST: ${CSP_WHITELIST:-}
69+
ALLOW_EMBED: ${ALLOW_EMBED:-}
6970
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
7071
MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
7172
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
@@ -552,7 +553,7 @@ services:
552553
volumes:
553554
- ./volumes/opengauss/data:/var/lib/opengauss/data
554555
healthcheck:
555-
test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"]
556+
test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1" ]
556557
interval: 10s
557558
timeout: 10s
558559
retries: 10

docker/docker-compose.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ x-shared-env: &shared-api-worker-env
419419
POSITION_PROVIDER_INCLUDES: ${POSITION_PROVIDER_INCLUDES:-}
420420
POSITION_PROVIDER_EXCLUDES: ${POSITION_PROVIDER_EXCLUDES:-}
421421
CSP_WHITELIST: ${CSP_WHITELIST:-}
422+
ALLOW_EMBED: ${ALLOW_EMBED:-}
422423
CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false}
423424
MAX_SUBMIT_COUNT: ${MAX_SUBMIT_COUNT:-100}
424425
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-10}
@@ -539,6 +540,7 @@ services:
539540
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
540541
TEXT_GENERATION_TIMEOUT_MS: ${TEXT_GENERATION_TIMEOUT_MS:-60000}
541542
CSP_WHITELIST: ${CSP_WHITELIST:-}
543+
ALLOW_EMBED: ${ALLOW_EMBED:-}
542544
MARKETPLACE_API_URL: ${MARKETPLACE_API_URL:-https://marketplace.dify.ai}
543545
MARKETPLACE_URL: ${MARKETPLACE_URL:-https://marketplace.dify.ai}
544546
TOP_K_MAX_VALUE: ${TOP_K_MAX_VALUE:-}
@@ -1025,7 +1027,7 @@ services:
10251027
volumes:
10261028
- ./volumes/opengauss/data:/var/lib/opengauss/data
10271029
healthcheck:
1028-
test: ["CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1"]
1030+
test: [ "CMD-SHELL", "netstat -lntp | grep tcp6 > /dev/null 2>&1" ]
10291031
interval: 10s
10301032
timeout: 10s
10311033
retries: 10

web/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=60000
2929

3030
# CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
3131
NEXT_PUBLIC_CSP_WHITELIST=
32+
# Default is not allow to embed into iframe to prevent Clickjacking: https://owasp.org/www-community/attacks/Clickjacking
33+
NEXT_PUBLIC_ALLOW_EMBED=
3234

3335
# Github Access Token, used for invoking Github API
3436
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=

web/app/components/app/overview/embedded/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const OPTION_MAP = {
2929
iframe: {
3030
getContent: (url: string, token: string) =>
3131
`<iframe
32-
src="${url}${basePath}/chatbot/${token}"
32+
src="${url}${basePath}/chat/${token}"
3333
style="width: 100%; height: 100%; min-height: 700px"
3434
frameborder="0"
3535
allow="microphone">
@@ -42,10 +42,10 @@ const OPTION_MAP = {
4242
token: '${token}'${isTestEnv
4343
? `,
4444
isDev: true`
45-
: ''}${IS_CE_EDITION
46-
? `,
45+
: ''}${IS_CE_EDITION
46+
? `,
4747
baseUrl: '${url}${basePath}'`
48-
: ''},
48+
: ''},
4949
systemVariables: {
5050
// user_id: 'YOU CAN DEFINE USER ID HERE',
5151
},

web/docker/entrypoint.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED}
2525

2626
export NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS}
2727
export NEXT_PUBLIC_CSP_WHITELIST=${CSP_WHITELIST}
28+
export NEXT_PUBLIC_ALLOW_EMBED=${ALLOW_EMBED}
2829
export NEXT_PUBLIC_TOP_K_MAX_VALUE=${TOP_K_MAX_VALUE}
2930
export NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=${INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH}
3031
export NEXT_PUBLIC_MAX_TOOLS_NUM=${MAX_TOOLS_NUM}

web/middleware.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@ import { NextResponse } from 'next/server'
33

44
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'
55

6+
const wrapResponseWithXFrameOptions = (response: NextResponse, pathname: string) => {
7+
// prevent clickjacking: https://owasp.org/www-community/attacks/Clickjacking
8+
// Chatbot page should be allowed to be embedded in iframe. It's a feature
9+
if (process.env.NEXT_PUBLIC_ALLOW_EMBED !== 'true' && !pathname.startsWith('/chat'))
10+
response.headers.set('X-Frame-Options', 'DENY')
11+
12+
return response
13+
}
614
export function middleware(request: NextRequest) {
15+
const { pathname } = request.nextUrl
16+
const requestHeaders = new Headers(request.headers)
17+
const response = NextResponse.next({
18+
request: {
19+
headers: requestHeaders,
20+
},
21+
})
22+
723
const isWhiteListEnabled = !!process.env.NEXT_PUBLIC_CSP_WHITELIST && process.env.NODE_ENV === 'production'
824
if (!isWhiteListEnabled)
9-
return NextResponse.next()
25+
return wrapResponseWithXFrameOptions(response, pathname)
1026

1127
const whiteList = `${process.env.NEXT_PUBLIC_CSP_WHITELIST} ${NECESSARY_DOMAIN}`
1228
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
@@ -33,25 +49,19 @@ export function middleware(request: NextRequest) {
3349
.replace(/\s{2,}/g, ' ')
3450
.trim()
3551

36-
const requestHeaders = new Headers(request.headers)
3752
requestHeaders.set('x-nonce', nonce)
3853

3954
requestHeaders.set(
4055
'Content-Security-Policy',
4156
contentSecurityPolicyHeaderValue,
4257
)
4358

44-
const response = NextResponse.next({
45-
request: {
46-
headers: requestHeaders,
47-
},
48-
})
4959
response.headers.set(
5060
'Content-Security-Policy',
5161
contentSecurityPolicyHeaderValue,
5262
)
5363

54-
return response
64+
return wrapResponseWithXFrameOptions(response, pathname)
5565
}
5666

5767
export const config = {

0 commit comments

Comments
 (0)