stalwartlabs / mail-server

Secure & Modern All-in-One Mail Server (IMAP, JMAP, POP3, SMTP)
https://stalw.art
4.53k stars 174 forks source link

[bug]: A freshly installed instance with a reverse proxy set up is asking for a TOTP challenge, even though no one has configured it before. #722

Closed OrvilleQ closed 2 weeks ago

OrvilleQ commented 2 weeks ago

What happened?

After running the install.sh script, I set up a reverse proxy and accessed the site using HTTPS. When I tried to log in with the admin account, it asked for a TOTP challenge, which I never set up. This issue does not occur when accessing the site directly via plain HTTP on port 8080.

I can't find any error inside the log file. but in the webui's log tacing page, there is an error called X-Forwarded-For header is missing.

image

Which is also wired. I enabled the Obtain remote IP from Forwarded header, and the request from nginx has X-Forwarded-For set corectly also. Not sure if this is related to the issue.

How can we reproduce the problem?

  1. Install stalwart with install.sh
  2. set a reverse proxy with ssl enable (I use bunkerweb WAF which use nginx)
  3. try login in with https, asked me for a TOTP challenge. image
  4. try login in with 8080 and plain http, did not ask me for that challenge.

Version

v0.9.x

What database are you using?

None

What blob storage are you using?

None

Where is your directory located?

None

What operating system are you using?

Linux

Relevant log output

