Add Security Headers to webadmin #1030

Open
opened 2026-10-05 04:59:18 +02:00 by adam · 5 comments
Owner

If you check the webadmin page with https://securityheaders.com you get warnings for all related http security headers:

  • Strict-Transport-Security
  • Content-Security-Policy
  • X-Frame-Options
  • X-Content-Type-Options
  • Referrer-Policy
  • Permissions-Policy

I'm not a cpp coder and I don't really know how other users are using the webadmin interface, so I can't evaluate the consequences of such headers. I've copied some values that I use for other projects as suggestions and I wonder if it would improve the webadmin security.

Found the AddHeader function in the HTTPSock.cpp file.

Examples:
AddHeader("X-Frame-Options", "deny");
AddHeader("X-Content-Type-Options", "nosniff");
AddHeader("X-XSS-Protection", "1; mode=block");
AddHeader("Permissions-Policy", "accelerometer=(), ambient-light-sensor(), autoplay=(), camera=(), display-capture(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), usb=()");

If using SSL:
AddHeader("Strict-Transport-Security", "max-age=31536000");

CSP is more complicated, needs to match all loaded webadmin ressources and needs to be tested heavily:
AddHeader("Content-Security-Policy", "connect-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; script-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none';");

Is that something that can rather easily be implemented for future versions?

If you check the webadmin page with https://securityheaders.com you get warnings for all related http security headers: - Strict-Transport-Security - Content-Security-Policy - X-Frame-Options - X-Content-Type-Options - Referrer-Policy - Permissions-Policy I'm not a cpp coder and I don't really know how other users are using the webadmin interface, so I can't evaluate the consequences of such headers. I've copied some values that I use for other projects as suggestions and I wonder if it would improve the webadmin security. Found the **AddHeader** function in the **HTTPSock.cpp** file. **Examples:** AddHeader("X-Frame-Options", "deny"); AddHeader("X-Content-Type-Options", "nosniff"); AddHeader("X-XSS-Protection", "1; mode=block"); AddHeader("Permissions-Policy", "accelerometer=(), ambient-light-sensor(), autoplay=(), camera=(), display-capture(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), usb=()"); **If using SSL:** AddHeader("Strict-Transport-Security", "max-age=31536000"); **CSP** is more complicated, needs to match all loaded webadmin ressources and needs to be tested heavily: AddHeader("Content-Security-Policy", "connect-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; script-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none';"); Is that something that can rather easily be implemented for future versions? <!-- github-sync-id:1913259472 -->
Author
Owner

Shouldn't that be the job of the reverse proxy in front of the znc webinterface?

Shouldn't that be the job of the reverse proxy in front of the znc webinterface? <!-- github-comment-id:1735640539 -->
Author
Owner

Shouldn't that be the job of the reverse proxy in front of the znc webinterface?

I technically would say yes, but the znc interface is sometimes direct-to-internet by users and not reverse proxied. It should be reverse proxied but not everyone does that step.

> Shouldn't that be the job of the reverse proxy in front of the znc webinterface? I technically would say yes, but the znc interface is sometimes direct-to-internet by users and not reverse proxied. It should be reverse proxied but not everyone does that step. <!-- github-comment-id:1735729690 -->
Author
Owner

I'd need to investigate what all of these mean :)

Re Strict-Transport-Security - this should be an option, it shouldn't be enabled unconditionally. If user doesn't want to commit to maintain SSL there, it will break ability to connect to the port. This is related to the fact that we can't even automatically get the signed certificate

Maybe just add an option to add an arbitrary header

I'd need to investigate what all of these mean :) Re Strict-Transport-Security - this should be an option, it shouldn't be enabled unconditionally. If user doesn't want to commit to maintain SSL there, it will break ability to connect to the port. This is related to the fact that we can't even automatically get the signed certificate Maybe just add an option to add an arbitrary header <!-- github-comment-id:1736456839 -->
Author
Owner

Firstly, you should not implement headers in any environment unless you really know what they are doing or what they're for, and what changing those headers' values may do to your applications.


