config.allowed_referer
Massimo Melina committed
Mar 13, 2022 at 20:27 UTC
0c4dd9093848b8ec46affaa38354a34f74cbac98
5 files changed
+18
-1
README.md
+1
@@ -204,6 +204,7 @@ Supported entries are:
204
- `https_port` listen on a specific port. Default is 443.
205
- `cert` use this file for https certificate. Minimum to start https is to give a cert and a private_key. Default is none.
206
- `private_key` use this file for https private key. Default is none.
207
+- `allowed_referer` you can decide what domains can link to your files. Wildcards supported. Default is any.
208
- `block` a list of rules that will block connections. At the moment only `ip` rules are supported. E.g.:
209
```
210
block:
admin/src/ConfigPage.ts
+1
@@ -72,6 +72,7 @@ export default function ConfigPage() {
72
},
73
{ k: 'accounts', comp: StringField, label: "Accounts file" },
74
{ k: 'open_browser_at_start', comp: BoolField },
75
+ { k: 'allowed_referer', placeholder: "any", helperText: "Leave empty to allow any", },
76
{ k: 'zip_calculate_size_for_seconds', comp: NumberField, md: 6, label: "Calculate ZIP size for seconds",
77
helperText: "If time is not enough, the browser will not show download percentage" },
78
{ k: 'mime', comp: StringStringField,
server/src/middlewares.ts
+9
-1
@@ -3,7 +3,7 @@
3
import compress from 'koa-compress'
4
import Koa from 'koa'
5
import session from 'koa-session'
6
-import { ADMIN_URI, BUILD_TIMESTAMP, SESSION_DURATION } from './const'
6
+import { ADMIN_URI, BUILD_TIMESTAMP, FORBIDDEN, SESSION_DURATION } from './const'
7
import Application from 'koa'
8
import { FRONTEND_URI } from './const'
9
import { cantReadStatusCode, hasPermission, urlToNode } from './vfs'
@@ -17,6 +17,7 @@ import { getAccount, getCurrentUsername } from './perm'
17
import { getConfig, subscribeConfig } from './config'
18
import { getConnections, socket2connection, updateConnection } from './connections'
19
import { Socket } from 'net'
20
+import { isMatch } from 'micromatch'
21
22
export const gzipper = compress({
23
threshold: 2048,
@@ -57,6 +58,13 @@ export const serveGuiAndSharedFiles: Koa.Middleware = async (ctx, next) => {
58
const { path } = ctx
59
if (ctx.body)
60
return next()
61
+ const allowedRef = getConfig('allowed_referer')
62
+ if (allowedRef) {
63
+ const ref = /\/\/([^:/]+)/.exec(ctx.get('referer'))?.[1] // extract host from url
64
+ if (ref && ref !== ctx.get('host')?.split(':')[0] // automatic accept if referer is basically the hosting domain
65
+ && !isMatch(ref, allowedRef))
66
+ return ctx.status = FORBIDDEN
67
+ }
68
if (path.startsWith(FRONTEND_URI))
69
return serveFrontendPrefixed(ctx,next)
70
if (path+'/' === ADMIN_URI)
tests/config.yaml
+1
@@ -1,3 +1,4 @@
1
+allowed_referer: x.com
2
vfs:
3
mime:
4
"*.jpg|*.png|*.mp3|*.txt": auto
tests/test.ts
+6
@@ -61,8 +61,13 @@ describe('basics', () => {
61
it('zip.head', req('/f1/?get=zip', { empty:true, length:13074 }, { method:'HEAD' }) )
62
it('zip.alfa is forbidden', req('/protectFromAbove/child/?get=zip&list=alfa.txt*renamed', { empty: true, length:138 }, { method:'HEAD' }))
63
it('login', reqApi('login', { username, password }, 406)) // by default, we don't support clear-text login
64
+
65
+ it('referer', req('/f1/page/gpl.png', 403, {
66
+ headers: { Referer: 'https://some-website.com/try-to-trick/x.com/' }
67
+ }))
68
})
69
70
+/*
71
let cookie:any
72
describe('after-login', () => {
73
before(req(API+'login', (data, res) => Boolean(cookie = res.headers['set-cookie']), {
@@ -74,6 +79,7 @@ describe('after-login', () => {
79
headers: { cookie },
80
})(done))
81
})
82
+*/
83
84
type Tester = number
85
| ((data: any, fullResponse: any) => boolean | Error)