2024-08-27T12:52:49Z INFO Starting Stalwart Mail Server v0.9.2 (server.startup) version = 0.9.2
2024-08-27T12:52:49Z TRACE Data store iteration operation (store.data-iterate) elapsed = 0ms
2024-08-27T12:52:49Z TRACE Data store iteration operation (store.data-iterate) elapsed = 0ms
2024-08-27T12:52:49Z TRACE Blob read operation (store.blob-read) key = base64:U1RBTFdBUlRfV0VCQURNSU4=, elapsed = 7ms, size = 1994677
2024-08-27T12:52:49Z INFO Webadmin resource unpacked (resource.webadmin-unpacked) path = "/tmp/STALWART_WEBADMIN"
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "http", localIp = ::, localPort = 8080, tls = false
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "https", localIp = ::, localPort = 8443, tls = true
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "imap", localIp = ::, localPort = 143, tls = false
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "imaptls", localIp = ::, localPort = 993, tls = true
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "pop3", localIp = ::, localPort = 110, tls = false
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "pop3s", localIp = ::, localPort = 995, tls = true
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "sieve", localIp = ::, localPort = 4190, tls = false
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "smtp", localIp = ::, localPort = 25, tls = false
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "submission", localIp = ::, localPort = 587, tls = false
2024-08-27T12:52:49Z INFO Network listener started (network.listen-start) listenerId = "submissions", localIp = ::, localPort = 465, tls = true
2024-08-27T12:52:49Z INFO Housekeeper process started (housekeeper.start)
2024-08-27T12:52:49Z DEBUG Housekeeper task scheduled (housekeeper.schedule) due = 2024-08-27T13:14:59Z, id = "Session"
2024-08-27T12:52:49Z DEBUG Housekeeper task scheduled (housekeeper.schedule) due = 2024-08-27T23:59:59Z, id = "Account"
2024-08-27T12:52:49Z TRACE Data store iteration operation (store.data-iterate) elapsed = 0ms
2024-08-27T12:52:49Z DEBUG Housekeeper task scheduled (housekeeper.schedule) due = 2024-08-28T02:59:59Z, id = "Store(0)"
2024-08-27T12:52:49Z DEBUG Housekeeper task scheduled (housekeeper.schedule) due = 2024-08-28T03:59:59Z, id = "Store(1)"
2024-08-27T12:52:49Z DEBUG Housekeeper task scheduled (housekeeper.schedule) due = 2024-08-28T04:59:59Z, id = "Store(2)"
2024-08-27T12:52:59Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47752
2024-08-27T12:52:59Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47752, remoteIp = 23.165.40.104, url = "/"
2024-08-27T12:52:59Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47752, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:52:59Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47752, contents = [binary data], code = 200, size = 2137
2024-08-27T12:52:59Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47752, elapsed = 0ms
2024-08-27T12:52:59Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47768
2024-08-27T12:52:59Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47778
2024-08-27T12:52:59Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47790
2024-08-27T12:52:59Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47790, remoteIp = 23.165.40.104, url = "/webadmin-338ec427211aa677.js"
2024-08-27T12:52:59Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47790, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:52:59Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47768, remoteIp = 23.165.40.104, url = "/output-fa0d16a5e0b2aecd.css"
2024-08-27T12:52:59Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47768, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:52:59Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47778, remoteIp = 23.165.40.104, url = "/webadmin-338ec427211aa677_bg.wasm"
2024-08-27T12:52:59Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47778, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:52:59Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47790, contents = [binary data], code = 200, size = 58864
2024-08-27T12:52:59Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47768, contents = [binary data], code = 200, size = 61593
2024-08-27T12:52:59Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47790, elapsed = 0ms
2024-08-27T12:52:59Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47768, elapsed = 8ms
2024-08-27T12:52:59Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47778, contents = [binary data], code = 200, size = 5599314
2024-08-27T12:52:59Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47778, elapsed = 13ms
2024-08-27T12:53:00Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47798
2024-08-27T12:53:00Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47798, remoteIp = 23.165.40.104, url = "/android-chrome-512x512-ffbecec3ff69a6ca.png"
2024-08-27T12:53:00Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47798, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:53:00Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47798, contents = [binary data], code = 200, size = 104372
2024-08-27T12:53:00Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47798, elapsed = 0ms
2024-08-27T12:53:01Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47806
2024-08-27T12:53:01Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47806, remoteIp = 23.165.40.104, url = "/android-chrome-192x192-6aba841e8fc78b83.png"
2024-08-27T12:53:01Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47806, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:53:01Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47806, contents = [binary data], code = 200, size = 21505
2024-08-27T12:53:01Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47806, elapsed = 0ms
2024-08-27T12:53:01Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47816
2024-08-27T12:53:01Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47816, remoteIp = 23.165.40.104, url = "/site.webmanifest"
2024-08-27T12:53:01Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47816, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:53:01Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47816, contents = [binary data], code = 200, size = 263
2024-08-27T12:53:01Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47816, elapsed = 0ms
2024-08-27T12:53:01Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47832
2024-08-27T12:53:01Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47832, remoteIp = 23.165.40.104, url = "/logo.svg"
2024-08-27T12:53:01Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47832, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:53:01Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47832, contents = [binary data], code = 200, size = 3523
2024-08-27T12:53:01Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47832, elapsed = 1ms
2024-08-27T12:53:01Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47842
2024-08-27T12:53:01Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47842, remoteIp = 23.165.40.104, url = "/favicon-16x16-9b86a4caa1876c9e.png"
2024-08-27T12:53:01Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47842, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:53:01Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47842, contents = [binary data], code = 200, size = 812
2024-08-27T12:53:01Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47842, elapsed = 0ms
2024-08-27T12:53:02Z INFO HTTP connection started (http.connection-start) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47850
2024-08-27T12:53:02Z DEBUG HTTP request URL (http.request-url) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47850, remoteIp = 23.165.40.104, url = "/favicon-32x32-33abe4bf0e53934c.png"
2024-08-27T12:53:02Z TRACE Expression evaluation result (eval.result) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47850, id = "server.http.allowed-endpoint", result = "Integer(200)"
2024-08-27T12:53:02Z TRACE HTTP response body (http.response-body) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47850, contents = [binary data], code = 200, size = 1784
2024-08-27T12:53:02Z INFO HTTP connection ended (http.connection-end) listenerId = "http", localPort = 8080, remoteIp = 127.0.0.1, remotePort = 47850, elapsed = 0ms

Code of Conduct

mdecimus commented 2 weeks ago

This is because your proxy is returning a forbidden response which is the Stalwart response that the webadmin uses to detect a missing TOTP token.

OrvilleQ commented 2 weeks ago

@mdecimus Thanks for your reply. I do found some 403 response inside nginx's access.log

mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:52 +0000] "GET / HTTP/2.0" 200 2137 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:53 +0000] "GET /output-fa0d16a5e0b2aecd.css HTTP/2.0" 200 61593 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:53 +0000] "GET /webadmin-338ec427211aa677.js HTTP/2.0" 200 58864 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:54 +0000] "GET /webadmin-338ec427211aa677_bg.wasm HTTP/2.0" 200 5599314 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:54 +0000] "GET /android-chrome-512x512-ffbecec3ff69a6ca.png HTTP/2.0" 200 104372 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /site.webmanifest HTTP/2.0" 200 263 "https://mai.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /logo.svg HTTP/2.0" 200 3523 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /android-chrome-192x192-6aba841e8fc78b83.png HTTP/2.0" 200 21505 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:55 +0000] "GET /favicon-16x16-9b86a4caa1876c9e.png HTTP/2.0" 200 812 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:56 +0000] "GET /favicon-32x32-33abe4bf0e53934c.png HTTP/2.0" 200 1784 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - - [27/Aug/2024:13:52:56 +0000] "GET /favicon-cfb706b7132e0fd2.ico HTTP/2.0" 200 15406 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"
mail.masked.com 199.xxx.xxx.xxx - 123 [27/Aug/2024:13:52:57 +0000] "POST /api/oauth HTTP/2.0" 403 548 "https://mail.masked.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0"