(all info here is a combo of info from Mozilla's documentation on MDN, and also from my knowledge about how every header works.)

X-Frame-Options - This header is used to indicate whether a browser should be allowed to render a given page in a frame, iframe, embed, or object. This helps prevent clickjacking by using "deny" or "sameorigin" (the second of which says all ancestor frames in the connection / inheritence chain must be the same origin).

X-Content-Type-Options - Used as a marker used by the server to indicate that the MIME types advertised in Content-Type headers should be followed and NOT changed. Also can be used to prevent MINE type sniffing by saying MIME types are deliberately configured ("nosniff" is a good default)

X-XSS-Protection - This is NOT part of any standard, and should NOT be used in production! It is designed as part of Internet Explorer, Chrome, and Safari that stops pages from loading when cross-site scripting is detected. This is largely unnecessary in modern browsers because of the Content-Security-Policy header, its duties, and the default disabling of in-line JavaScript. Of note on this, though, Chrome has removed the XSS Auditor which checks against X-XSS-Protection, Firefox has not and will not implement X-XSS-Protection, and Microsoft Edge has retired their XSS filter which utilizes X-XSS-Protection; as such, unless we must support ancient legacy browsers we should NEVER set this header and instead focus on Content-Security-Policy headers.

Protection-Policy - This header provides a mechanism to allow and deny the use of browser features in a document or within any iframe elements in the document. Details of the various permissions and such can be found here in Mozilla HTTP devel documents. Before just blindly accepting any permissions policies, it must be reviewed if any of these features are actually needed or not. There may be cases where it's determined that it is in fact needed.

Strict-Transport-Security - This can be a dangerous header. If any domain is observed with this header active, it will force all browsers to use HTTPS only on connecting to all subdomains of the base domain. So if you have ZNC webadmin at zncadmin.example.com, then all of example.com will be forced to use HTTPS. Which is dangerous because you might not have HTTPS functionality deployed on the entire domain, especially if this domain is internal. It should not be activated unless you KNOW what you're doing

Content-Security-Policy - As it states, this is an added layer of security (designed for, among other reasons, to replace X-XSS-Protection) that can be used to define specific content policies and what is or is NOT allowed to be executed on a website. The primary reason for this is to mitigate cross site scripting attacks and other malicious vectors, however this may break sites and their functionality so great care has to be taken using CSP, and that should be configured by the administrators and not blindly.


I agree completely with @DarthGandalf. It would be easier to allow arbitrary headers and responses. I would suggest if we do implement a mechanism for custom headers on webadmin, we use either a multiline text box that'd accept syntax like this:

header-one value
header-two multiple spaced value
header-three zalgo is tony the pony HE COMES

... or a nested option like we do for adding channels where we have a section called "Webadmin Headers" and "Add custom header" that then prompts for the header and values and then allows us to easily check and select things.


If we must do some sane defaults that we should be allowed to override, then the defaults should, in my opinion as an IT Security person, be as follows:

Not use in any way shape or form:

  • X-XSS-Protection (deprecated, use Content-Security-Policy instead)

Do not set by default, allow to be manually specified by admins:

  • Strict-Transport-Security
  • Content-Security-Policy
  • Permissions-Policy

Set by default, but permit overrides (present in default config creation text field or in default-loaded options in webadmin config):

  • X-Frame-Options: sameorigin
  • X-Content-Type-Options: nosniff

However, I would say that none of these should be hard-coded and unable to be configured as requested in this GH issue, because setting these blindly is not the way to approach proper header security settings in web-applications (such as ZNC Webadmin)

Firstly, **you should not implement headers in any environment unless you really know what they are doing or what they're for**, and what changing those headers' values may do to your applications. --- (all info here is a combo of info from Mozilla's documentation on MDN, and also from my knowledge about how every header works.) **X-Frame-Options** - This header is used to indicate whether a browser should be allowed to render a given page in a frame, iframe, embed, or object. This helps prevent clickjacking by using "deny" or "sameorigin" (the second of which says all ancestor frames in the connection / inheritence chain must be the same origin). **X-Content-Type-Options** - Used as a marker used by the server to indicate that the MIME types advertised in Content-Type headers should be followed and NOT changed. Also can be used to prevent MINE type sniffing by saying MIME types are deliberately configured ("nosniff" is a good default) **X-XSS-Protection** - **This is NOT part of any standard, and should NOT be used in production!** It is designed as part of Internet Explorer, Chrome, and Safari that stops pages from loading when cross-site scripting is detected. This is **largely unnecessary** in modern browsers because of the Content-Security-Policy header, its duties, and the default disabling of in-line JavaScript. Of note on this, though, Chrome has removed the XSS Auditor which checks against X-XSS-Protection, Firefox has not **and will not** implement X-XSS-Protection, and Microsoft Edge has retired their XSS filter which utilizes X-XSS-Protection; as such, unless we must support ancient legacy browsers we should NEVER set this header and instead focus on Content-Security-Policy headers. **Protection-Policy** - This header provides a mechanism to allow and deny the use of browser features in a document or within any iframe elements in the document. Details of the various permissions and such can be found [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy) in Mozilla HTTP devel documents. **Before just blindly accepting any permissions policies**, it must be reviewed if any of these features are actually needed or not. There may be cases where it's determined that it is in fact needed. **Strict-Transport-Security** - **This can be a dangerous header.** If *any* domain is observed with this header active, it will **force all browsers to use HTTPS only on connecting to all subdomains of the base domain**. So if you have ZNC webadmin at `zncadmin.example.com`, then all of `example.com` will be forced to use HTTPS. Which is dangerous because you might not have HTTPS functionality deployed on the entire domain, especially if this domain is internal. **It should not be activated unless you KNOW what you're doing** **Content-Security-Policy** - As it states, this is an added layer of security (designed for, among other reasons, to replace X-XSS-Protection) that can be used to define specific content policies and what is or is NOT allowed to be executed on a website. The primary reason for this is to mitigate cross site scripting attacks and other malicious vectors, however this **may** break sites and their functionality so great care has to be taken using CSP, and that should be configured by the administrators and not blindly. --- I agree completely with @DarthGandalf. It would be easier to allow arbitrary headers and responses. I would suggest if we do implement a mechanism for custom headers on webadmin, we use either a multiline text box that'd accept syntax like this: ```text header-one value header-two multiple spaced value header-three zalgo is tony the pony HE COMES ``` ... or a nested option like we do for adding channels where we have a section called "Webadmin Headers" and "Add custom header" that then prompts for the header and values and then allows us to easily check and select things. --- If we must do some sane defaults **that we should be allowed to override**, then the defaults should, in my opinion as an IT Security person, be as follows: **Not use in any way shape or form**: - `X-XSS-Protection` (deprecated, use `Content-Security-Policy` instead) **Do not set by default, allow to be manually specified by admins**: - `Strict-Transport-Security` - `Content-Security-Policy` - `Permissions-Policy` **Set by default, but permit overrides (present in default config creation text field or in default-loaded options in webadmin config)**: - `X-Frame-Options: sameorigin` - `X-Content-Type-Options: nosniff` However, I would say that *none* of these should be hard-coded and unable to be configured as requested in this GH issue, because setting these blindly is not the way to approach proper header security settings in web-applications (such as ZNC Webadmin) <!-- github-comment-id:1736471802 -->
Author
Owner

Thanks for the explanation @teward

I followed @bjoe2k4 advice and managed to configure the reverse proxy for webadmin, there I easily implemented all the headers mentioned by me.

I'm using following headers and webadmin is working perfect for me:

X-Content-Type-Options: nosniff
X-Frame-Options: deny
Strict-Transport-Security: max-age=31536000
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: accelerometer=(), ambient-light-sensor(), autoplay=(), camera=(), display-capture(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), usb=()
Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none';

The issue can be closed if you think that it's not necessary to implement this on ZNC.

Thanks for the explanation @teward I followed @bjoe2k4 advice and managed to configure the reverse proxy for webadmin, there I easily implemented all the headers mentioned by me. I'm using following headers and webadmin is working perfect for me: X-Content-Type-Options: nosniff X-Frame-Options: deny Strict-Transport-Security: max-age=31536000 Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: accelerometer=(), ambient-light-sensor(), autoplay=(), camera=(), display-capture(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), usb=() Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; object-src 'none'; The issue can be closed if you think that it's not necessary to implement this on ZNC. <!-- github-comment-id:1738088931 -->
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adam/znc#1030