To me it seems like the 403 response is came from the stalwart server rather than nginx?

EDIT: It seems like the POST request is blocked by modsecurity.

OrvilleQ commented 2 weeks ago

Log from Modsecurity.

user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0
origin: https://mail.masked.com
sec-ch-ua-mobile: ?0
content-type: text/plain;charset=UTF-8
accept: */*
sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Microsoft Edge";v="128"
sec-ch-ua-platform: "Windows"
referer: https://mail.masked.com/
content-length: 58
priority: u=1, i
authorization: Basic YWRtaW46eE5ENmJUV3FqZQ==
host: mail.masked.com
sec-fetch-mode: cors
sec-fetch-dest: empty
accept-encoding: gzip, deflate, br, zstd
accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

---jiBGgn7T---C--
{"type":"Code","client_id":"webadmin","redirect_uri":null}

---jiBGgn7T---F--
HTTP/2.0 403
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), hid=(), idle-detection=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), usb=(), web-share=(), xr-spatial-tracking=()
Feature-Policy: accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; battery 'none'; camera 'none'; display-capture 'none'; document-domain 'none'; encrypted-media 'none'; execution-while-not-rendered 'none'; execution-while-out-of-viewport 'none'; fullscreen 'none'; geolocation 'none'; gyroscope 'none'; layout-animation 'none'; legacy-image-formats 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; navigation-override 'none'; payment 'none'; picture-in-picture 'none'; publickey-credentials-get 'none'; speaker-selection 'none'; sync-xhr 'none'; unoptimized-images 'none'; unsized-media 'none'; usb 'none'; screen-wake-lock 'none'; web-share 'none'; xr-spatial-tracking 'none';
Date: Tue, 27 Aug 2024 14:02:10 GMT
X-XSS-Protection: 1; mode=block
Connection: close
X-Powered-By:
X-Content-Type-Options: nosniff
Content-Type: text/html
Content-Length: 548
Server:
Server:
Content-Security-Policy: object-src 'none'; form-action 'self'; frame-ancestors 'self';
Strict-Transport-Security: max-age=31536000
Expect-CT:
X-AspNet-Version:
X-AspNetMvc-Version:

---jiBGgn7T---H--
ModSecurity: Warning. Matched "Operator `Within' with parameter `|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudev (16 characters omitted)' against variable `TX:content_type' (Value: `|text/plain|' ) [file "/usr/share/bunkerweb/core/modsecurity/files/coreruleset-v3/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "938"] [id "920420"] [rev ""] [msg "Request content type is not allowed by policy"] [data "|text/plain|"] [severity "2"] [ver "OWASP_CRS/3.3.5"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/255/153"] [tag "PCI/12.1"] [hostname "49.xxx.xxx.xxx"] [uri "/api/oauth"] [unique_id "172476733036.320579"] [ref "o0,10v394,24t:lowercase"]
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/usr/share/bunkerweb/core/modsecurity/files/coreruleset-v3/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "81"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.5"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "49.xxx.xxx.xxx"] [uri "/api/oauth"] [unique_id "172476733036.320579"] [ref ""]

---jiBGgn7T---Z--

Seems like the POST request is using content_type "text/plain", which is not allowed by Modsecurity's rule 920420. The allowed content types by this rule are `|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudev (16 characters omitted).

I will check if there is a way to temporarily disable this rule. Also can this issue be considered as an improvement to be addressed later? @mdecimus

mdecimus commented 2 weeks ago

Something else is generating that content-type header. Stalwart does not return text/plain unless you are accessing a text file such asrobots.txt, mta-sts.txt, etc.

OrvilleQ commented 2 weeks ago

Something else is generating that content-type header. Stalwart does not return text/plain unless you are accessing a text file such asrobots.txt, mta-sts.txt, etc.

It's not Stalwart return text/plain, it's the login in interface POST /api/oauth and /api/token with text/plain.

image image

Maybe I should reopen this issue at https://github.com/stalwartlabs/webadmin?