mirror of
https://git.valherun.com/valherun/cis-benchmarks.git
synced 2026-10-15 19:42:03 -04:00
5382 lines
228 KiB
Plaintext
5382 lines
228 KiB
Plaintext
CIS NGINX Benchmark
|
||
v3.0.0 - 12-24-2025
|
||
Terms of Use
|
||
Please see the below link for our current terms of use:
|
||
https://www.cisecurity.org/cis-securesuite/cis-securesuite-membership-terms-of-use/
|
||
|
||
|
||
For information on referencing and/or citing CIS Benchmarks in 3rd party documentation
|
||
(including using portions of Benchmark Recommendations) please contact CIS Legal
|
||
([email protected]) and request guidance on copyright usage.
|
||
|
||
NOTE: It is NEVER acceptable to host a CIS Benchmark in ANY format (PDF, etc.)
|
||
on a 3rd party (non-CIS owned) site.
|
||
|
||
|
||
|
||
|
||
Page 1
|
||
Table of Contents
|
||
Terms of Use ................................................................................................................. 1
|
||
Table of Contents .......................................................................................................... 2
|
||
Overview ........................................................................................................................ 5
|
||
Important Usage Information ................................................................................................ 5
|
||
Key Stakeholders ................................................................................................................................... 5
|
||
Apply the Correct Version of a Benchmark ........................................................................................ 6
|
||
Exceptions .............................................................................................................................................. 6
|
||
Remediation ........................................................................................................................................... 7
|
||
Summary ................................................................................................................................................. 7
|
||
Target Technology Details .................................................................................................... 8
|
||
Intended Audience................................................................................................................. 8
|
||
Consensus Guidance ............................................................................................................ 9
|
||
Typographical Conventions .................................................................................................10
|
||
Recommendation Definitions ..................................................................................... 11
|
||
Title ........................................................................................................................................11
|
||
Assessment Status...............................................................................................................11
|
||
Automated ............................................................................................................................................ 11
|
||
Manual ................................................................................................................................................... 11
|
||
Profile ....................................................................................................................................11
|
||
Description ............................................................................................................................11
|
||
Rationale Statement .............................................................................................................11
|
||
Impact Statement ..................................................................................................................12
|
||
Audit Procedure ....................................................................................................................12
|
||
Remediation Procedure........................................................................................................12
|
||
Default Value .........................................................................................................................12
|
||
References ............................................................................................................................12
|
||
CIS Critical Security Controls® (CIS Controls®) ..................................................................12
|
||
Additional Information..........................................................................................................12
|
||
Profile Definitions .................................................................................................................13
|
||
Acknowledgements ..............................................................................................................15
|
||
Recommendations ...................................................................................................... 16
|
||
1 Initial Setup ........................................................................................................................16
|
||
1.1 Installation ...................................................................................................................................... 17
|
||
1.1.1 Ensure NGINX is installed (Manual) ................................................................................ 18
|
||
1.2 Configure Software Updates ........................................................................................................ 20
|
||
1.2.1 Ensure package manager repositories are properly configured (Manual) ...................... 21
|
||
|
||
|
||
Page 2
|
||
1.2.2 Ensure the latest software package is installed (Manual) ............................................... 24
|
||
2 Basic Configuration ...........................................................................................................26
|
||
2.1 Minimize NGINX Modules .............................................................................................................. 27
|
||
2.1.1 Ensure only required dynamic modules are loaded (Manual) ......................................... 28
|
||
2.2 Account Security ........................................................................................................................... 31
|
||
2.2.1 Ensure that NGINX is run using a non-privileged, dedicated service account (Manual) . 32
|
||
2.2.2 Ensure the NGINX service account is locked (Manual)................................................... 35
|
||
2.2.3 Ensure the NGINX service account has an invalid shell (Manual) .................................. 37
|
||
2.3 Permissions and Ownership ........................................................................................................ 39
|
||
2.3.1 Ensure NGINX directories and files are owned by root (Manual) .................................... 40
|
||
2.3.2 Ensure access to NGINX directories and files is restricted (Manual) .............................. 42
|
||
2.3.3 Ensure the NGINX process ID (PID) file is secured (Manual) ......................................... 45
|
||
2.4 Network Configuration .................................................................................................................. 47
|
||
2.4.1 Ensure NGINX only listens for network connections on authorized ports (Manual) ........ 48
|
||
2.4.2 Ensure requests for unknown host names are rejected (Manual) ................................... 51
|
||
2.4.3 Ensure keepalive_timeout is 10 seconds or less, but not 0 (Manual) ............................. 54
|
||
2.4.4 Ensure send_timeout is set to 10 seconds or less, but not 0 (Manual) ........................... 56
|
||
2.5 Information Disclosure .................................................................................................................. 58
|
||
2.5.1 Ensure server_tokens directive is set to `off` (Manual) ................................................... 59
|
||
2.5.2 Ensure default error and index.html pages do not reference NGINX (Manual) .............. 61
|
||
2.5.3 Ensure hidden file serving is disabled (Manual) .............................................................. 63
|
||
2.5.4 Ensure the NGINX reverse proxy does not enable information disclosure (Manual) ...... 66
|
||
3 Logging ..............................................................................................................................68
|
||
3.1 Ensure detailed logging is enabled (Manual) ..................................................................... 69
|
||
3.2 Ensure access logging is enabled (Manual) ....................................................................... 72
|
||
3.3 Ensure error logging is enabled and set to the info logging level (Manual)........................ 75
|
||
3.4 Ensure proxies pass source IP information (Manual) ......................................................... 78
|
||
4 Encryption ..........................................................................................................................81
|
||
4.1 TLS / SSL Configuration ............................................................................................................... 82
|
||
4.1.1 Ensure HTTP is redirected to HTTPS (Manual) .............................................................. 83
|
||
4.1.2 Ensure a trusted certificate and trust chain is installed (Manual) .................................... 85
|
||
4.1.3 Ensure private key permissions are restricted (Manual) ................................................. 88
|
||
4.1.4 Ensure only modern TLS protocols are used (Manual) ................................................... 90
|
||
4.1.5 Disable weak ciphers (Manual) ........................................................................................ 93
|
||
4.1.6 Ensure awareness of TLS 1.3 new Diffie-Hellman parameters (Manual) ....................... 96
|
||
4.1.7 Ensure Online Certificate Status Protocol (OCSP) stapling is enabled (Manual) ........... 98
|
||
4.1.8 Ensure HTTP Strict Transport Security (HSTS) is enabled (Manual) ........................... 101
|
||
4.1.9 Ensure upstream server traffic is authenticated with a client certificate (Manual) ........ 104
|
||
4.1.10 Ensure the upstream traffic server certificate is trusted (Manual) ............................... 107
|
||
4.1.11 Ensure Secure Session Resumption is Enabled (Manual) .......................................... 110
|
||
4.1.12 Ensure HTTP/3.0 is used (Manual) ............................................................................. 112
|
||
5 Request Filtering and Restrictions .................................................................................115
|
||
5.1 Access Control ............................................................................................................................. 116
|
||
5.1.1 Ensure allow and deny filters limit access to specific IP addresses (Manual) .............. 117
|
||
5.1.2 Ensure only approved HTTP methods are allowed (Manual) ........................................ 119
|
||
5.2 Request Limits ............................................................................................................................. 122
|
||
5.2.1 Ensure timeout values for reading the client header and body are set correctly (Manual)
|
||
................................................................................................................................................ 123
|
||
5.2.2 Ensure the maximum request body size is set correctly (Manual) ................................ 126
|
||
5.2.3 Ensure the maximum buffer size for URIs is defined (Manual) ..................................... 129
|
||
5.2.4 Ensure the number of connections per IP address is limited (Manual) ......................... 132
|
||
5.2.5 Ensure rate limits by IP address are set (Manual) ......................................................... 135
|
||
5.3 Browser Security ......................................................................................................................... 139
|
||
|
||
|
||
Page 3
|
||
5.3.1 Ensure X-Content-Type-Options header is configured and enabled (Manual) ............. 140
|
||
5.3.2 Ensure that Content Security Policy (CSP) is enabled and configured properly (Manual)
|
||
................................................................................................................................................ 142
|
||
5.3.3 Ensure the Referrer Policy is enabled and configured properly (Manual) ..................... 145
|
||
6 Mandatory Access Control..............................................................................................147
|
||
Appendix: Summary Table ....................................................................................... 148
|
||
Appendix: Change History ....................................................................................... 152
|
||
|
||
|
||
|
||
|
||
Page 4
|
||
Overview
|
||
All CIS Benchmarks™ (Benchmarks) focus on technical configuration settings used to
|
||
maintain and/or increase the security of the addressed technology, and they should be
|
||
used in conjunction with other essential cyber hygiene tasks like:
|
||
• Monitoring the base operating system and applications for vulnerabilities and
|
||
quickly updating with the latest security patches.
|
||
• End-point protection (Antivirus software, Endpoint Detection and Response
|
||
(EDR), etc.).
|
||
• Logging and monitoring user and system activity.
|
||
|
||
In the end, the Benchmarks are designed to be a key component of a comprehensive
|
||
cybersecurity program.
|
||
|
||
Important Usage Information
|
||
All Benchmarks are available free for non-commercial use from the CIS Website. They
|
||
can be used to manually assess and remediate systems and applications. In lieu of
|
||
manual assessment and remediation, there are several tools available to assist with
|
||
assessment:
|
||
• CIS Configuration Assessment Tool (CIS-CAT® Pro Assessor)
|
||
• CIS Benchmarks™ Certified 3rd Party Tooling
|
||
|
||
These tools make the hardening process much more scalable for large numbers of
|
||
systems and applications.
|
||
|
||
NOTE: Some tooling focuses only on the Benchmark Recommendations that can
|
||
be fully automated (skipping ones marked Manual). It is important that ALL
|
||
Recommendations (Automated and Manual) be addressed since all are
|
||
important for properly securing systems and are typically in scope for
|
||
audits.
|
||
|
||
Key Stakeholders
|
||
Cybersecurity is a collaborative effort, and cross functional cooperation is imperative
|
||
within an organization to discuss, test, and deploy Benchmarks in an effective and
|
||
efficient way. The Benchmarks are developed to be best practice configuration
|
||
guidelines applicable to a wide range of use cases. In some organizations, exceptions
|
||
to specific Recommendations will be needed, and this team should work to prioritize the
|
||
problematic Recommendations based on several factors like risk, time, cost, and labor.
|
||
These exceptions should be properly categorized and documented for auditing
|
||
purposes.
|
||
|
||
|
||
|
||
Page 5
|
||
Apply the Correct Version of a Benchmark
|
||
Benchmarks are developed and tested for a specific set of products and versions and
|
||
applying an incorrect Benchmark to a system can cause the resulting pass/fail score to
|
||
be incorrect. This is due to the assessment of settings that do not apply to the target
|
||
systems. To assure the correct Benchmark is being assessed:
|
||
• Deploy the Benchmark applicable to the way settings are managed in the
|
||
environment: An example of this is the Microsoft Windows family of
|
||
Benchmarks, which have separate Benchmarks for Group Policy, Intune, and
|
||
Stand-alone systems based upon how system management is deployed.
|
||
Applying the wrong Benchmark in this case will give invalid results.
|
||
|
||
• Use the most recent version of a Benchmark: This is true for all Benchmarks,
|
||
but especially true for cloud technologies. Cloud technologies change frequently
|
||
and using an older version of a Benchmark may have invalid methods for
|
||
auditing and remediation.
|
||
|
||
Exceptions
|
||
The guidance items in the Benchmarks are called recommendations and not
|
||
requirements, and exceptions to some of them are expected and acceptable. The
|
||
Benchmarks strive to be a secure baseline, or starting point, for a specific technology,
|
||
with known issues identified during Benchmark development are documented in the
|
||
Impact section of each Recommendation. In addition, organizational, system specific
|
||
requirements, or local site policy may require changes as well, or an exception to a
|
||
Recommendation or group of Recommendations (e.g. A Benchmark could Recommend
|
||
that a Web server not be installed on the system, but if a system's primary purpose is to
|
||
function as a Webserver, there should be a documented exception to this
|
||
Recommendation for that specific server).
|
||
|
||
In the end, exceptions to some Benchmark Recommendations are common and
|
||
acceptable, and should be handled as follows:
|
||
• The reasons for the exception should be reviewed cross-functionally and be well
|
||
documented for audit purposes.
|
||
• A plan should be developed for mitigating, or eliminating, the exception in the
|
||
future, if applicable.
|
||
• If the organization decides to accept the risk of this exception (not work toward
|
||
mitigation or elimination), this should be documented for audit purposes.
|
||
|
||
It is the responsibility of the organization to determine their overall security policy, and
|
||
which settings are applicable to their unique needs based on the overall risk profile for
|
||
the organization.
|
||
|
||
|
||
|
||
|
||
Page 6
|
||
Remediation
|
||
CIS has developed Build Kits for many technologies to assist in the automation of
|
||
hardening systems. Build Kits are designed to correspond to Benchmark's
|
||
“Remediation” section, which provides the manual remediation steps necessary to make
|
||
that Recommendation compliant to the Benchmark.
|
||
|
||
|
||
When remediating systems (changing configuration settings on
|
||
deployed systems as per the Benchmark's Recommendations),
|
||
please approach this with caution and test thoroughly.
|
||
|
||
The following is a reasonable remediation approach to follow:
|
||
• CIS Build Kits, or internally developed remediation methods should never be
|
||
applied to production systems without proper testing.
|
||
• Proper testing consists of the following:
|
||
o Understand the configuration (including installed applications) of the targeted
|
||
systems. Various parts of the organization may need different configurations
|
||
(e.g., software developers vs standard office workers).
|
||
o Read the Impact section of the given Recommendation to help determine if
|
||
there might be an issue with the targeted systems.
|
||
o Test the configuration changes with representative lab system(s). If issues
|
||
arise during testing, they can be resolved prior to deploying to any production
|
||
systems.
|
||
o When testing is complete, initially deploy to a small sub-set of production
|
||
systems and monitor closely for issues. If there are issues, they can be
|
||
resolved prior to deploying more broadly.
|
||
o When the initial deployment above is completes successfully, iteratively
|
||
deploy to additional systems and monitor closely for issues. Repeat this
|
||
process until the full deployment is complete.
|
||
Summary
|
||
Using the Benchmarks Certified tools, working as a team with key stakeholders, being
|
||
selective with exceptions, and being careful with remediation deployment, it is possible
|
||
to harden large numbers of deployed systems in a cost effective, efficient, and safe
|
||
manner.
|
||
|
||
NOTE: As previously stated, the PDF versions of the CIS Benchmarks™ are
|
||
available for free, non-commercial use on the CIS Website. All other formats
|
||
of the CIS Benchmarks™ (MS Word, Excel, and Build Kits) are available for
|
||
CIS SecureSuite® members.
|
||
|
||
CIS-CAT® Pro is also available to CIS SecureSuite® members.
|
||
|
||
|
||
|
||
Page 7
|
||
Target Technology Details
|
||
This document, CIS NGINX Benchmark, provides prescriptive guidance for establishing
|
||
a secure configuration posture for NGINX version 1.28.x running on a systemd based
|
||
Linux system
|
||
This guide was tested against NGINX version 1.28.0 with default installation location,
|
||
using the packages installed using the Red Hat Enterprise Linux 9 package managers.
|
||
This Benchmark was written using commands for and tested on, Red Hat Enterprise
|
||
Linux release 9.0. For other versions of Linux, please substitute the distribution-specific
|
||
commands for the equivalent commands on the Linux distribution you are using.
|
||
To obtain the latest version of this guide, please visit https://benchmarks.cisecurity.org.
|
||
If you have questions or comments or have identified ways to improve this guide, please
|
||
write to us at [email protected].
|
||
|
||
|
||
|
||
Intended Audience
|
||
This document is intended for system and application administrators, security
|
||
specialists, auditors, and help desk and platform deployment personnel who plan to
|
||
develop, deploy, assess, or secure solutions that incorporate NGINX.
|
||
|
||
|
||
|
||
|
||
Page 8
|
||
Consensus Guidance
|
||
This CIS Benchmark™ was created using a consensus review process comprised of a
|
||
global community of subject matter experts. The process combines real world
|
||
experience with data-based information to create technology specific guidance to assist
|
||
users to secure their environments. Consensus participants provide perspective from a
|
||
diverse set of backgrounds including consulting, software development, audit and
|
||
compliance, security research, operations, government, and legal.
|
||
Each CIS Benchmark undergoes two phases of consensus review. The first phase
|
||
occurs during initial Benchmark development. During this phase, subject matter experts
|
||
convene to discuss, create, and test working drafts of the Benchmark. This discussion
|
||
occurs until consensus has been reached on Benchmark recommendations. The
|
||
second phase begins after the Benchmark has been published. During this phase, all
|
||
feedback provided by the Internet community is reviewed by the consensus team for
|
||
incorporation in the Benchmark. If you are interested in participating in the consensus
|
||
process, please visit https://workbench.cisecurity.org/.
|
||
|
||
|
||
|
||
|
||
Page 9
|
||
Typographical Conventions
|
||
The following typographical conventions are used throughout this guide:
|
||
|
||
Convention Meaning
|
||
|
||
Used for blocks of code, command, and
|
||
Stylized Monospace font script examples. Text should be interpreted
|
||
exactly as presented.
|
||
|
||
Used for inline code, commands, UI/Menu
|
||
Monospace font selections or examples. Text should be
|
||
interpreted exactly as presented.
|
||
|
||
Text set in angle brackets denote a variable
|
||
<Monospace font in brackets>
|
||
requiring substitution for a real value.
|
||
|
||
Used to reference other relevant settings,
|
||
CIS Benchmarks and/or Benchmark
|
||
Italic font
|
||
Communities. Also, used to denote the title
|
||
of a book, article, or other publication.
|
||
|
||
Additional information or caveats things like
|
||
Notes, Warnings, or Cautions (usually just
|
||
Bold font
|
||
the word itself and the rest of the text
|
||
normal).
|
||
|
||
|
||
|
||
|
||
Page 10
|
||
Recommendation Definitions
|
||
The following defines the various components included in a CIS recommendation as
|
||
applicable. If any of the components are not applicable it will be noted, or the
|
||
component will not be included in the recommendation.
|
||
|
||
Title
|
||
Concise description for the recommendation's intended configuration.
|
||
|
||
Assessment Status
|
||
An assessment status is included for every recommendation. The assessment status
|
||
indicates whether the given recommendation can be automated or requires manual
|
||
steps to implement. Both statuses are equally important and are determined and
|
||
supported as defined below:
|
||
|
||
Automated
|
||
Represents recommendations for which assessment of a technical control can be fully
|
||
automated and validated to a pass/fail state. Recommendations will include the
|
||
necessary information to implement automation.
|
||
|
||
Manual
|
||
Represents recommendations for which assessment of a technical control cannot be
|
||
fully automated and requires all or some manual steps to validate that the configured
|
||
state is set as expected. The expected state can vary depending on the environment.
|
||
|
||
Profile
|
||
A collection of recommendations for securing a technology or a supporting platform.
|
||
Most benchmarks include at least a Level 1 and Level 2 Profile. Level 2 extends Level 1
|
||
recommendations and is not a standalone profile. The Profile Definitions section in the
|
||
benchmark provides the definitions as they pertain to the recommendations included for
|
||
the technology.
|
||
|
||
Description
|
||
Detailed information pertaining to the setting with which the recommendation is
|
||
concerned. In some cases, the description will include the recommended value.
|
||
|
||
Rationale Statement
|
||
Detailed reasoning for the recommendation to provide the user a clear and concise
|
||
understanding on the importance of the recommendation.
|
||
|
||
|
||
|
||
Page 11
|
||
Impact Statement
|
||
Any security, functionality, or operational consequences that can result from following
|
||
the recommendation.
|
||
|
||
Audit Procedure
|
||
Systematic instructions for determining if the target system complies with the
|
||
recommendation.
|
||
|
||
Remediation Procedure
|
||
Systematic instructions for applying recommendations to the target system to bring it
|
||
into compliance according to the recommendation.
|
||
|
||
Default Value
|
||
Default value for the given setting in this recommendation, if known. If not known, either
|
||
not configured or not defined will be applied.
|
||
|
||
References
|
||
Additional documentation relative to the recommendation.
|
||
|
||
CIS Critical Security Controls® (CIS Controls®)
|
||
The mapping between a recommendation and the CIS Controls is organized by CIS
|
||
Controls version, Safeguard, and Implementation Group (IG). The Benchmark in its
|
||
entirety addresses the CIS Controls safeguards of (v7) “5.1 - Establish Secure
|
||
Configurations” and (v8) '4.1 - Establish and Maintain a Secure Configuration Process”
|
||
so individual recommendations will not be mapped to these safeguards.
|
||
|
||
Additional Information
|
||
Supplementary information that does not correspond to any other field but may be
|
||
useful to the user.
|
||
|
||
|
||
|
||
|
||
Page 12
|
||
Profile Definitions
|
||
The following configuration profiles are defined by this Benchmark:
|
||
|
||
• Level 1 - Webserver
|
||
|
||
Items in this profile intend to:
|
||
|
||
o be practical and prudent;
|
||
o provide a clear security benefit; and
|
||
o not inhibit the utility of the technology beyond acceptable means.
|
||
|
||
• Level 1 - Proxy
|
||
|
||
Items in this profile intend to:
|
||
|
||
o be practical and prudent;
|
||
o provide a clear security benefit; and
|
||
o not inhibit the utility of the technology beyond acceptable means.
|
||
|
||
• Level 1 - Loadbalancer
|
||
|
||
Items in this profile intend to:
|
||
|
||
o be practical and prudent;
|
||
o provide a clear security benefit; and
|
||
o not inhibit the utility of the technology beyond acceptable means.
|
||
|
||
• Level 2 - Webserver
|
||
|
||
This profile extends the "Level 1" profile. Items in this profile exhibit one or more
|
||
of the following characteristics:
|
||
|
||
o are intended for environments or use cases where security is paramount
|
||
o acts as defense in depth measure
|
||
o may negatively inhibit the utility or performance of the technology
|
||
|
||
• Level 2 - Proxy
|
||
|
||
This profile extends the "Level 1" profile. Items in this profile exhibit one or more
|
||
of the following characteristics:
|
||
|
||
o are intended for environments or use cases where security is paramount
|
||
o acts as defense in depth measure
|
||
|
||
Page 13
|
||
o may negatively inhibit the utility or performance of the technology
|
||
|
||
• Level 2 - Loadbalancer
|
||
|
||
This profile extends the "Level 1" profile. Items in this profile exhibit one or more
|
||
of the following characteristics:
|
||
|
||
o are intended for environments or use cases where security is paramount
|
||
o acts as defense in depth measure
|
||
o may negatively inhibit the utility or performance of the technology
|
||
|
||
|
||
|
||
|
||
Page 14
|
||
Acknowledgements
|
||
This Benchmark exemplifies the great things a community of users, vendors, and
|
||
subject matter experts can accomplish through consensus collaboration. The CIS
|
||
community thanks the entire consensus team with special recognition to the following
|
||
individuals who contributed greatly to the creation of this guide:
|
||
|
||
Contributor
|
||
Alexander Sennhauser
|
||
James Scott
|
||
Greg MacLean
|
||
|
||
Editor
|
||
Eric Pinnell
|
||
Krishna Rayavaram
|
||
Stephan Wenderlich
|
||
|
||
|
||
|
||
|
||
Page 15
|
||
Recommendations
|
||
1 Initial Setup
|
||
This section contains recommendations for the installation and maintenance of an
|
||
NGINX server.
|
||
|
||
|
||
|
||
|
||
Page 16
|
||
1.1 Installation
|
||
|
||
|
||
|
||
|
||
Page 17
|
||
1.1.1 Ensure NGINX is installed (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
An NGINX installation must be present on the system. To ensure support for modern
|
||
security standards (such as TLS 1.3 and HTTP/3) and to mitigate known vulnerabilities,
|
||
the installed version must be 1.28.0 or later and compiled with the necessary modules.
|
||
|
||
Rationale:
|
||
NGINX must be installed and operational to serve as the target for this benchmark's
|
||
security controls. Enforcing a minimum version and feature set ensures the platform is
|
||
capable of supporting the required security configurations.
|
||
Impact:
|
||
Upgrading NGINX to a newer version may introduce configuration syntax changes or
|
||
deprecated directives. Administrators should test the configuration syntax nginx -t
|
||
before restarting the service.
|
||
Audit:
|
||
1. Verify Installation and Version: Run the following command to display the installed
|
||
NGINX version:
|
||
# Note: It's an uppercase V
|
||
nginx -V
|
||
2. Evaluation:
|
||
|
||
• Verify Output: The command must return an installed version (e.g., nginx
|
||
version: nginx/1.28.0). If the command is not found, NGINX is not installed.
|
||
• Check Version: Ensure the version number is 1.28.0 or higher.
|
||
|
||
Remediation:
|
||
Install or upgrade NGINX to version 1.28.0 or later.
|
||
Note: Official packages from nginx.org (see recommendation 1.2.1) typically include
|
||
these modules by default. Custom builds must explicitly enable them.
|
||
Default Value:
|
||
NGINX is not installed by default.
|
||
|
||
Page 18
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/install.html
|
||
2. https://nginx.org/en/linux_packages.html
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
2.2 Ensure Authorized Software is Currently Supported
|
||
Ensure that only currently supported software is designated as authorized in the
|
||
software inventory for enterprise assets. If software is unsupported, yet necessary
|
||
v8 for the fulfillment of the enterprise’s mission, document an exception detailing ● ● ●
|
||
mitigating controls and residual risk acceptance. For any unsupported software
|
||
without an exception documentation, designate as unauthorized. Review the
|
||
software list to verify software support at least monthly, or more frequently.
|
||
|
||
|
||
2.2 Ensure Software is Supported by Vendor
|
||
Ensure that only software applications or operating systems currently supported
|
||
v7 by the software's vendor are added to the organization's authorized software ● ● ●
|
||
inventory. Unsupported software should be tagged as unsupported in the inventory
|
||
system.
|
||
|
||
|
||
|
||
|
||
Page 19
|
||
1.2 Configure Software Updates
|
||
|
||
|
||
|
||
|
||
Page 20
|
||
1.2.1 Ensure package manager repositories are properly
|
||
configured (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
Package repositories must be trustworthy, properly configured, and maintained to
|
||
ensure the system receives timely security patches, bug fixes, and support for modern
|
||
protocols. While Operating System (OS) vendors provide NGINX packages, these
|
||
versions are often frozen at older release points ("stable" but stale). Access to critical
|
||
modern features like HTTP/3 (QUIC) and the latest TLS updates typically requires using
|
||
the official repositories maintained by NGINX/F5.
|
||
Rationale:
|
||
If a system's package manager repositories are misconfigured or outdated, critical
|
||
security patches may not be applied in a timely manner. Furthermore, relying solely on
|
||
default OS repositories often restricts the web server to legacy versions that lack
|
||
support for modern security standards (e.g., HTTP/3). Using the official nginx.org
|
||
repositories ensures access to the latest stable and mainline versions directly from the
|
||
source, reducing the risk of running obsolete software. Conversely, adding untrusted
|
||
third-party repositories can introduce compromised software or dependency conflicts.
|
||
Impact:
|
||
Switching from OS-provided packages to upstream (nginx.org) packages alters the
|
||
update lifecycle. Administrators become responsible for tracking upstream changes
|
||
rather than relying on the OS vendor's backporting policy. However, this is often
|
||
necessary to meet modern security and performance requirements.
|
||
|
||
|
||
|
||
|
||
Page 21
|
||
Audit:
|
||
To verify that package manager repositories are configured correctly and point to a
|
||
trusted source (either the OS vendor or official NGINX), run the following commands:
|
||
Red Hat / Rocky-Linux:
|
||
dnf repolist -v | grep -i nginx
|
||
Debian / Ubuntu:
|
||
apt-cache policy nginx
|
||
Evaluation:
|
||
|
||
• Verify that the repository URL points to a trusted domain (e.g., nginx.org, rhel...,
|
||
ubuntu...).
|
||
• Ensure that the repository is enabled.
|
||
• Check that no unknown or untrusted third-party repositories are configured for
|
||
NGINX.
|
||
|
||
Remediation:
|
||
Configure your package manager to use a trusted repository that meets your version
|
||
requirements.
|
||
To enable the official NGINX repository (Recommended for HTTP/3 support): Follow the
|
||
instructions at nginx.org/en/linux_packages.html for your specific distribution. This
|
||
typically involves adding the NGINX signing key and creating a repository configuration
|
||
file.
|
||
Default Value:
|
||
By default, systems are configured with the OS vendor's repositories, which typically
|
||
contain older versions of NGINX and not always all needed features.
|
||
References:
|
||
|
||
1. https://nginx.org/en/linux_packages.html
|
||
|
||
Additional Information:
|
||
Package update and installation commands and repository structures vary by Linux
|
||
distribution. The examples provided are based on Red Hat Enterprise Linux 9. Please
|
||
substitute with the appropriate commands for your environment. Note that HTTP/3
|
||
(QUIC) support was introduced in NGINX 1.25.0 (Mainline) and is not available in many
|
||
OS-bundled versions.
|
||
|
||
|
||
|
||
|
||
Page 22
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
7.3 Perform Automated Operating System Patch
|
||
v8 Management ● ● ●
|
||
Perform operating system updates on enterprise assets through automated
|
||
patch management on a monthly, or more frequent, basis.
|
||
|
||
|
||
7.4 Perform Automated Application Patch Management
|
||
v8 Perform application updates on enterprise assets through automated patch ● ● ●
|
||
management on a monthly, or more frequent, basis.
|
||
|
||
|
||
3.4 Deploy Automated Operating System Patch
|
||
Management Tools
|
||
v7 Deploy automated software update tools in order to ensure that the operating ● ● ●
|
||
systems are running the most recent security updates provided by the software
|
||
vendor.
|
||
|
||
|
||
3.5 Deploy Automated Software Patch Management
|
||
Tools
|
||
v7 Deploy automated software update tools in order to ensure that third-party ● ● ●
|
||
software on all systems is running the most recent security updates provided by
|
||
the software vendor.
|
||
|
||
|
||
|
||
|
||
Page 23
|
||
1.2.2 Ensure the latest software package is installed (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
As new security vulnerabilities are discovered, the corresponding fixes are implemented
|
||
by your NGINX software package provider. Installing the latest software version ensures
|
||
these fixes are available on your system.
|
||
Rationale:
|
||
Up-to-date software provides the best possible protection against exploitation of security
|
||
vulnerabilities, such as the execution of malicious code.
|
||
Impact:
|
||
Updating the NGINX package requires a service reload or restart to apply the changes.
|
||
This may cause a brief interruption or configuration errors if the new version deprecates
|
||
existing syntax.
|
||
Audit:
|
||
To verify your NGINX package is up to date, run the following command (example):
|
||
Redhat:
|
||
dnf info nginx
|
||
|
||
Remediation:
|
||
To install the latest NGINX package, run the following command (example):
|
||
Redhat:
|
||
dnf update nginx -y
|
||
|
||
References:
|
||
|
||
1. https://nginx.org/en/linux_packages.html
|
||
|
||
Additional Information:
|
||
Package update and installation commands are based on Red Hat Enterprise Linux 9. If
|
||
using a different Linux distribution, please substitute with the appropriate command(s)
|
||
and consider your organization patch management policy.
|
||
|
||
Page 24
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
7.3 Perform Automated Operating System Patch
|
||
v8 Management ● ● ●
|
||
Perform operating system updates on enterprise assets through automated
|
||
patch management on a monthly, or more frequent, basis.
|
||
|
||
|
||
7.4 Perform Automated Application Patch Management
|
||
v8 Perform application updates on enterprise assets through automated patch ● ● ●
|
||
management on a monthly, or more frequent, basis.
|
||
|
||
|
||
3.4 Deploy Automated Operating System Patch
|
||
Management Tools
|
||
v7 Deploy automated software update tools in order to ensure that the operating ● ● ●
|
||
systems are running the most recent security updates provided by the software
|
||
vendor.
|
||
|
||
|
||
3.5 Deploy Automated Software Patch Management
|
||
Tools
|
||
v7 Deploy automated software update tools in order to ensure that third-party ● ● ●
|
||
software on all systems is running the most recent security updates provided by
|
||
the software vendor.
|
||
|
||
|
||
|
||
|
||
Page 25
|
||
2 Basic Configuration
|
||
|
||
|
||
|
||
|
||
Page 26
|
||
2.1 Minimize NGINX Modules
|
||
|
||
|
||
|
||
|
||
Page 27
|
||
2.1.1 Ensure only required dynamic modules are loaded (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
NGINX functionality is provided by modules. These modules are either compiled
|
||
statically into the NGINX binary or loaded dynamically at runtime via the load_module
|
||
directive.
|
||
|
||
• Static Modules: These are fixed at compile time. When using official pre-built
|
||
packages (e.g., from nginx.org or OS vendors), a standard set of modules is
|
||
included and cannot be removed without recompiling NGINX.
|
||
• Dynamic Modules: These are separate .so files that can be loaded on demand.
|
||
To reduce the attack surface and complexity, only strictly required dynamic
|
||
modules should be loaded. Additionally, administrators should be aware of the
|
||
active static modules to avoid configuring unused features unintentionally.
|
||
|
||
Rationale:
|
||
Minimizing the loaded code reduces the potential attack surface. While static modules in
|
||
pre-built packages cannot be removed, ensuring that no unnecessary dynamic
|
||
modules are loaded prevents the execution of unneeded code. Furthermore,
|
||
understanding which static modules are present helps administrators avoid enabling
|
||
risky features (like autoindex or stub_status) in the configuration if they are not
|
||
needed.
|
||
Impact:
|
||
Removing a required dynamic module or misinterpreting the availability of a static
|
||
module can cause the NGINX service to fail on restart or break specific application
|
||
features.
|
||
|
||
|
||
|
||
|
||
Page 28
|
||
Audit:
|
||
1. Audit Dynamic Modules (Actionable):
|
||
Run the following command to check for actively loaded dynamic modules:
|
||
nginx -T 2>/dev/null | grep "load_module"
|
||
Evaluation:
|
||
|
||
• If the output is empty, no dynamic modules are loaded (PASS).
|
||
• If output exists (e.g., load_module modules/ngx_http_geoip_module.so;),
|
||
verify that each listed module is required for the application's business logic.
|
||
|
||
2. Audit Static Modules (Informational):
|
||
Run the following command to list all modules compiled into the binary:
|
||
nginx -V 2>&1 | grep -oEi '\-\-(with|without)-[^ ]*'
|
||
Evaluation:
|
||
Review the --with-... flags to understand the server's capabilities. Ensure that risky
|
||
modules present in the build (e.g., http_stub_status_module) are not enabled in any
|
||
server or location block unless authorized.
|
||
|
||
Remediation:
|
||
For Dynamic Modules:
|
||
Open the main configuration file (/etc/nginx/nginx.conf) or the relevant include file
|
||
(e.g., in /etc/nginx/modules-enabled/). Comment out or remove the load_module
|
||
directive for any module that is not strictly necessary.
|
||
For Static Modules:
|
||
Since static modules cannot be removed from pre-built packages, ensure their
|
||
directives are not used in your configuration. If a specific static module poses a critical
|
||
risk to your environment, you must switch to a custom build or a different package flavor
|
||
that excludes it.
|
||
Default Value:
|
||
Official pre-built packages (like nginx-stable or nginx-mainline) are "feature-rich"
|
||
builds containing most standard modules statically. This is a trade-off for ease of
|
||
maintenance. Security hardening for these packages relies on configuration discipline
|
||
(not enabling unused modules) rather than binary minimization.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/
|
||
|
||
|
||
|
||
Page 29
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
2.6 Allowlist Authorized Libraries
|
||
Use technical controls to ensure that only authorized software libraries, such
|
||
v8 as specific .dll, .ocx, .so, etc., files, are allowed to load into a system process. ● ●
|
||
Block unauthorized libraries from loading into a system process. Reassess bi-
|
||
annually, or more frequently.
|
||
|
||
|
||
2.8 Implement Application Whitelisting of Libraries
|
||
v7 The organization's application whitelisting software must ensure that only
|
||
authorized software libraries (such as *.dll, *.ocx, *.so, etc) are allowed to load into
|
||
●
|
||
a system process.
|
||
|
||
|
||
|
||
|
||
Page 30
|
||
2.2 Account Security
|
||
|
||
|
||
|
||
|
||
Page 31
|
||
2.2.1 Ensure that NGINX is run using a non-privileged, dedicated
|
||
service account (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The NGINX master process typically runs as root to bind to privileged ports and
|
||
manage resources, but it spawns worker processes to handle the actual client traffic.
|
||
The user directive in the main configuration designates the operating system account
|
||
under which these worker processes run.
|
||
Running worker processes under a non-privileged, dedicated service account limits the
|
||
damage an attacker can cause in the event the NGINX process is compromised. This
|
||
account should be exclusively dedicated to NGINX, have no login capabilities, and
|
||
possess no elevated system privileges.
|
||
Rationale:
|
||
If an attacker successfully exploits a vulnerability in a worker process (e.g., via a buffer
|
||
overflow or Remote Code Execution), they inherit the permissions of the user account
|
||
running that process. Using a privileged account like root significantly increases the
|
||
risk of lateral movement. A dedicated, locked-down service account ensures that an
|
||
attacker cannot access other services, modify sensitive system files, or easily escalate
|
||
privileges, effectively reducing the impact of the compromise.
|
||
Impact:
|
||
Changing the NGINX user requires that ownership and permissions for all runtime
|
||
directories (logs, caches, PID files) are updated to match the new account. Incorrect
|
||
permissions will prevent NGINX from starting or writing necessary logs. Additionally, the
|
||
dedicated user must remain strictly unprivileged; adding it to groups like sudo or wheel
|
||
would negate the security benefit.
|
||
|
||
|
||
|
||
|
||
Page 32
|
||
Audit:
|
||
1. Identify Configured User:
|
||
Inspect the running configuration to find the user directive:
|
||
nginx -T 2>/dev/null | grep -i "^user"
|
||
Evaluation: Verify that a specific user is defined (e.g., user nginx; or user www-
|
||
data;). If missing, NGINX might run as nobody or the user used at compile time.
|
||
2. Verify User Privileges:
|
||
Check the UID and group membership of the identified user (e.g., nginx):
|
||
id nginx
|
||
Evaluation:
|
||
|
||
• Ensure uid is not 0 (root).
|
||
• Ensure the user is not a member of privileged groups (like root, wheel, sudo).
|
||
|
||
3. Check Sudo Access:
|
||
Ensure the user cannot execute commands via sudo:
|
||
sudo -l -U nginx
|
||
Evaluation: Output should indicate "User nginx is not allowed to run sudo".
|
||
|
||
Remediation:
|
||
1. Create/Harden User (if missing):
|
||
If no user exists, create a system user with a no login shell:
|
||
useradd -r -d /var/cache/nginx -s /sbin/nologin nginx
|
||
2. Configure NGINX:
|
||
Set the user directive in the main context of nginx.conf:
|
||
user nginx;
|
||
3. Lock User Account:
|
||
Ensure the account cannot be used for login:
|
||
usermod -s /sbin/nologin nginx
|
||
usermod -L nginx
|
||
|
||
Default Value:
|
||
Official packages usually create and configure a dedicated user (e.g., nginx or www-
|
||
data). If compiled from source without arguments, the user defaults to nobody.
|
||
|
||
|
||
|
||
Page 33
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/ngx_core_module.html#user
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
5.4 Restrict Administrator Privileges to Dedicated
|
||
Administrator Accounts
|
||
v8 Restrict administrator privileges to dedicated administrator accounts on
|
||
enterprise assets. Conduct general computing activities, such as internet
|
||
● ● ●
|
||
browsing, email, and productivity suite use, from the user’s primary, non-privileged
|
||
account.
|
||
|
||
|
||
4.3 Ensure the Use of Dedicated Administrative Accounts
|
||
v7 Ensure that all users with administrative account access use a dedicated or
|
||
secondary account for elevated activities. This account should only be used for
|
||
● ● ●
|
||
administrative activities and not internet browsing, email, or similar activities.
|
||
|
||
|
||
|
||
|
||
Page 34
|
||
2.2.2 Ensure the NGINX service account is locked (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
The NGINX service account must not have a usable password and should be explicitly
|
||
locked in the system's shadow file to prevent direct login or password-based privilege
|
||
escalation.
|
||
Rationale:
|
||
As a defense-in-depth measure, the NGINX service account should be explicitly locked.
|
||
This prevents password-based logins and blocks adversaries from using the account for
|
||
lateral movement, even if they manage to change the account's shell configuration.
|
||
In a properly hardened environment, there is no operational need for any user to log in
|
||
as nginx. Administrative tasks requiring the NGINX identity should be performed using
|
||
sudo (e.g., sudo -u nginx), which utilizes the administrator's credentials rather than
|
||
the service account's password.
|
||
Impact:
|
||
Locking the service account has minimal operational impact. The account is not
|
||
intended for human interaction, and all administrative tasks requiring the NGINX user
|
||
context should already be performed using sudo -u nginx rather than password-
|
||
based authentication.
|
||
|
||
|
||
|
||
|
||
Page 35
|
||
Audit:
|
||
1. Identify the User:
|
||
nginx -T 2>/dev/null | grep -i "^user"
|
||
(Note the user, e.g., nginx)
|
||
2. Check Lock Status:
|
||
Run the following command for the identified user:
|
||
passwd -S nginx
|
||
Evaluation:
|
||
Verify that the output indicates a locked status:
|
||
|
||
• RHEL: Shows LK or Password locked.
|
||
• Debian/Ubuntu: Shows L.
|
||
|
||
Remediation:
|
||
Lock the account using the passwd command:
|
||
passwd -l nginx
|
||
(Replace nginx with the actual service user found in step 1)
|
||
|
||
Default Value:
|
||
System users created by official packages are typically created without a password
|
||
(locked by default).
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.3 Configure Data Access Control Lists
|
||
v8 Configure data access control lists based on a user’s need to know. Apply data
|
||
access control lists, also known as access permissions, to local and remote file
|
||
● ● ●
|
||
systems, databases, and applications.
|
||
|
||
|
||
14.6 Protect Information through Access Control Lists
|
||
Protect all information stored on systems with file system, network share,
|
||
v7 claims, application, or database specific access control lists. These controls will
|
||
enforce the principle that only authorized individuals should have access to the
|
||
● ● ●
|
||
information based on their need to access the information as a part of their
|
||
responsibilities.
|
||
|
||
|
||
|
||
|
||
Page 36
|
||
2.2.3 Ensure the NGINX service account has an invalid shell
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The NGINX service account must be configured with an invalid login shell to prevent
|
||
interactive access.
|
||
Rationale:
|
||
The NGINX service account is strictly for running daemon processes. Assigning it a
|
||
valid login shell (like /bin/bash) unnecessarily expands the attack surface. If an
|
||
attacker compromises the account credentials (or adds an SSH key), a valid shell
|
||
facilitates interactive system access. Setting the shell to /sbin/nologin or /bin/false
|
||
ensures that even with valid credentials, the system immediately rejects a login attempt.
|
||
Impact:
|
||
None. Service accounts do not require interactive login capabilities for normal operation.
|
||
Audit:
|
||
1. Identify the User:
|
||
nginx -T 2>/dev/null | grep -i "^user"
|
||
(Note the user, e.g., nginx)
|
||
2. Verify Shell:
|
||
Run the following command to inspect the configured shell for the identified user:
|
||
getent passwd nginx
|
||
(Replace nginx with the actual user found in step 1)
|
||
Evaluation:
|
||
Examine the last field of the output (the shell).
|
||
|
||
• PASS: The shell is set to /sbin/nologin, /bin/nologin, or /bin/false.
|
||
• FAIL: The shell is set to /bin/bash, /bin/sh, or any other interactive shell listed
|
||
in /etc/shells.
|
||
|
||
|
||
|
||
Page 37
|
||
Example Output (PASS):
|
||
nginx:x:999:988:nginx user:/nonexistent:/usr/sbin/nologin
|
||
|
||
Remediation:
|
||
Change the login shell for the identified user to /sbin/nologin:
|
||
usermod -s /sbin/nologin nginx
|
||
(Replace nginx with the actual user)
|
||
|
||
Default Value:
|
||
Official packages typically configure the user with /sbin/nologin or /bin/false by
|
||
default.
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
4.1 Establish and Maintain a Secure Configuration Process
|
||
Establish and maintain a secure configuration process for enterprise assets
|
||
v8 (end-user devices, including portable and mobile, non-computing/IoT devices, and
|
||
servers) and software (operating systems and applications). Review and update
|
||
● ● ●
|
||
documentation annually, or when significant enterprise changes occur that could
|
||
impact this Safeguard.
|
||
|
||
|
||
5.1 Establish Secure Configurations
|
||
v7 Maintain documented, standard security configuration standards for all ● ● ●
|
||
authorized operating systems and software.
|
||
|
||
|
||
|
||
|
||
Page 38
|
||
2.3 Permissions and Ownership
|
||
|
||
|
||
|
||
|
||
Page 39
|
||
2.3.1 Ensure NGINX directories and files are owned by root
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The NGINX configuration directory and all contained files must be owned by the root
|
||
user and group to prevent unauthorized modification.
|
||
Rationale:
|
||
The NGINX configuration controls the security posture of the web server. If a non-
|
||
privileged user (including the nginx worker user) can modify these files, they can
|
||
trivially escalate privileges (e.g., by loading a malicious module or changing the user
|
||
directive to root). Ensuring that only root owns these files guarantees that
|
||
configuration changes require administrative privileges.
|
||
Impact:
|
||
None. The NGINX master process runs as root and can read these files. The worker
|
||
processes (running as nginx) do not need write access to the configuration.
|
||
|
||
Audit:
|
||
1. Identify Configuration Directory:
|
||
Run nginx -V and look for --conf-path to find the main configuration file location
|
||
(e.g., /etc/nginx/nginx.conf). The directory containing this file is the target.
|
||
2. Verify Ownership:
|
||
Run the following command to audit the ownership of the configuration directory and its
|
||
contents:
|
||
find /etc/nginx -name "*" \( -not -user root -o -not -group root \) -exec ls
|
||
-ld {} \;
|
||
(Replace /etc/nginx with the actual configuration path in case it is different)
|
||
Evaluation:
|
||
|
||
• PASS: The command produces no output. All files are owned by root:root.
|
||
• FAIL: The command lists files owned by other users (e.g., nginx or a developer
|
||
account).
|
||
|
||
|
||
Page 40
|
||
Remediation:
|
||
Set the ownership of the NGINX configuration directory and files to root:
|
||
chown -R root:root /etc/nginx
|
||
(Replace /etc/nginx with the actual configuration path in case it is different)
|
||
Note: Ensure that this does not break access to specific files if you have a custom
|
||
setup where external processes need write access.
|
||
Default Value:
|
||
The default ownership and group for nginx is root.
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.3 Configure Data Access Control Lists
|
||
v8 Configure data access control lists based on a user’s need to know. Apply data
|
||
access control lists, also known as access permissions, to local and remote file
|
||
● ● ●
|
||
systems, databases, and applications.
|
||
|
||
|
||
14.6 Protect Information through Access Control Lists
|
||
Protect all information stored on systems with file system, network share,
|
||
v7 claims, application, or database specific access control lists. These controls will
|
||
enforce the principle that only authorized individuals should have access to the
|
||
● ● ●
|
||
information based on their need to access the information as a part of their
|
||
responsibilities.
|
||
|
||
|
||
|
||
|
||
Page 41
|
||
2.3.2 Ensure access to NGINX directories and files is restricted
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The NGINX configuration directory (/etc/nginx or equivalent) and its contents should
|
||
have restrictive permissions to enforce the principle of least privilege.
|
||
|
||
• Directories should be accessible only by the root user and the root group (and
|
||
potentially read/execute by the group), but not by others.
|
||
• Files should be readable/writable by root and readable by the group, but
|
||
inaccessible to others.
|
||
|
||
Rationale:
|
||
Restrictive file permissions prevent unauthorized users on the system from viewing
|
||
sensitive configuration details, such as backend IP addresses, routing logic, or loaded
|
||
module paths. By removing "world" access (permissions for "other"), we ensure that
|
||
only administrators (via sudo) can interact with the web server configuration. This is a
|
||
fundamental defense against information disclosure.
|
||
Impact:
|
||
Setting permissions to 640 (files) and 750 (directories) prevents non-privileged users
|
||
from listing the configuration directory or reading configuration files.
|
||
Audit:
|
||
1. Identify Configuration Directory:
|
||
Run nginx -V and identify the --conf-path to determine the configuration root (e.g.,
|
||
/etc/nginx).
|
||
2. Audit Directory Permissions:
|
||
Run the following command to find directories with loose permissions:
|
||
find /etc/nginx -type d -exec stat -Lc "%n %a" {} +
|
||
Evaluation:
|
||
Verify that the output shows permissions of 750 (drwxr-x---) or more restrictive (e.g.,
|
||
700).
|
||
|
||
Page 42
|
||
• Standard: 755 (drwxr-xr-x) allows world read/execute.
|
||
• Hardened Target: 750 or 700.
|
||
|
||
3. Audit File Permissions:
|
||
Run the following command to find files with loose permissions:
|
||
find /etc/nginx -type f -exec stat -Lc "%n %a" {} +
|
||
Evaluation:
|
||
Verify that the output shows permissions of 640 (-rw-r-----) or more restrictive (e.g.,
|
||
600).
|
||
|
||
• Standard: 644 (-rw-r--r--) allows world read.
|
||
• Hardened Target: 640 or 600.
|
||
|
||
Remediation:
|
||
To restrict access to the NGINX configuration directory and files, execute the following
|
||
commands:
|
||
1. Restrict Directories (750):
|
||
Allow owner (root) full access, group read/execute, deny others.
|
||
find /etc/nginx -type d -exec chmod 750 {} +
|
||
2. Restrict Files (640): Allow owner (root) read/write, group read, deny others.
|
||
find /etc/nginx -type f -exec chmod 640 {} +
|
||
Note: Private keys (e.g., .key files) require even stricter permissions (400 or 600) and
|
||
should be addressed separately or manually verified here.
|
||
Default Value:
|
||
Default permissions are typically 755 for directories and 644 for files (world-readable).
|
||
|
||
Additional Information:
|
||
Critical: If your NGINX setup relies on the worker process (nginx user) reading
|
||
configuration files directly (uncommon for standard configs, but possible with certain
|
||
includes or SSL certificates not handled by the master process), ensure the nginx user
|
||
is in the group that owns the files, OR use chmod 644 if absolutely necessary. However,
|
||
the Master Process (root) handles parsing, so 640/root:root is usually safe.
|
||
|
||
|
||
|
||
|
||
Page 43
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.3 Configure Data Access Control Lists
|
||
v8 Configure data access control lists based on a user’s need to know. Apply data
|
||
access control lists, also known as access permissions, to local and remote file
|
||
● ● ●
|
||
systems, databases, and applications.
|
||
|
||
|
||
14.6 Protect Information through Access Control Lists
|
||
Protect all information stored on systems with file system, network share,
|
||
v7 claims, application, or database specific access control lists. These controls will
|
||
enforce the principle that only authorized individuals should have access to the
|
||
● ● ●
|
||
information based on their need to access the information as a part of their
|
||
responsibilities.
|
||
|
||
|
||
|
||
|
||
Page 44
|
||
2.3.3 Ensure the NGINX process ID (PID) file is secured (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
The PID file stores the main process ID of the nginx process. This file should be
|
||
protected from unauthorized modification.
|
||
Rationale:
|
||
The PID file should be owned by root and the group root. It should also be readable to
|
||
everyone, but only writable by root (permissions 644). This will prevent unauthorized
|
||
modification of the PID file, which could cause a denial of service.
|
||
|
||
Impact:
|
||
None. The PID file is managed by the master process (root). Restricting write access
|
||
prevents other users from tampering with the file, but read access is generally safe and
|
||
required for monitoring.
|
||
Audit:
|
||
1. Identify PID File Location:
|
||
Run nginx -V and look for the --pid-path argument to confirm the location (e.g.,
|
||
/run/nginx.pid or /var/run/nginx.pid).
|
||
2. Verify Ownership and Permissions:
|
||
Run the following command (substituting the identified path):
|
||
stat -Lc "%U:%G %a" /run/nginx.pid
|
||
Evaluation:
|
||
|
||
• Ownership: Must be root:root.
|
||
• Permissions: Must be 644 (rw-r--r--) or more restrictive.
|
||
|
||
|
||
|
||
|
||
Page 45
|
||
Remediation:
|
||
Set the correct ownership and permissions for the PID file (replace path as needed):
|
||
chown root:root /run/nginx.pid
|
||
chmod 644 /run/nginx.pid
|
||
|
||
Default Value:
|
||
The PID file is owned by root and has permissions 644 by default when building using
|
||
(dnf or apt).
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.3 Configure Data Access Control Lists
|
||
v8 Configure data access control lists based on a user’s need to know. Apply data
|
||
access control lists, also known as access permissions, to local and remote file
|
||
● ● ●
|
||
systems, databases, and applications.
|
||
|
||
|
||
14.6 Protect Information through Access Control Lists
|
||
Protect all information stored on systems with file system, network share,
|
||
v7 claims, application, or database specific access control lists. These controls will
|
||
enforce the principle that only authorized individuals should have access to the
|
||
● ● ●
|
||
information based on their need to access the information as a part of their
|
||
responsibilities.
|
||
|
||
|
||
|
||
|
||
Page 46
|
||
2.4 Network Configuration
|
||
|
||
|
||
|
||
|
||
Page 47
|
||
2.4.1 Ensure NGINX only listens for network connections on
|
||
authorized ports (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
NGINX should be configured to listen only on authorized ports and protocols. While
|
||
traditional HTTP/1.1 and HTTP/2 use TCP ports 80 and 443, modern HTTP/3 (QUIC)
|
||
utilizes UDP port 443. Ensuring that NGINX binds only to approved interfaces and ports
|
||
minimizes the attack surface.
|
||
Rationale:
|
||
Limiting listening ports to authorized values ensures that no hidden or unintended
|
||
services are exposed via NGINX. It also enforces strict control over which protocols
|
||
(TCP vs. UDP) are accessible, which is particularly important with the introduction of
|
||
UDP-based HTTP/3 traffic alongside traditional TCP traffic.
|
||
Impact:
|
||
Disabling unused ports reduces the risk of unauthorized access. However,
|
||
administrators must be aware that disabling UDP port 443 will break HTTP/3
|
||
connectivity, forcing clients to fall back to slower TCP-based HTTP/2 or HTTP/1.1.
|
||
Audit:
|
||
1. Inspect Configuration:
|
||
Run the following command to inspect all listen directives in the loaded configuration:
|
||
nginx -T 2>/dev/null | grep -r "listen"
|
||
Evaluation:
|
||
Review the output for unauthorized ports. A modern secure configuration typically
|
||
includes:
|
||
|
||
• listen 80; (TCP) - Often used only for redirecting to HTTPS.
|
||
• listen 443 ssl; (TCP) - For HTTP/1.1 and HTTP/2.
|
||
• listen 443 quic; (UDP) - For HTTP/3 (QUIC).
|
||
|
||
|
||
|
||
|
||
Page 48
|
||
Example Output:
|
||
server {
|
||
|
||
listen 80;
|
||
listen 443 ssl;
|
||
listen 443 quic reuseport; # HTTP/3 (UDP)
|
||
...
|
||
}
|
||
Ensure that no other ports (e.g., 8080, 8443) are open unless explicitly authorized for
|
||
internal services or management interfaces.
|
||
2. Verify System Listening Ports:
|
||
Optionally, verify what the process is actually binding to on the OS level:
|
||
netstat -tulpen | grep -i nginx
|
||
|
||
• Look for tcp lines for standard traffic.
|
||
• Look for udp lines (e.g., *:443) if HTTP/3 is enabled.
|
||
|
||
Remediation:
|
||
Remove or comment out any listen directives that bind to unauthorized ports.
|
||
For HTTP/3 (QUIC) Support: Ensure that you explicitly authorize and configure UDP
|
||
port 443 in addition to TCP port 443.
|
||
server {
|
||
|
||
# Standard HTTPS (TCP)
|
||
listen 443 ssl;
|
||
|
||
# HTTP/3 (UDP)
|
||
listen 443 quic reuseport;
|
||
|
||
# ... SSL/TLS configuration ...
|
||
}
|
||
|
||
Default Value:
|
||
By default, NGINX often listens only on TCP port 80. Modern secure defaults should
|
||
listen on TCP 80 (for redirect), TCP 443, and optionally UDP 443 (for HTTP/3).
|
||
|
||
|
||
|
||
|
||
Page 49
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.10 Apply Secure Design Principles in Application
|
||
Architectures
|
||
Apply secure design principles in application architectures. Secure design
|
||
principles include the concept of least privilege and enforcing mediation to validate
|
||
v8 every operation that the user makes, promoting the concept of "never trust user
|
||
input." Examples include ensuring that explicit error checking is performed and
|
||
● ●
|
||
documented for all input, including for size, data type, and acceptable ranges or
|
||
formats. Secure design also means minimizing the application infrastructure attack
|
||
surface, such as turning off unprotected ports and services, removing unnecessary
|
||
programs and files, and renaming or removing default accounts.
|
||
|
||
|
||
9.2 Ensure Only Approved Ports, Protocols and Services
|
||
v7 Are Running ● ●
|
||
Ensure that only network ports, protocols, and services listening on a system
|
||
with validated business needs, are running on each system.
|
||
|
||
|
||
|
||
|
||
Page 50
|
||
2.4.2 Ensure requests for unknown host names are rejected
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
NGINX routes incoming requests to the appropriate virtual host by matching the Host
|
||
header (HTTP/1.1) or :authority pseudo-header (HTTP/2, HTTP/3) against the
|
||
server_name directives in your configuration. If no explicit match is found, NGINX falls
|
||
back to the first defined server block or the one marked as default_server. Without
|
||
a properly configured catch-all block that rejects unknown hostnames, your server will
|
||
respond to arbitrary domain names that happen to point to your IP address, potentially
|
||
exposing internal applications or enabling Host Header attacks.
|
||
Rationale:
|
||
When NGINX receives a request, it selects the virtual host based on the Host header
|
||
(or :authority in HTTP/2/3). If requests for unknown host names are not explicitly
|
||
rejected, your applications may be served for arbitrary domains that simply point to your
|
||
IP. This behavior can be abused in Host Header attacks and makes it harder to
|
||
distinguish legitimate traffic from automated scans or misrouted requests in your logs.
|
||
Impact:
|
||
Clients accessing the server directly via IP address or an unconfigured CNAME will be
|
||
rejected. This is intended behavior but requires that all valid domains are explicitly
|
||
defined in their own server blocks.
|
||
|
||
Audit:
|
||
1. Review Configuration:
|
||
Check for the existence of a default server block that handles unknown hosts.
|
||
nginx -T 2>/dev/null | grep -Ei "listen.*default_server|ssl_reject_handshake"
|
||
Evaluation:
|
||
|
||
• Ensure a server block exists with listen ... default_server.
|
||
• Verify it contains return 444; (closes connection) or a 4xx error code.
|
||
• For HTTPS/TLS: Verify ssl_reject_handshake on; is used to prevent
|
||
certificate leakage.
|
||
|
||
|
||
|
||
Page 51
|
||
2. Functional Test:
|
||
Send a request with an invalid Host header and verify the connection is rejected or
|
||
returns an error.
|
||
# Test HTTPS (expect connection reset or 4xx)
|
||
curl -k -v https://127.0.0.1 -H 'Host: invalid.example.com'
|
||
|
||
Remediation:
|
||
Configure a "Catch-All" default server block as the first block in your configuration (or
|
||
explicitly marked with default_server).
|
||
Configuration Example (Modern Standard with TLS/HTTP3):
|
||
server {
|
||
|
||
# Listen on standard ports for IPv4 and IPv6
|
||
listen 80 default_server;
|
||
listen [::]:80 default_server;
|
||
|
||
# Listen for HTTPS (TCP) and QUIC (UDP)
|
||
listen 443 ssl default_server;
|
||
listen [::]:443 ssl default_server;
|
||
listen 443 quic default_server;
|
||
listen [::]:443 quic default_server;
|
||
|
||
# Reject SSL Handshake for unknown domains (Prevents cert leakage)
|
||
ssl_reject_handshake on;
|
||
|
||
# Catch-all name
|
||
server_name _;
|
||
|
||
# Close connection without response (Non-standard code 444)
|
||
return 444;
|
||
|
||
}
|
||
After adding this block, ensure all your valid applications have their own server blocks
|
||
with explicit server_name directives.
|
||
|
||
Default Value:
|
||
By default, if no default_server is defined, NGINX uses the first server block
|
||
configuration it finds, potentially serving your application for any incoming request
|
||
regardless of the Host header.
|
||
|
||
|
||
|
||
|
||
Page 52
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/request_processing.html
|
||
2. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Host
|
||
3. https://owasp.org/www-project-web-security-testing-guide/latest/4-
|
||
Web_Application_Security_Testing/07-Input_Validation_Testing/17-
|
||
Testing_for_Host_Header_Injection
|
||
4. https://portswigger.net/web-security/host-header
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
4.1 Establish and Maintain a Secure Configuration Process
|
||
Establish and maintain a secure configuration process for enterprise assets
|
||
v8 (end-user devices, including portable and mobile, non-computing/IoT devices, and
|
||
servers) and software (operating systems and applications). Review and update
|
||
● ● ●
|
||
documentation annually, or when significant enterprise changes occur that could
|
||
impact this Safeguard.
|
||
|
||
|
||
5.1 Establish Secure Configurations
|
||
v7 Maintain documented, standard security configuration standards for all ● ● ●
|
||
authorized operating systems and software.
|
||
|
||
|
||
|
||
|
||
Page 53
|
||
2.4.3 Ensure keepalive_timeout is 10 seconds or less, but not 0
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
Persistent connections are leveraged by all modern browsers to facilitate greater web
|
||
performance. The keep-alive timeout limits the time a persistent connection may remain
|
||
open. Setting the keep-alive timeout allows this timeout to be controlled on the server
|
||
side.
|
||
Rationale:
|
||
Setting a keep-alive timeout on the server side helps mitigate denial of service attacks
|
||
that establish too many persistent connections, exhausting server resources.
|
||
Audit:
|
||
To check the current setting for the keepalive_timeout directive, issue the below
|
||
command. You should also manually check your nginx configuration for include
|
||
statements that may be located outside the /etc/nginx directory. If none of these are
|
||
present, the value is set at the default.
|
||
grep -ir keepalive_timeout /etc/nginx
|
||
The output of the command should contain something similar to the following:
|
||
keepalive_timeout 10;
|
||
|
||
Remediation:
|
||
Find the HTTP or server block of your nginx configuration, and add the
|
||
keepalive_timeout directive. Set it to 10 seconds or less, but not 0. This example
|
||
command sets it to 10 seconds:
|
||
keepalive_timeout 10;
|
||
|
||
Default Value:
|
||
By default, this timeout is dictated by the user agent and varies. It is not set on the
|
||
server side by default.
|
||
|
||
|
||
|
||
|
||
Page 54
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 55
|
||
2.4.4 Ensure send_timeout is set to 10 seconds or less, but not 0
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The send_timeout directive sets a timeout for transmitting a response to the client
|
||
between two successive write operations.
|
||
Rationale:
|
||
Setting the send_timeout directive on the server side helps mitigate slow HTTP denial
|
||
of service attacks by ensuring write operations taking up large amounts of time are
|
||
closed.
|
||
Audit:
|
||
To check the current setting for the send_timeout directive, issue the below command.
|
||
You should also manually check your nginx configuration for include statements that
|
||
may be located outside the /etc/nginx directory. If none of these are present, the
|
||
value is set at the default.
|
||
grep -ir send_timeout /etc/nginx
|
||
The output of the command should be similar to the following:
|
||
send_timeout 10;
|
||
|
||
Remediation:
|
||
Find the HTTP or server block of your nginx configuration, and add the send_timeout
|
||
directive. Set it to 10 seconds or less, but not 0.
|
||
send_timeout 10;
|
||
|
||
Default Value:
|
||
send_timeout 60s;
|
||
|
||
References:
|
||
|
||
1. https://www.owasp.org/index.php/SCG_WS_nginx
|
||
2. https://nginx.org/en/docs/http/ngx_http_core_module.html#send_timeout
|
||
|
||
|
||
|
||
Page 56
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 57
|
||
2.5 Information Disclosure
|
||
|
||
|
||
|
||
|
||
Page 58
|
||
2.5.1 Ensure server_tokens directive is set to `off` (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
The server_tokens directive is responsible for displaying the NGINX version number
|
||
and operating system version on error pages and in the Server HTTP response header
|
||
field. This information should not be displayed.
|
||
Rationale:
|
||
Attackers can conduct reconnaissance on a website using these response headers,
|
||
then target attacks for specific known vulnerabilities associated with the underlying
|
||
technologies. Hiding the version will slow down and deter some potential attackers.
|
||
Impact:
|
||
None. Disabling server tokens does not affect functionality. It merely removes the
|
||
version string from error pages and headers. Note that determined attackers can still
|
||
fingerprint NGINX via other methods, but removing the banner raises the bar for
|
||
opportunistic scanners.
|
||
Audit:
|
||
In the NGINX configuration file nginx.conf, verify the server_tokens directive is set
|
||
to off. To do this, check the response headers for the server header by issuing this
|
||
command:
|
||
curl -I 127.0.0.1 | grep -i server
|
||
The output should not contain the server header providing your server version, such as
|
||
the below:
|
||
Server: nginx/1.28.0
|
||
|
||
|
||
|
||
|
||
Page 59
|
||
Remediation:
|
||
Disable version disclosure globally by adding the directive to the http block in
|
||
/etc/nginx/nginx.conf:
|
||
http {
|
||
...
|
||
server_tokens off;
|
||
...
|
||
}
|
||
|
||
Default Value:
|
||
The default value of server_tokens is on.
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 60
|
||
2.5.2 Ensure default error and index.html pages do not reference
|
||
NGINX (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
Default error pages (e.g., 404, 500) and the default welcome page often contain NGINX
|
||
branding or signatures. These pages should be removed or replaced with generic or
|
||
custom-branded pages that do not disclose the underlying server technology.
|
||
Rationale:
|
||
Standard NGINX error pages visually identify the server software, even if headers are
|
||
suppressed. By gathering information about the underlying technology stack, attackers
|
||
can tailor their exploits to known vulnerabilities of NGINX. Replacing default pages with
|
||
generic or branded content removes this information leakage vector and increases the
|
||
effort required for successful reconnaissance.
|
||
Impact:
|
||
Creating and maintaining custom error pages requires additional administrative effort.
|
||
Ensure that custom error pages are simple and do not themselves introduce
|
||
vulnerabilities.
|
||
Audit:
|
||
1. Verify Config:
|
||
Check if error_page directives are active:
|
||
nginx -T 2>/dev/null | grep -i "error_page"
|
||
2. Verify Content (Functional Test):
|
||
Trigger an error (e.g., request a non-existent page) and inspect the body:
|
||
curl -k https://127.0.0.1/non-existent-page | grep -i "nginx"
|
||
Evaluation:
|
||
|
||
• PASS: The output does not contain "nginx" (or grep returns nothing).
|
||
• FAIL: The HTML body contains "nginx".
|
||
|
||
|
||
|
||
|
||
Page 61
|
||
Remediation:
|
||
Instead of editing the default files (which may be overwritten by package updates),
|
||
configure NGINX to use custom error pages.
|
||
1. Create Custom Error Pages:
|
||
Create a directory (e.g., /var/www/html/errors) and place generic HTML files there
|
||
(e.g., 404.html, 50x.html) without NGINX branding.
|
||
2. Configure NGINX:
|
||
Add the error_page directive to your http or server blocks:
|
||
error_page 404 /404.html;
|
||
error_page 500 502 503 504 /50x.html;
|
||
|
||
location = /50x.html {
|
||
root /var/www/html/errors;
|
||
internal;
|
||
}
|
||
|
||
Default Value:
|
||
Default error pages identify the server as NGINX.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 62
|
||
2.5.3 Ensure hidden file serving is disabled (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
Hidden files and directories (starting with a dot, e.g., .git, .env) often contain sensitive
|
||
metadata, version control history, or environment configurations. Serving these files
|
||
should be globally disabled.
|
||
Rationale:
|
||
Version control systems (Git, SVN) and editors create hidden files that may
|
||
unintentionally be deployed to the web root. If accessible, files like .git/config or
|
||
.env can leak database credentials, source code, and infrastructure details, leading to
|
||
full system compromise. Blocking requests to any path starting with a dot (.) neutralizes
|
||
this risk.
|
||
Impact:
|
||
Blocking all dot-files will break Let's Encrypt / Certbot validation (.well-known/acme-
|
||
challenge) unless explicitly allowed. Ensure the exception rule is placed before the
|
||
deny rule or is more specific.
|
||
Audit:
|
||
1. Check Configuration:
|
||
Search the loaded configuration for hidden file protection rules:
|
||
nginx -T 2>/dev/null | grep "location.*\\\."
|
||
Evaluation:
|
||
|
||
• Look for a block like location ~ /\. { deny all; ... }.
|
||
|
||
2. Functional Test (Recommended):
|
||
Try to access a dummy hidden file:
|
||
curl -k -I https://127.0.0.1/.git/HEAD
|
||
Evaluation:
|
||
|
||
• PASS: Returns 403 Forbidden or 404 Not Found.
|
||
• FAIL: Returns 200 OK (if file exists) or the content of the file.
|
||
|
||
Page 63
|
||
Remediation:
|
||
To restrict access to hidden files, add the configuration block below inside each server
|
||
block.
|
||
Option A: Direct Configuration
|
||
Place this block directly into your server contexts:
|
||
# Allow Let's Encrypt validation (must be before the deny rule)
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
allow all;
|
||
default_type "text/plain";
|
||
}
|
||
|
||
# Deny access to all other hidden files
|
||
location ~ /\. {
|
||
deny all;
|
||
return 404;
|
||
}
|
||
Option B: Using a Shared Snippet (Recommended)
|
||
Create a reusable snippet file (e.g., /etc/nginx/snippets/deny-hidden.conf)
|
||
containing the rules above, and include it in your server blocks:
|
||
|
||
1. Create /etc/nginx/snippets/deny-hidden.conf with the content from Option
|
||
A.
|
||
2. Security Check: Ensure the new file has restrictive permissions (Owner:
|
||
root:root, Mode: 640) as described in Recommendation 2.3.2.
|
||
3. Add the include directive to your server blocks:
|
||
|
||
server {
|
||
# Modern HTTP/3 (QUIC) and HTTP/2 Setup
|
||
|
||
listen 443 ssl; # TCP for HTTP/1.1 & HTTP/2
|
||
listen 443 quic reuseport; # UDP for HTTP/3
|
||
http2 on; # Explicitly enable HTTP/2 (since NGINX
|
||
1.25.1)
|
||
|
||
server_name example.com;
|
||
|
||
include /etc/nginx/snippets/deny-hidden.conf;
|
||
|
||
# ... rest of configuration
|
||
}
|
||
|
||
Default Value:
|
||
This protection is not set by default. NGINX will serve any hidden file if it exists in the
|
||
web root.
|
||
|
||
|
||
|
||
|
||
Page 64
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#location
|
||
2. https://nginx.org/en/docs/http/ngx_http_access_module.html#deny
|
||
3. https://capec.mitre.org/data/definitions/139.html
|
||
4. https://cwe.mitre.org/data/definitions/538.html
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 65
|
||
2.5.4 Ensure the NGINX reverse proxy does not enable
|
||
information disclosure (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
When NGINX acts as a reverse proxy, it forwards headers sent by the upstream
|
||
application (e.g., "X-Powered-By: Custom_APP" or "Server: Apache/2.4"). These
|
||
headers should be stripped before the response reaches the client to prevent
|
||
information disclosure about the backend infrastructure.
|
||
Rationale:
|
||
Attackers conduct reconnaissance by inspecting response headers to identify the
|
||
technologies used in the backend (e.g., specific versions of PHP, Java/Tomcat, or
|
||
Python frameworks). Knowing the exact version allows attackers to target specific CVEs
|
||
associated with that software stack. Removing these headers reduces the information
|
||
available for targeted attacks.
|
||
Audit:
|
||
1. Configuration Check:
|
||
Search the loaded configuration for header hiding directives:
|
||
nginx -T 2>/dev/null | grep -Ei "(proxy|fastcgi)_hide_header"
|
||
Evaluation:
|
||
|
||
• Verify that directives exist to hide X-Powered-By and Server.
|
||
|
||
2. Functional Check (Recommended):
|
||
Send a request to a proxied endpoint and inspect the response headers:
|
||
curl -k -I https://127.0.0.1 | grep -Ei "^(Server|X-Powered-By)"
|
||
Evaluation:
|
||
|
||
• PASS: The output does not contain backend details (e.g., X-Powered-By:
|
||
PHP/8.2). If Server is present, it should only be Server: nginx (controlled by
|
||
Control 2.5.1).
|
||
• FAIL: The output contains backend information or unmasked version numbers.
|
||
|
||
|
||
|
||
|
||
Page 66
|
||
Remediation:
|
||
Configure NGINX to strip the sensitive headers. The directive depends on the upstream
|
||
protocol (HTTP Proxy vs. FastCGI).
|
||
For Standard Reverse Proxy (proxy_pass):
|
||
Add the following directives to your http, server, or location block:
|
||
proxy_hide_header X-Powered-By;
|
||
proxy_hide_header Server;
|
||
For PHP/FastCGI (fastcgi_pass):
|
||
If you are using FastCGI (e.g., for PHP-FPM), use the fastcgi_hide_header directive
|
||
instead:
|
||
fastcgi_hide_header X-Powered-By;
|
||
|
||
Default Value:
|
||
By default, NGINX passes all headers received from the upstream server to the client
|
||
unchanged.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_proxy_module.html
|
||
2. https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_hide_header
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 67
|
||
3 Logging
|
||
|
||
|
||
|
||
|
||
Page 68
|
||
3.1 Ensure detailed logging is enabled (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
System logging must be configured to meet organizational security and privacy policies.
|
||
Detailed logs provide the necessary context (event source, timestamp, user, network
|
||
data) for incident response and forensic analysis. Modern logging strategies favor
|
||
structured formats (JSON) over unstructured text to facilitate parsing by SIEM solutions.
|
||
Note: Sensitive information (e.g., session tokens, PII in query strings) should be
|
||
excluded or masked in logs to prevent data leaks.
|
||
Rationale:
|
||
Detailed logs are the foundation of effective incident response. CIS Control 8.5 ("Collect
|
||
Detailed Audit Logs") recommends capturing event sources, dates, users, timestamps,
|
||
and network addresses. Traditional text logs require complex, fragile Regex parsing that
|
||
breaks easily when formats change. Structured logging (JSON) solves this by providing
|
||
a self-describing format that is natively ingested by modern analysis tools (SIEM),
|
||
ensuring that critical forensic data is always indexable and searchable.
|
||
Impact:
|
||
Enabling detailed JSON logging increases the volume of log data. Ensure that your log
|
||
rotation policies (logrotate) and disk space monitoring are adjusted to handle the
|
||
increased storage requirements.
|
||
Audit:
|
||
1. Verify Log Format Configuration:
|
||
Inspect the log_format directives in your configuration:
|
||
nginx -T 2>/dev/null | grep -i "log_format"
|
||
Evaluation:
|
||
|
||
• Confirm that a detailed format (preferably JSON) is defined.
|
||
• Verify that the format includes critical fields: $time_iso8601, $remote_addr,
|
||
$remote_user, $request, $status, $http_user_agent.
|
||
|
||
|
||
|
||
|
||
Page 69
|
||
2. Verify Access Log Usage:
|
||
Check that the defined format is actually used by the access_log directive:
|
||
nginx -T 2>/dev/null | grep "access_log"
|
||
Evaluation:
|
||
|
||
• The access_log directive should reference the detailed format name (e.g.,
|
||
access_log /var/log/nginx/access.json main_access_json;).
|
||
|
||
Remediation:
|
||
Define a detailed log format in the http block of /etc/nginx/nginx.conf. It is highly
|
||
recommended to use JSON format for compatibility with modern SIEM tools.
|
||
Recommended Configuration (JSON):
|
||
http {
|
||
log_format main_access_json escape=json '{'
|
||
'"timestamp": "$time_iso8601",'
|
||
'"remote_addr": "$remote_addr",'
|
||
'"remote_user": "$remote_user",'
|
||
'"server_name": "$server_name",'
|
||
'"request_method": "$request_method",'
|
||
'"request_uri": "$request_uri",'
|
||
'"status": $status,'
|
||
'"body_bytes_sent": $body_bytes_sent,'
|
||
'"http_referer": "$http_referer",'
|
||
'"http_user_agent": "$http_user_agent",'
|
||
'"x_forwarded_for": "$http_x_forwarded_for",'
|
||
'"request_id": "$request_id"'
|
||
'}';
|
||
|
||
# Apply the format globally or per server
|
||
access_log /var/log/nginx/access.json main_access_json;
|
||
}
|
||
Legacy Configuration (Text-based):
|
||
If JSON is not feasible, ensure the text format captures all necessary fields:
|
||
log_format main_detailed '$remote_addr - $remote_user [$time_local] '
|
||
'"$request" $status $body_bytes_sent '
|
||
'"$http_referer" "$http_user_agent" '
|
||
'"$http_x_forwarded_for"';
|
||
|
||
|
||
Default Value:
|
||
By default, NGINX uses the combined log format, which is a standard text format but
|
||
lacks details (e.g., request processing time, upstream information).
|
||
|
||
|
||
|
||
|
||
Page 70
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format
|
||
2. https://nginx.org/en/docs/varindex.html
|
||
|
||
Additional Information:
|
||
|
||
• Load Balancers & Proxies: Since NGINX often sits behind other proxies (LBs,
|
||
CDNs), the $remote_addr variable may only show the LB's IP. Ensure you log
|
||
$http_x_forwarded_for (as shown in the JSON example) to capture the true
|
||
client IP.
|
||
• SIEM Integration: The escape=json parameter automatically handles escaping
|
||
of special characters, preventing broken JSON structures.
|
||
• Policy Compliance: Consult your internal Logging & Monitoring Policy to
|
||
determine exactly which data points are required for retention and which
|
||
sensitive fields must be excluded.
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
8.5 Collect Detailed Audit Logs
|
||
v8 Configure detailed audit logging for enterprise assets containing sensitive data.
|
||
Include event source, date, username, timestamp, source addresses, destination
|
||
● ●
|
||
addresses, and other useful elements that could assist in a forensic investigation.
|
||
|
||
|
||
6.3 Enable Detailed Logging
|
||
v7 Enable system logging to include detailed information such as an event source,
|
||
date, user, timestamp, source addresses, destination addresses, and other useful
|
||
● ●
|
||
elements.
|
||
|
||
|
||
|
||
|
||
Page 71
|
||
3.2 Ensure access logging is enabled (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
The access_log directive enables the logging of client requests. While NGINX enables
|
||
this by default, it allows granular control per server or location context. Based on
|
||
enterprise requirements, the log should be enriched with relevant variables or converted
|
||
to structured JSON format for modern SIEM integration. Refer to Recommendation 3.1
|
||
for detailed configuration of log formats and variables. Ensure that access logging is
|
||
active for all critical services.
|
||
Rationale:
|
||
Access logs are the primary record of system usage, detailing who accessed what
|
||
resources and when and general troubleshooting. Without active access logs, incident
|
||
responders are blind to web-based attacks (such as SQL injection, XSS probing, or
|
||
Brute Force attempts) and auditors cannot verify compliance or user activity. Disabling
|
||
logs globally (access_log off;) effectively destroys the forensic chain of custody for
|
||
security events.
|
||
Impact:
|
||
Enabling detailed access logging increases disk space usage significantly. Without
|
||
proper log rotation (e.g., logrotate) and monitoring, log files can rapidly consume
|
||
available disk space, potentially causing the server to stop processing requests or
|
||
crash. Ensure sufficient storage capacity and retention policies are in place.
|
||
Audit:
|
||
1. Verify Configuration:
|
||
Inspect the fully loaded configuration for log settings:
|
||
nginx -T 2>/dev/null | grep -i "access_log"
|
||
|
||
|
||
|
||
|
||
Page 72
|
||
Evaluation:
|
||
|
||
• Destination Check: Verify that access_log directives point to a valid local file
|
||
path (e.g., /var/log/nginx/access.json) for ingestion by log shippers.
|
||
• Status Check: Identify any instances of access_log off;.
|
||
• Pass: If access_log off; is absent, or strictly limited to non-critical assets (e.g.,
|
||
location = /favicon.ico, static assets, or internal health checks).
|
||
• Fail: If access_log off; is applied globally in the http block or to server
|
||
blocks handling business logic.
|
||
|
||
Remediation:
|
||
Enable access logging in the http block to set a secure global default, or configure it
|
||
explicitly within specific server blocks. It is recommended to use the detailed log format
|
||
defined in Recommendation 3.1.
|
||
Configuration Example:
|
||
http {
|
||
|
||
# Enable global logging using the detailed JSON format from Rec 3.1
|
||
access_log /var/log/nginx/access.json main_access_json;
|
||
|
||
server {
|
||
|
||
# Inherits the global log setting, or can be overridden:
|
||
access_log /var/log/nginx/example.com.access.json main_access_json;
|
||
|
||
location / {
|
||
# ...
|
||
}
|
||
|
||
# Exception: Disable logging for favicon to reduce noise (Optional)
|
||
location = /favicon.ico {
|
||
access_log off;
|
||
log_not_found off;
|
||
}
|
||
}
|
||
}
|
||
|
||
Default Value:
|
||
Access logging is enabled by default, typically logging to logs/access.log or
|
||
/var/log/nginx/access.log using the standard combined format.
|
||
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log
|
||
2. https://nginx.org/en/docs/http/ngx_http_core_module.html#log_not_found
|
||
|
||
|
||
|
||
|
||
Page 73
|
||
Additional Information:
|
||
Embedded Variables for NGINX
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
8.5 Collect Detailed Audit Logs
|
||
v8 Configure detailed audit logging for enterprise assets containing sensitive data.
|
||
Include event source, date, username, timestamp, source addresses, destination
|
||
● ●
|
||
addresses, and other useful elements that could assist in a forensic investigation.
|
||
|
||
|
||
6.3 Enable Detailed Logging
|
||
v7 Enable system logging to include detailed information such as an event source,
|
||
date, user, timestamp, source addresses, destination addresses, and other useful
|
||
● ●
|
||
elements.
|
||
|
||
|
||
|
||
|
||
Page 74
|
||
3.3 Ensure error logging is enabled and set to the info logging
|
||
level (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The error_log directive configures logging for server errors and operational
|
||
messages. Unlike access logs, error logs capture diagnostic information about failed
|
||
requests, upstream connection issues, and configuration errors. The log level
|
||
determines the verbosity of these messages and should be set to capture sufficient
|
||
detail (typically notice or info) without overwhelming the storage system.
|
||
|
||
Rationale:
|
||
While access logs capture incoming request patterns, error logs provide the internal
|
||
system context required to diagnose why a request failed. They are essential for
|
||
identifying:
|
||
1. Upstream Failures: Connection timeouts or refused connections to backend servers
|
||
(e.g., application server is down).
|
||
2. Process Anomalies: Unexpected worker process terminations or restarts, which
|
||
may indicate resource exhaustion or exploitation attempts.
|
||
3. Configuration Errors: Invalid request handling that NGINX rejects before logging to
|
||
access logs (e.g., header size limits exceeded).
|
||
Without error logs, an administrator sees a "500 Internal Server Error" in the
|
||
access log but has no way to determine the root cause.
|
||
Impact:
|
||
Setting the log level to info (or even debug) can generate a significant volume of log
|
||
data, especially on busy servers or during denial-of-service attacks. This increases disk
|
||
I/O and storage requirements. Ensure that log rotation (e.g., via logrotate) is
|
||
configured and storage usage is monitored to prevent disk exhaustion.
|
||
|
||
|
||
|
||
|
||
Page 75
|
||
Audit:
|
||
1. Verify Configuration:
|
||
Check the fully loaded configuration for error log settings:
|
||
nginx -T 2>/dev/null | grep -i "error_log"
|
||
Evaluation:
|
||
|
||
• Presence: Verify that error_log is defined globally in the main context (or http
|
||
block).
|
||
• Destination: Ensure it points to a valid local file (e.g.,
|
||
/var/log/nginx/error.log) accessible for ingestion by log shippers.
|
||
• Level: Confirm the level is set according to your internal "Monitoring and
|
||
Logging" policy.
|
||
• Fail: If error_log points to /dev/null or the level is set to crit, alert, or
|
||
emerg (which suppresses too many relevant warnings).
|
||
|
||
Remediation:
|
||
Configure the error_log directive in the main context (at the top of nginx.conf) to
|
||
capture operational events.
|
||
Configuration Example:
|
||
# Log errors to a specific file with the 'notice' level
|
||
error_log /var/log/nginx/error.log notice;
|
||
|
||
http {
|
||
# ...
|
||
}
|
||
Note: The specific logging level should be aligned with the organization's "Monitoring
|
||
and Logging" Policy, balancing the need for forensic detail against storage and
|
||
processing costs. Typically, info or notice is recommended.
|
||
|
||
Default Value:
|
||
By default, NGINX logs errors to logs/error.log with the severity level error. This
|
||
configuration misses warn, notice, and info events.
|
||
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/ngx_core_module.html#error_log
|
||
2. https://docs.nginx.com/nginx/admin-guide/monitoring/logging/
|
||
|
||
Additional Information:
|
||
Unlike access logs, NGINX Open Source uses a hardcoded text format for error logs
|
||
and does not support custom log_format definitions (e.g., JSON, additional variables).
|
||
|
||
|
||
Page 76
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
8.5 Collect Detailed Audit Logs
|
||
v8 Configure detailed audit logging for enterprise assets containing sensitive data.
|
||
Include event source, date, username, timestamp, source addresses, destination
|
||
● ●
|
||
addresses, and other useful elements that could assist in a forensic investigation.
|
||
|
||
|
||
6.3 Enable Detailed Logging
|
||
v7 Enable system logging to include detailed information such as an event source,
|
||
date, user, timestamp, source addresses, destination addresses, and other useful
|
||
● ●
|
||
elements.
|
||
|
||
|
||
|
||
|
||
Page 77
|
||
3.4 Ensure proxies pass source IP information (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
When NGINX acts as a reverse proxy or load balancer, it terminates the client
|
||
connection and opens a new connection to the upstream application server. By default,
|
||
the upstream server sees the NGINX server's internal IP address as the source,
|
||
obscuring the original client IP. Standard HTTP headers like X-Forwarded-For and X-
|
||
Real-IP must be explicitly configured to pass the original client's IP address and
|
||
protocol information to the backend application.
|
||
Rationale:
|
||
Visibility of the true client IP address is essential for security auditing, incident response,
|
||
and access control within the backend application. Without forwarding this information:
|
||
|
||
1. Forensics: Application logs will show all traffic coming from the NGINX proxy IP,
|
||
making it impossible to trace malicious activity to a specific attacker.
|
||
2. Access Control: Application-level IP allow/deny lists or rate limits will fail or
|
||
mistakenly block the entire proxy.
|
||
3. Compliance: Accurate logging of the user origin is often a regulatory
|
||
requirement.
|
||
|
||
Impact:
|
||
Enabling these headers allows the backend application to see the original client IP.
|
||
However, if NGINX simply appends to an existing X-Forwarded-For header sent by a
|
||
malicious client, the backend might be tricked into trusting a spoofed IP at the beginning
|
||
of the list.
|
||
|
||
|
||
|
||
|
||
Page 78
|
||
Audit:
|
||
1. Verify Configuration:
|
||
Check the active configuration for proxy header directives in proxied locations:
|
||
nginx -T 2>/dev/null | grep -E "proxy_set_header (X-Real-IP|X-Forwarded-For)"
|
||
Evaluation:
|
||
|
||
• Presence: Verify that proxy_set_header X-Forwarded-For and
|
||
proxy_set_header X-Real-IP are present in location blocks that use
|
||
proxy_pass (or grpc_pass, fastcgi_pass).
|
||
• Correctness:
|
||
o X-Forwarded-For should typically use $proxy_add_x_forwarded_for
|
||
(to preserve the chain) or $remote_addr (if NGINX is the first trusted
|
||
hop).
|
||
o X-Real-IP should use $remote_addr.
|
||
• Scope: Check that this is applied to all relevant proxied locations.
|
||
|
||
Remediation:
|
||
Configure NGINX to forward client IP information in your server or location blocks
|
||
where proxy_pass is used.
|
||
Configuration Example:
|
||
location / {
|
||
|
||
# Use 'https' for Zero Trust environments (requires proxy_ssl_verify
|
||
configuration)
|
||
# Use 'http' for standard TLS offloading (upstream traffic is
|
||
unencrypted)
|
||
proxy_pass <protocol>://example_backend_application;
|
||
|
||
# Standard header: Appends the client IP to the list of proxies
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
|
||
# NGINX-specific header: Sets the direct client IP (useful for apps
|
||
expecting a single value)
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
|
||
# Recommended: Forward the protocol (http vs https)
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
Default Value:
|
||
By default, NGINX does not add these headers. The upstream server receives requests
|
||
appearing to originate from the NGINX server's IP address.
|
||
|
||
|
||
|
||
|
||
Page 79
|
||
References:
|
||
|
||
1. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-
|
||
Forwarded-For
|
||
2. https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
|
||
|
||
Additional Information:
|
||
Privacy: Users' privacy should be considered when forwarding client IP addresses.
|
||
Ensure your organization's privacy policy discloses the collection and processing of IP
|
||
address information.
|
||
Security Trust: Any information in the X-Forwarded-For header supplied by the client
|
||
(before reaching your trusted infrastructure) is untrusted and can be easily spoofed.
|
||
Backend applications and security controls (like rate limiting) must be configured to trust
|
||
only the IPs appended by your own NGINX instance or known trusted proxies, ignoring
|
||
the client-supplied part of the chain.
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
8.11 Conduct Audit Log Reviews
|
||
v8 Conduct reviews of audit logs to detect anomalies or abnormal events that
|
||
could indicate a potential threat. Conduct reviews on a weekly, or more frequent,
|
||
● ●
|
||
basis.
|
||
|
||
|
||
v7 6.7 Regularly Review Logs ● ●
|
||
On a regular basis, review logs to identify anomalies or abnormal events.
|
||
|
||
|
||
|
||
|
||
Page 80
|
||
4 Encryption
|
||
|
||
|
||
|
||
|
||
Page 81
|
||
4.1 TLS / SSL Configuration
|
||
|
||
|
||
|
||
|
||
Page 82
|
||
4.1.1 Ensure HTTP is redirected to HTTPS (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
Browsers and clients establish encrypted connections with servers by leveraging
|
||
HTTPS. Requests leveraging HTTP are unencrypted. Unencrypted requests should be
|
||
redirected so they are encrypted. Any listening HTTP port on your web server should
|
||
redirect to a server profile that uses encryption. The default HTTP (unencrypted) port is
|
||
80.
|
||
|
||
Rationale:
|
||
Redirecting user agent traffic to HTTPS helps to ensure all user traffic is encrypted.
|
||
Modern browsers alert users that your website is insecure when HTTPS is not used.
|
||
This can decrease user trust in your website and ultimately result in decreased use of
|
||
your web services. Redirection from HTTP to HTTPS couples security with usability;
|
||
users are able to access your website even if they lack the security awareness to use
|
||
HTTPS over HTTP when requesting your website.
|
||
Impact:
|
||
Use of HTTPS does result in a performance reduction in traffic to your website,
|
||
however, due to the increased value of the security, many businesses consider this to
|
||
be a cost of doing business.
|
||
|
||
|
||
|
||
|
||
Page 83
|
||
Audit:
|
||
To verify your server listening configuration, check your web server or proxy
|
||
configuration file. The default web server configuration file is
|
||
/etc/nginx/conf.d/default.conf, and the default proxy configuration file is
|
||
/etc/nginx/nginx.conf. The configuration file should return a statement redirecting
|
||
to HTTPS. This should be similar to the code below, where cisecurity.org is used as an
|
||
example.
|
||
server {
|
||
listen 80;
|
||
|
||
server_name cisecurity.org;
|
||
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
Remediation:
|
||
Edit your web server or proxy configuration file to redirect all unencrypted listening
|
||
ports, such as port 80, using a redirection through the return directive (cisecurity.org is
|
||
used as an example server name).
|
||
server {
|
||
listen 80;
|
||
|
||
server_name cisecurity.org;
|
||
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
Default Value:
|
||
NGINX is not configured to use HTTPS or redirect to it by default.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 84
|
||
4.1.2 Ensure a trusted certificate and trust chain is installed
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
Certificates and their trust chains are needed to establish the identity of a web server as
|
||
legitimate and trusted. Certificate authorities validate a web server's identity and that
|
||
you are the owner of that web server domain name.
|
||
Rationale:
|
||
Without a certificate and full trust chain installed on your web server, modern browsers
|
||
will flag your web server as untrusted.
|
||
Audit:
|
||
Run this command to find the file location of your certificate:
|
||
grep -ir ssl_certificate /etc/nginx/
|
||
The output of your command should look similar to the below output. If there is no
|
||
output, you do not have a certificate installed.
|
||
Web Server:
|
||
/etc/nginx/nginx.conf: ssl_certificate /etc/nginx/cert.pem;
|
||
/etc/nginx/nginx.conf: ssl_certificate_key /etc/nginx/nginx.key;
|
||
Open the file to the right of the ssl_certificate directive using the following
|
||
command:
|
||
cat /etc/nginx/cert.pem
|
||
The output of your command should look similar to the below. It should include the full
|
||
certificate chain.
|
||
|
||
|
||
|
||
|
||
Page 85
|
||
-----BEGIN CERTIFICATE-----
|
||
Insert Your Web Server Certificate
|
||
-----END CERTIFICATE-----
|
||
-----BEGIN CERTIFICATE-----
|
||
Insert Your Certificate Authority Intermediate Certificate
|
||
-----END CERTIFICATE-----
|
||
-----BEGIN CERTIFICATE-----
|
||
Insert Your Certificate Authority Root Certificate
|
||
-----END CERTIFICATE-----
|
||
|
||
Remediation:
|
||
Use the following procedure to install a certificate and its signing certificate chain onto
|
||
your web server, load balancer, or proxy.
|
||
Step 1: Create the server's private key and a certificate signing request.
|
||
The following command will create your certificate's private key with 4096-bit key
|
||
strength. It will also output your certificate signing request to the nginx.csr file in your
|
||
present working directory.
|
||
openssl req -new -newkey rsa:4096 -keyout nginx.key -out nginx.csr
|
||
Enter the below information about your private key:
|
||
Country Name (2 letter code) [XX]: Your Country
|
||
State or Province Name (full name) []: Your State
|
||
Locality Name (eg, city) [Default City]: Your City
|
||
Organization Name (eg, company) [Default Company Ltd]: Your City
|
||
Organizational Unit Name (eg, section) []: Your Organizational Unit
|
||
Common Name (eg, your name or your server's hostname) []: Your server's DNS
|
||
name
|
||
Email Address []: Your email address
|
||
Step 2: Obtain a signed certificate from your certificate authority.
|
||
Provide your chosen certificate authority with your certificate signing request. Follow
|
||
your certificate authority's signing procedures in order to obtain a certificate and the
|
||
certificate's trust chain. A full trust chain is typically delivered in .pem format.
|
||
Step 3: Install certificate and signing certificate chain on your web server.
|
||
Place the .pem file from your certificate authority into the directory of your choice.
|
||
Locate your created key file from the command you used to generate your certificate
|
||
signing request. Open your website configuration file and edit your encrypted listener to
|
||
leverage the ssl_certificate and ssl_certificate_key directives for a web server as
|
||
shown below. You should also inspect include files inside your nginx.conf. This should
|
||
be part of the server block.
|
||
|
||
|
||
|
||
|
||
Page 86
|
||
server {
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
ssl_certificate /etc/nginx/cert.crt;
|
||
ssl_certificate_key /etc/nginx/nginx.key;
|
||
...
|
||
}
|
||
After editing this file, you must restart the nginx systemd service for these changes to
|
||
take effect. This can be done with the following command:
|
||
sudo systemctl restart nginx
|
||
|
||
Default Value:
|
||
No certificate is installed by default.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/configuring_https_servers.html#chains
|
||
2. https://support.globalsign.com/customer/portal/articles/1290470-install-certificate-
|
||
--nginx
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 87
|
||
4.1.3 Ensure private key permissions are restricted (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
The server's and potentially its vhost's private keys should be protected from
|
||
unauthorized access by limiting access based on the principle of least privilege.
|
||
Rationale:
|
||
A server's private key file should be restricted to 400 permissions. This ensures only the
|
||
owner of the private key file can access it. This is the minimum necessary permissions
|
||
for the server to operate. If the private key file is not protected, an unauthorized user
|
||
with access to the server may be able to find the private key file and use it to decrypt
|
||
traffic sent to your server.
|
||
Audit:
|
||
Verify the permissions on the key file are 400. This can be found by running the
|
||
following command. You should replace /etc/nginx/nginx.key with the location of
|
||
your key file.
|
||
find /etc/nginx/ -name '*.key' -exec stat -Lc "%n %a" {} +
|
||
The output should show mode 400 or more restrictive
|
||
Example:
|
||
/etc/nginx/nginx.key 400
|
||
|
||
Remediation:
|
||
Run the following command to remove excessive permissions on key files in the
|
||
/etc/nginx/ directory.
|
||
Note: The directory /etc/nginx/ should be replaced with the location of your key file.
|
||
find /etc/nginx/ -name '*.key' -exec chmod u-wx,go-rwx {} +
|
||
|
||
Default Value:
|
||
The default permissions on the server's private key are 644 or -rw-r--r--.
|
||
|
||
|
||
|
||
|
||
Page 88
|
||
Additional Information:
|
||
Important Note: This recommendation should be applied to both the keys of your
|
||
server certificate and the key of your client certificate if you are looking to mutually
|
||
authenticate a proxy server.
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.3 Configure Data Access Control Lists
|
||
v8 Configure data access control lists based on a user’s need to know. Apply data
|
||
access control lists, also known as access permissions, to local and remote file
|
||
● ● ●
|
||
systems, databases, and applications.
|
||
|
||
|
||
14.6 Protect Information through Access Control Lists
|
||
Protect all information stored on systems with file system, network share,
|
||
v7 claims, application, or database specific access control lists. These controls will
|
||
enforce the principle that only authorized individuals should have access to the
|
||
● ● ●
|
||
information based on their need to access the information as a part of their
|
||
responsibilities.
|
||
|
||
|
||
|
||
|
||
Page 89
|
||
4.1.4 Ensure only modern TLS protocols are used (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
Only modern TLS protocols should be enabled in NGINX for all client connections and
|
||
upstream connections. Removing legacy TLS and SSL protocols (SSL 3.0, TLS 1.0, 1.1
|
||
and 1.2), and enable stable TLS protocols (TLS 1.3), ensures users are able to take
|
||
advantage of strong security capabilities and protects them from insecure legacy
|
||
protocols.
|
||
Rationale:
|
||
Why disable SSL 3.0: The POODLE Vulnerability allowed attackers to exploit SSL 3.0
|
||
to obtain cleartext information by exploiting weaknesses in CBC in 2014. SSL 3.0 is also
|
||
no longer FIPS 140-2 compliant.
|
||
Why disable TLS 1.0: TLS 1.0 was deprecated from use when PCI DSS Compliance
|
||
mandated that it not be used for any applications processing credit card numbers in
|
||
June 2018. TLS 1.0 does not make use of modern protections, and almost all user
|
||
agents that do not support TLS 1.2 or higher are no longer supported by their vendor.
|
||
Why disable TLS 1.1: Because of the increased security associated with higher
|
||
versions of TLS, TLS 1.0 should be disabled. Modern browsers will begin to flag TLS
|
||
1.1 as deprecated in early 2019.
|
||
Why disable TLS 1.2: While robust for its time, TLS 1.2's complexity allows for weak
|
||
configurations, including cipher suites that lack Perfect Forward Secrecy. TLS 1.3
|
||
eliminates this risk by mandating PFS and removing outdated cryptographic primitives.
|
||
Acknowledging this, NIST SP 800-52 Rev. 2 allows for TLS 1.2 to be disabled if it is not
|
||
required for interoperability, positioning TLS 1.3 as the sole recommended protocol for
|
||
modern, secure environments.
|
||
Why enable TLS 1.3: TLS 1.3 improves security by removing several insecure cipher
|
||
suites by default and adding several more secure algorithms. All public-key exchange
|
||
mechanisms support perfect forward secrecy in this version of TLS. Additionally, TLS
|
||
1.3 makes drastic performance improvements by removing a full round trip in the TLS
|
||
handshake.
|
||
|
||
|
||
|
||
|
||
Page 90
|
||
Impact:
|
||
Disabling certain TLS may not allow legacy user agents to connect to your server.
|
||
Disabling negotiation of specific protocols with your backend server may also limit your
|
||
ability to connect with legacy servers. You should always consider if you need to
|
||
support legacy user agents or servers when selecting your TLS protocols.
|
||
Audit:
|
||
You can verify which SSL/TLS protocols your server uses by issuing the below
|
||
command to see the configured cipher suites on the server. If anything older than TLS
|
||
1.3 is implemented or nothing appears, this recommendation is not implemented.
|
||
grep -ir ssl_protocol /etc/nginx
|
||
Note: Depending on your configuration, you may see different results. The directive
|
||
ssl_protocols should always be part of your server block. If your NGINX server is
|
||
also a proxy or load balancer, you should also check for the presence of the
|
||
proxy_ssl_protocols directive as part of the location block of your nginx
|
||
configuration. This ensures your proxy follows a specific set of negotiation rules for
|
||
encrypting traffic with your upstream server.
|
||
Remediation:
|
||
Run the following commands to change your ssl_protocols if they are already
|
||
configured. This remediation advice assumes your nginx configuration file does not
|
||
include server configuration outside of /etc/nginx/nginx.conf. You may have to also
|
||
inspect the include files in your nginx.conf to ensure this is properly implemented.
|
||
Web Server:
|
||
sed -i "s/ssl_protocols[^;]*;/ssl_protocols TLSv1.3;/" /etc/nginx/nginx.conf
|
||
Proxy:
|
||
sed -i "s/proxy_ssl_protocols[^;]*;/proxy_ssl_protocols TLSv1.3;/"
|
||
/etc/nginx/nginx.conf
|
||
If your ssl_protocols are not already configured, this can be accomplished manually
|
||
by opening your web server or proxy server configuration file and manually adding the
|
||
directives.
|
||
Web Server:
|
||
server {
|
||
ssl_protocols TLSv1.3;
|
||
}
|
||
|
||
|
||
|
||
|
||
Page 91
|
||
Proxy:
|
||
location / {
|
||
proxy_pass cisecurity.org;
|
||
proxy_ssl_protocols TLSv1.3;
|
||
}
|
||
|
||
Default Value:
|
||
By default, NGINX does not specify the TLS protocol and accepts all TLS versions,
|
||
except for TLS 1.3, which must be enabled by an administrator to take effect.
|
||
Defaults: ssl_protocols TLSv1.0 TLSv1.1 TLSv1.2 proxy_ssl_protocols TLSv1.0
|
||
TLSv1.1 TLSv1.2
|
||
References:
|
||
|
||
1. https://webkit.org/blog/8462/deprecation-of-legacy-tls-1-0-and-1-1-versions/
|
||
2. https://www.cloudflare.com/learning/ssl/why-use-tls-1.3/
|
||
3. https://tools.ietf.org/html/rfc8446
|
||
4. https://doi.org/10.6028/NIST.SP.800-52r2
|
||
|
||
Additional Information:
|
||
Note: TLS configuration should always be set to your organizational policy. This
|
||
recommendation is designed to meet best practices.
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 92
|
||
4.1.5 Disable weak ciphers (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
The ssl_protocols directive must be used to disable weak protocols and exclusively
|
||
enable TLS 1.3. In a TLS 1.3-only configuration, the ssl_ciphers directive is no longer
|
||
necessary, as the protocol itself mandates a small, non-negotiable set of highly secure
|
||
AEAD ciphers (Authenticated Encryption with Associated Data). This approach
|
||
simplifies configuration and eliminates the risk of choosing weak or insecure cipher
|
||
suites.
|
||
The ssl_prefer_server_ciphers directive should be set to off for TLS 1.3. Since all
|
||
available ciphers are secure, allowing the client (user agent) to choose the most
|
||
performant cipher for its hardware provides a performance benefit without
|
||
compromising security.
|
||
In a reverse proxy setup, it is critical to ensure that any upstream services also support
|
||
TLS 1.3. If an upstream server requires an older protocol, the proxy_ssl_protocols
|
||
and proxy_ssl_ciphers directives must be configured to match the upstream's
|
||
requirements, but this should be treated as a temporary exception to be remediated.
|
||
Rationale:
|
||
Weak cryptographic ciphers can lead to the compromise of sensitive data. In modern
|
||
TLS configurations, the most effective way to disable all weak ciphers is to exclusively
|
||
enable the TLS 1.3 protocol. The TLS 1.3 specification removes all previously known
|
||
weak and legacy cipher suites, mandating the use of a small set of highly secure
|
||
Authenticated Encryption (AEAD) ciphers. This approach is simpler and less error-prone
|
||
than maintaining a complex denylist or allowlist of ciphers for older protocols.
|
||
Impact:
|
||
Impact of Mandating TLS 1.3:
|
||
Enforcing an exclusive TLS 1.3 configuration enhances security but will intentionally
|
||
cause connection failures for legacy clients. This primarily affects:
|
||
|
||
• Unsupported User Agents: Clients on outdated operating systems (e.g., Android
|
||
< 10, iOS < 12.2, Windows < 10 without updates) and very old browsers (e.g.,
|
||
Internet Explorer) that do not support TLS 1.3 will be unable to connect. These
|
||
clients are typically outside their vendor's security lifecycle and pose an
|
||
independent risk.
|
||
|
||
Page 93
|
||
- Legacy Upstream Services: In a reverse proxy scenario, if NGINX is configured for
|
||
TLS 1.3 exclusively, it will be unable to establish a connection to an upstream (backend)
|
||
server that only supports TLS 1.2 or older. This will cause service disruptions. A
|
||
thorough assessment of backend compatibility is required before enforcement.
|
||
Audit:
|
||
Use the following procedure to verify that only ssl_protocols TLSv1.3; is used.
|
||
nginx -T 2>/dev/null | grep -E '^\s*ssl_protocols'
|
||
The output must show ssl_protocols TLSv1.3; exclusively for every relevant
|
||
configuration block (e.g., at the http or server level).
|
||
Example output:
|
||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||
# configuration file /etc/nginx/nginx.conf:
|
||
ssl_protocols TLSv1.3;
|
||
# configuration file /etc/nginx/conf.d/default.conf:
|
||
ssl_protocols TLSv1.3;
|
||
|
||
Remediation:
|
||
Set ssl_protocols TLSv1.3;. The ssl_ciphers directive is not required for a TLS
|
||
1.3-only configuration, as the secure defaults of the underlying crypto library (e.g.,
|
||
OpenSSL) will be used.
|
||
Default Value:
|
||
These directives are not specified by default and are set to the default of
|
||
HIGH:!aNULL:!MD5.
|
||
|
||
References:
|
||
|
||
1. https://mozilla.github.io/server-side-tls/ssl-config-generator/
|
||
2. https://nginx.org/en/docs/http/ngx_http_ssl_module.html
|
||
|
||
Additional Information:
|
||
Note: TLS configuration should always be set to your organizational policy. This
|
||
recommendation is designed to meet best practices and offers several profiles your
|
||
organization may align to.
|
||
|
||
|
||
|
||
|
||
Page 94
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 95
|
||
4.1.6 Ensure awareness of TLS 1.3 new Diffie-Hellman
|
||
parameters (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
This control is not applicable to environments exclusively using TLS 1.3.
|
||
Rationale:
|
||
The TLS 1.3 protocol (RFC 8446) deprecates the use of custom finite-field Diffie-
|
||
Hellman (DHE) groups, which were configured via the ssl_dhparam directive in NGINX.
|
||
Instead, TLS 1.3 exclusively uses a set of pre-defined, standardized, and secure elliptic
|
||
curve (ECDHE) and finite-field (FFDHE) groups for its key exchange mechanism. This
|
||
design eliminates the risk associated with weak or misconfigured custom DH
|
||
parameters. As such, the ssl_dhparam directive has no effect in a TLS 1.3-only
|
||
configuration.
|
||
Impact:
|
||
None for a TLS 1.3-only configuration.
|
||
Audit:
|
||
Verify that the configuration does not rely on TLS 1.2 or older protocols that would
|
||
require this setting. This control is implicitly passed if control 4.1.4 ("Ensure only modern
|
||
TLS protocols are used") is configured for TLS 1.3 exclusively.
|
||
Remediation:
|
||
No remediation is necessary. Ensure ssl_protocols TLSv1.3; is set. The
|
||
ssl_dhparam directive should be removed as it is obsolete.
|
||
|
||
References:
|
||
|
||
1. https://datatracker.ietf.org/doc/html/rfc8446#page-95
|
||
2. https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
|
||
|
||
|
||
|
||
|
||
Page 96
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 97
|
||
4.1.7 Ensure Online Certificate Status Protocol (OCSP) stapling is
|
||
enabled (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
OCSP stapling allows a server to efficiently deliver certificate revocation information to
|
||
the client, improving performance and privacy. The server caches the OCSP response
|
||
from the Certificate Authority (CA), eliminating the need for the client to make a
|
||
separate connection. For robust security, certificates should be issued with the OCSP
|
||
Must-Staple extension, which transforms the traditional "soft-fail" behavior into a "hard-
|
||
fail," ensuring clients always receive and validate a current revocation status.
|
||
Rationale:
|
||
OCSP stapling is a critical mechanism for distributing certificate revocation status.
|
||
Without it, clients might not be aware that a server's certificate has been compromised,
|
||
allowing for potential man-in-the-middle attacks. The OCSP Must-Staple extension is
|
||
essential as it mitigates the inherent weakness of optional OCSP ("soft-fail"), where a
|
||
browser might proceed with a connection if it doesn't receive a staple. By enforcing a
|
||
"hard-fail", Must-Staple ensures that a compromised certificate can be reliably blocked.
|
||
Impact:
|
||
If OCSP Must-Staple is used, a misconfiguration on the server (e.g., a firewall blocking
|
||
outbound OCSP queries, an incorrect DNS resolver) will cause clients to reject the
|
||
certificate and refuse to connect, leading to a service outage. This "hard-fail" behavior is
|
||
intentional for security but requires diligent configuration and monitoring of the OCSP
|
||
stapling mechanism.
|
||
|
||
|
||
|
||
|
||
Page 98
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for the four
|
||
required directives:
|
||
nginx -T 2>/dev/null | grep -E
|
||
'^\s*(ssl_stapling|ssl_stapling_verify|ssl_trusted_certificate|resolver)'
|
||
Verify that the output contains the following directives with appropriate values within the
|
||
relevant http or server blocks:
|
||
|
||
• ssl_stapling on;
|
||
• ssl_stapling_verify on;
|
||
• ssl_trusted_certificate /path/to/your/chain.pem;
|
||
• resolver A.B.C.D;
|
||
|
||
If any of these four directives are missing, this recommendation is not fully
|
||
implemented.
|
||
Remediation:
|
||
Follow this procedure to enable a robust OCSP stapling configuration:
|
||
|
||
1. When issuing a certificate, request that the OCSP Must-Staple extension be
|
||
included.
|
||
2. Edit your NGINX configuration to include all four necessary directives. The
|
||
ssl_trusted_certificate must point to a file containing your root and
|
||
intermediate certificates. The resolver must be set to one or more trusted DNS
|
||
resolvers.
|
||
|
||
# Example for a server block
|
||
server {
|
||
# ... other directives ...
|
||
|
||
# OCSP Stapling
|
||
ssl_stapling on;
|
||
ssl_stapling_verify on;
|
||
|
||
# Path to the certificate chain (Root CA + Intermediates) for
|
||
verification
|
||
ssl_trusted_certificate /etc/nginx/ssl/full_chain.pem;
|
||
|
||
# DNS resolver for NGINX to query the CA's OCSP server
|
||
resolver 8.8.8.8 1.1.1.1 valid=300s;
|
||
}
|
||
|
||
Default Value:
|
||
OCSP stapling is not enabled by default.
|
||
|
||
|
||
|
||
|
||
Page 99
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling
|
||
2. https://datatracker.ietf.org/doc/html/rfc6066
|
||
3. https://datatracker.ietf.org/doc/html/rfc7633
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 100
|
||
4.1.8 Ensure HTTP Strict Transport Security (HSTS) is enabled
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
HTTP Strict Transport Security (HSTS) is a critical security header that instructs
|
||
browsers to communicate with a domain exclusively over HTTPS. A comprehensive
|
||
HSTS policy must include the includeSubDomains directive to apply the policy to all
|
||
current and future subdomains. For maximum protection, the policy should also contain
|
||
the preload directive, allowing the domain to be submitted to browser-pre-load lists.
|
||
This ensures that even the very first connection to the domain is made securely. The
|
||
max-age should be set to a long duration, typically two years (63072000 seconds), to
|
||
ensure browsers enforce this policy persistently.
|
||
Rationale:
|
||
HSTS is the primary mechanism to mitigate protocol downgrade attacks and cookie
|
||
hijacking. By enforcing HTTPS, it prevents attackers from intercepting requests and
|
||
manipulating them. The includeSubDomains directive is vital as it closes a significant
|
||
gap where an attacker could otherwise target a non-secure subdomain. The preload
|
||
directive provides protection by removing the initial window of opportunity for an attack
|
||
on a user's first visit, as the browser already knows to use HTTPS before making any
|
||
connection.
|
||
Impact:
|
||
Once an HSTS policy with a long max-age is set, there is effectively "no going back." If
|
||
any part of your site or any subdomain cannot be served over HTTPS, users with a
|
||
cached HSTS policy will be unable to access it. Enabling includeSubDomains requires
|
||
a commitment that all subdomains of the domain will support HTTPS. Submitting a
|
||
domain to the HSTS preload list is a long-term commitment and removal is a slow,
|
||
manual process. Careful planning and testing with short max-age values are essential
|
||
before full deployment.
|
||
|
||
|
||
|
||
|
||
Page 101
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for the
|
||
Strict-Transport-Security header:
|
||
nginx -T 2>/dev/null | grep -i 'Strict-Transport-Security'
|
||
Verify that the output includes a header with the following components:
|
||
|
||
• A max-age directive of at least 31536000 (one year), with 63072000 (two years)
|
||
being the recommended value.
|
||
• The includeSubDomains directive.
|
||
• The always parameter at the end of the add_header directive.
|
||
|
||
Example output:
|
||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"
|
||
always;
|
||
|
||
Remediation:
|
||
It is critical to deploy HSTS incrementally to avoid locking users out.
|
||
Step 1: Initial Rollout (Low max-age)
|
||
Add the HSTS header with a very short max-age to test for any issues. Verify that all
|
||
parts of your site, including all subdomains, function correctly over HTTPS.
|
||
# Test with 5 minutes
|
||
add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;
|
||
Step 2: Increase max-age
|
||
Once confident, gradually increase the max-age.
|
||
# Increase to 1 week
|
||
add_header Strict-Transport-Security "max-age=604800; includeSubDomains"
|
||
always;
|
||
Step 3: Full Deployment (Long max-age and Preload)
|
||
After thorough testing (e.g., one month), set the max-age to the recommended final
|
||
value of two years. Add the preload directive if you intend to submit your site to the
|
||
HSTS preload list.
|
||
# Final configuration (2 years)
|
||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains;
|
||
preload" always;
|
||
If preloading is desired, submit your domain at hstspreload.org.
|
||
Default Value:
|
||
HSTS headers are not set by default.
|
||
|
||
|
||
|
||
Page 102
|
||
References:
|
||
|
||
1. https://hstspreload.org
|
||
2. https://tools.ietf.org/html/rfc6797
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 103
|
||
4.1.9 Ensure upstream server traffic is authenticated with a client
|
||
certificate (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
In a reverse proxy configuration, NGINX acts as a client when communicating with an
|
||
upstream server. To secure this server-to-server connection based on a Zero Trust
|
||
principle, mutual TLS (mTLS) must be used. This is achieved by configuring NGINX to
|
||
present its own client certificate to the upstream server. The upstream server then
|
||
authenticates NGINX based on this certificate, ensuring that only trusted proxies can
|
||
access backend services.
|
||
Rationale:
|
||
Authenticating the proxy's connection to the upstream server via a client certificate
|
||
provides strong, cryptographic proof of identity. This is vastly superior to weaker
|
||
authentication methods like IP whitelisting, which can be spoofed. In a modern
|
||
microservices or cloud environment, mTLS is a cornerstone of network security, as it
|
||
prevents unauthorized services from making requests to sensitive backends, thereby
|
||
mitigating lateral movement attacks.
|
||
Impact:
|
||
Implementing mTLS introduces operational overhead for certificate management. You
|
||
must have a process (often an internal Public Key Infrastructure, or PKI) for issuing,
|
||
renewing, and revoking these client certificates. If the client certificate used by NGINX
|
||
expires, or if the CA certificate on the upstream server expires, the connection between
|
||
NGINX and the upstream will fail, leading to a service outage.
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for the
|
||
required directives:
|
||
nginx -T 2>/dev/null | grep -E
|
||
'^\s*(proxy_ssl_certificate|proxy_ssl_certificate_key)'
|
||
Verify that the output includes both proxy_ssl_certificate and
|
||
proxy_ssl_certificate_key directives with the correct paths within the relevant
|
||
location block.
|
||
Note: A complete audit is two-sided. You must also verify that the upstream server is
|
||
configured to require and validate client certificates against a trusted CA. This part of
|
||
the audit is outside the scope of the NGINX configuration itself.
|
||
|
||
|
||
Page 104
|
||
Remediation:
|
||
Implementing mTLS requires configuration on both the NGINX proxy (the client) and the
|
||
upstream server (the server). This example assumes you have a simple internal CA.
|
||
Prerequisite: Create a CA
|
||
# Create CA Key
|
||
openssl genrsa -out my-ca.key 4096
|
||
# Create CA Certificate
|
||
openssl req -x509 -new -nodes -key my-ca.key -sha256 -days 3650 -out my-
|
||
ca.crt
|
||
Step 1: Configure the Upstream Server to Require Client Certificates
|
||
The upstream server must be configured to request and verify client certificates against
|
||
your CA. (If your upstream is also NGINX, the config would look like this):
|
||
# On the Upstream Server's configuration
|
||
server {
|
||
listen 443 ssl;
|
||
# ... other ssl directives ...
|
||
|
||
ssl_client_certificate /path/to/my-ca.crt; # The CA to verify against
|
||
ssl_verify_client on; # Require a valid client cert
|
||
}
|
||
Step 2: Create and Sign a Client Certificate for NGINX
|
||
On your NGINX proxy, create a key and a certificate signing request (CSR).
|
||
# Create a key for the NGINX proxy
|
||
openssl genrsa -out nginx-client.key 4096
|
||
# Create CSR
|
||
openssl req -new -key nginx-client.key -out nginx-client.csr
|
||
Sign this CSR with your CA to create the client certificate:
|
||
openssl x509 -req -in nginx-client.csr -CA my-ca.crt -CAkey my-ca.key -
|
||
CAcreateserial -out nginx-client.crt -days 365
|
||
Step 3: Configure NGINX to Present its Client Certificate
|
||
In your NGINX reverse proxy configuration, use the generated client certificate and key.
|
||
# In your reverse proxy's location block
|
||
location /api/ {
|
||
proxy_pass https://your-upstream-server;
|
||
|
||
# Present this client cert to the upstream
|
||
proxy_ssl_certificate /etc/nginx/ssl/nginx-client.crt;
|
||
proxy_ssl_certificate_key /etc/nginx/ssl/nginx-client.key;
|
||
}
|
||
|
||
Default Value:
|
||
This is not authenticated by default.
|
||
|
||
|
||
Page 105
|
||
References:
|
||
|
||
1. https://docs.nginx.com/nginx-instance-manager/system-configuration/secure-
|
||
traffic/
|
||
2. https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 106
|
||
4.1.10 Ensure the upstream traffic server certificate is trusted
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Proxy
|
||
• Level 2 - Loadbalancer
|
||
|
||
Description:
|
||
When acting as a reverse proxy, NGINX must be configured to function as a secure
|
||
TLS client. This requires validating the identity of the upstream server by verifying its
|
||
certificate against a trusted Certificate Authority (CA). Furthermore, NGINX must
|
||
ensure that the hostname of the upstream server matches the name (Subject Name /
|
||
SAN) within the certificate itself.
|
||
Rationale:
|
||
Without proper validation, NGINX blindly trusts the identity of the upstream server,
|
||
making it vulnerable to man-in-the-middle (MitM) attacks within the internal network. An
|
||
attacker could impersonate a legitimate backend service and intercept sensitive traffic.
|
||
By enforcing certificate validation (proxy_ssl_verify) and hostname verification
|
||
(proxy_ssl_name), NGINX guarantees that it is communicating with the intended,
|
||
authentic upstream server.
|
||
Impact:
|
||
Enabling strict upstream verification introduces a dependency on certificate lifecycle
|
||
management. If the upstream server's certificate expires or is misconfigured, or if the
|
||
CA certificate file on the NGINX server is outdated, NGINX will refuse to connect to the
|
||
upstream, resulting in a service outage. This behavior is intentional and secures the
|
||
connection at the cost of requiring careful certificate management.
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for the
|
||
three required directives within the relevant proxy location block:
|
||
nginx -T 2>/dev/null | grep -E
|
||
'^\s*(proxy_ssl_verify|proxy_ssl_trusted_certificate|proxy_ssl_name)'
|
||
Verify that the output contains the following directives:
|
||
|
||
• proxy_ssl_verify on;
|
||
• proxy_ssl_trusted_certificate /path/to/ca.crt;
|
||
• proxy_ssl_name your-upstream-hostname.com;
|
||
|
||
|
||
|
||
|
||
Page 107
|
||
If any of these three directives are missing, this recommendation is not fully
|
||
implemented. Additionally, manually verify that the certificate referenced by
|
||
proxy_ssl_trusted_certificate is the correct, valid CA certificate for your upstream
|
||
services.
|
||
Remediation:
|
||
To securely configure upstream validation, you must obtain the CA certificate that
|
||
signed your upstream server's certificate.
|
||
|
||
1. Place the CA certificate (e.g., upstream_ca.crt) in a secure directory on the
|
||
NGINX server (e.g., /etc/nginx/ssl/).
|
||
2. In the location block that proxies traffic, add the following three directives. The
|
||
proxy_ssl_name must match the hostname used in the proxy_pass directive.
|
||
|
||
location /api/ {
|
||
proxy_pass https://your-upstream-hostname.com;
|
||
# 1. Enable verification
|
||
proxy_ssl_verify on;
|
||
|
||
# 2. Specify the CA to trust for verification
|
||
proxy_ssl_trusted_certificate /etc/nginx/ssl/upstream_ca.crt;
|
||
|
||
# 3. Verify the certificate's name matches the server's hostname
|
||
proxy_ssl_name your-upstream-hostname.com;
|
||
}
|
||
|
||
Default Value:
|
||
By default, proxy_ssl_verify is set to off. This means NGINX does not validate the
|
||
upstream server's certificate, creating a significant security risk equivalent to a
|
||
browser ignoring all certificate warnings.
|
||
References:
|
||
|
||
1. https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-
|
||
upstream/
|
||
2. https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_trusted_ce
|
||
rtificate
|
||
|
||
|
||
|
||
|
||
Page 108
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 109
|
||
4.1.11 Ensure Secure Session Resumption is Enabled (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
TLS 1.3 introduces a secure session resumption mechanism using Pre-Shared Keys
|
||
(PSKs) that significantly improves performance for returning clients by reducing the
|
||
handshake latency. This modern mechanism should be enabled to enhance user
|
||
experience without compromising security.
|
||
Rationale:
|
||
Unlike older TLS versions, the TLS 1.3 resumption mechanism preserves Perfect
|
||
Forward Secrecy (PFS). It accomplishes this by combining the PSK with a fresh
|
||
Ephemeral Diffie-Hellman key exchange (ECDHE) for every resumed session. This
|
||
ensures that a compromise of the resumption key does not compromise any past or
|
||
future session keys. Disabling this feature provides no security benefit and negatively
|
||
impacts performance.
|
||
Impact:
|
||
Enabling session resumption has a positive performance impact. There are no
|
||
significant negative security implications when using a TLS 1.3-only configuration.
|
||
Audit:
|
||
Run the following command to verify that ssl_session_tickets is not explicitly turned
|
||
off.
|
||
# This command should produce NO output.
|
||
grep -ir "ssl_session_tickets" /etc/nginx/ | grep -i "off"
|
||
If the command produces any output containing ssl_session_tickets off;, this
|
||
recommendation is not implemented.
|
||
|
||
|
||
|
||
|
||
Page 110
|
||
Remediation:
|
||
Ensure that ssl_session_tickets is not set to off. The recommended approach is to
|
||
remove the directive entirely, as the default value is on.
|
||
If the directive is present, either remove it or set it to on:
|
||
# REMOVE this line from your configuration:
|
||
# ssl_session_tickets off;
|
||
|
||
# OR, if you want to be explicit, ensure it is set to ON (optional):
|
||
ssl_session_tickets on;
|
||
|
||
Default Value:
|
||
ssl_session_tickets is enabled (on) by default. This is the desired and secure state
|
||
for TLS 1.3.
|
||
References:
|
||
|
||
1. https://datatracker.ietf.org/doc/html/rfc8446
|
||
2. https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_tickets
|
||
3. https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 111
|
||
4.1.12 Ensure HTTP/3.0 is used (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Webserver
|
||
Description:
|
||
HTTP/2 is the established standard for web communication, offering significant
|
||
performance benefits over HTTP/1.1 through multiplexing. For 2025 and beyond,
|
||
HTTP/3 should also be enabled. HTTP/3 operates over the QUIC protocol, which is built
|
||
on UDP, to solve head-of-line blocking, reduce connection setup time, and improve
|
||
performance on unreliable networks. Both protocols require a secure TLS 1.3
|
||
environment to function.
|
||
Rationale:
|
||
Enabling HTTP/2 provides a baseline of modern performance via stream multiplexing.
|
||
Enabling HTTP/3 provides a further competitive advantage by mitigating TCP's head-of-
|
||
line blocking and offering a faster, more reliable connection handshake, which is
|
||
especially beneficial for mobile users. A server supporting both protocols can serve the
|
||
vast majority of modern clients with the best possible performance and security. The
|
||
strong encryption requirements of both protocols naturally align with a TLS 1.3-only
|
||
policy.
|
||
Impact:
|
||
HTTP/2 has no significant negative impact as it is universally supported by modern
|
||
clients. Enabling HTTP/3 has operational considerations:
|
||
|
||
1. NGINX Build: Your NGINX binary must be compiled with HTTP/3 and QUIC
|
||
support. Standard OS packages may not include this. The repository of NGINX
|
||
itself has the http_v3 module since NGINX version 1.25.0
|
||
|
||
• Run this command and check if the http_v3 module is present
|
||
|
||
nginx -V 2>&1 | tr ' ' '\n' | grep --color=auto 'with-'
|
||
|
||
2. Firewall Configuration: You must allow UDP traffic on port 443, as HTTP/3
|
||
uses the QUIC protocol over UDP. This is a common oversight that will cause
|
||
HTTP/3 to fail.
|
||
|
||
|
||
|
||
|
||
Page 112
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration:
|
||
nginx -T 2>/dev/null | grep -E '^\s*(listen|add_header.*Alt-Svc)'
|
||
Verify the following in the output for your primary server block:
|
||
|
||
1. The TCP listen directive includes the http2 parameter: listen 443 ssl
|
||
http2;
|
||
2. A UDP listen directive with the quic parameter exists: listen 443 quic
|
||
reuseport;
|
||
3. An Alt-Svc header is being sent to advertise HTTP/3 availability: add_header
|
||
Alt-Svc 'h3=":443"; ma=63072000';
|
||
|
||
If any of these are missing, this recommendation is not fully implemented.
|
||
Remediation:
|
||
Prerequisite: Ensure your NGINX version is compiled with the --with-
|
||
http_v3_module flag.
|
||
|
||
1. Open your NGINX server configuration file.
|
||
2. In the main server block for your HTTPS site, add or modify the directives to
|
||
enable HTTP/2, HTTP/3, and advertise its availability.
|
||
3. Ensure your firewall allows UDP traffic on port 443.
|
||
|
||
server {
|
||
# 1. Enable HTTP/2 on the standard TCP listener
|
||
listen 443 ssl http2;
|
||
listen [::]:443 ssl http2;
|
||
|
||
# 2. Enable HTTP/3 on the UDP listener
|
||
listen 443 quic reuseport;
|
||
listen [::]:443 quic reuseport;
|
||
|
||
# ... other ssl directives like ssl_certificate ...
|
||
|
||
# 3. Advertise HTTP/3 availability to browsers
|
||
# The max-age (ma) is in seconds (e.g., 2 years)
|
||
add_header Alt-Svc 'h3=":443"; ma=63072000';
|
||
|
||
# Required for HTTP/3
|
||
ssl_early_data on;
|
||
}
|
||
|
||
Default Value:
|
||
By default, NGINX only enables HTTP/1.1. HTTP/2 and HTTP/3 must be explicitly
|
||
configured.
|
||
|
||
|
||
|
||
Page 113
|
||
References:
|
||
|
||
1. https://datatracker.ietf.org/doc/html/rfc9114
|
||
2. https://datatracker.ietf.org/doc/html/rfc9000
|
||
3. https://datatracker.ietf.org/doc/html/rfc9001
|
||
4. https://datatracker.ietf.org/doc/html/rfc7838
|
||
5. https://nginx.org/en/docs/quic.html
|
||
6. https://nginx.org/en/docs/http/ngx_http_v3_module.html
|
||
7. https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
3.10 Encrypt Sensitive Data in Transit
|
||
v8 Encrypt sensitive data in transit. Example implementations can include: ● ●
|
||
Transport Layer Security (TLS) and Open Secure Shell (OpenSSH).
|
||
|
||
|
||
v7 14.4 Encrypt All Sensitive Information in Transit ● ●
|
||
Encrypt all sensitive information in transit.
|
||
|
||
|
||
|
||
|
||
Page 114
|
||
5 Request Filtering and Restrictions
|
||
|
||
|
||
|
||
|
||
Page 115
|
||
5.1 Access Control
|
||
|
||
|
||
|
||
|
||
Page 116
|
||
5.1.1 Ensure allow and deny filters limit access to specific IP
|
||
addresses (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Webserver
|
||
• Level 2 - Proxy
|
||
• Level 2 - Loadbalancer
|
||
Description:
|
||
Access control based on IP addresses is a fundamental defense-in-depth mechanism.
|
||
By using NGINX's allow and deny directives, access to the entire server or specific
|
||
location blocks can be restricted to trusted network sources, such as internal subnets,
|
||
specific hosts, or VPN ranges. This is particularly effective for protecting non-public
|
||
administrative interfaces or internal APIs from the public internet.
|
||
Rationale:
|
||
Applying the principle of least privilege at the network layer is a highly effective security
|
||
measure. By explicitly defining which IP addresses or CIDR ranges are permitted to
|
||
access sensitive resources and implicitly denying all others with deny all;, the attack
|
||
surface is significantly reduced. This prevents unauthorized network segments from
|
||
even attempting to exploit potential application-layer vulnerabilities.
|
||
Impact:
|
||
A misconfigured IP filter list can lead to service denial for legitimate users or services. In
|
||
dynamic environments where IP addresses can change (e.g., cloud instances without
|
||
static IPs), this can be a particular challenge. Maintaining accurate and up-to-date IP
|
||
allow-lists requires operational discipline.
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for allow
|
||
and deny rules:
|
||
nginx -T 2>/dev/null | grep -E '^\s*(allow|deny)'
|
||
Then, manually review the active rules within the http, server, or location blocks.
|
||
Verify that the configured IP addresses and CIDR ranges align with the documented list
|
||
of trusted sources and that a deny all; rule is present to enforce a default-deny policy.
|
||
|
||
|
||
|
||
|
||
Page 117
|
||
Remediation:
|
||
Identify the specific location block you wish to protect (e.g., an admin login page or
|
||
internal stats). Compile a list of trusted source IP addresses and network ranges. Add
|
||
allow directives for each trusted source, followed by a final deny all; directive.
|
||
NGINX processes rules in order, and stops at the first match.
|
||
location /admin_login/ {
|
||
# Allow a specific monitoring server
|
||
allow 192.168.1.100;
|
||
|
||
# Allow the internal office network range
|
||
allow 10.20.30.0/24;
|
||
|
||
# Deny all other access to this location
|
||
deny all;
|
||
|
||
# ... other directives for the admin location, e.g., proxy_pass ...
|
||
}
|
||
|
||
Default Value:
|
||
By default, no IP-based restrictions are configured. NGINX will process requests from
|
||
any source IP address unless allow or deny directives are specified.
|
||
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_access_module.html
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
13.10 Perform Application Layer Filtering
|
||
v8 Perform application layer filtering. Example implementations include a filtering ●
|
||
proxy, application layer firewall, or gateway.
|
||
|
||
|
||
9.5 Implement Application Firewalls
|
||
v7 Place application firewalls in front of any critical servers to verify and validate
|
||
the traffic going to the server. Any unauthorized traffic should be blocked and
|
||
●
|
||
logged.
|
||
|
||
|
||
|
||
|
||
Page 118
|
||
5.1.2 Ensure only approved HTTP methods are allowed (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
|
||
Description:
|
||
Following the principle of least functionality, an NGINX server should be configured to
|
||
reject any HTTP methods that are not explicitly required by the application. While
|
||
standard web browsing typically only needs GET, POST, and HEAD, modern RESTful APIs
|
||
might require methods like PUT, PATCH, or DELETE. Any method not essential for the
|
||
application's functionality should be blocked at the web server level.
|
||
Rationale:
|
||
Disabling unused HTTP methods mitigates the risk of unintended server interaction and
|
||
can prevent certain classes of web application attacks. For example, if an attacker finds
|
||
a way to bypass application-layer authentication, an enabled but unused PUT or DELETE
|
||
method on the web server could potentially lead to unauthorized file modification or
|
||
deletion. By explicitly denying such methods, NGINX ensures that requests never even
|
||
reach the backend application and therefore significantly reducing the attack surface.
|
||
Impact:
|
||
An overly restrictive filter can block legitimate application functionality. Before
|
||
implementing these restrictions, it is crucial to coordinate with application developers to
|
||
get a definitive list of all required HTTP methods for every application endpoint.
|
||
Incorrectly blocking a required method (e.g., PUT for a file upload feature) will cause
|
||
parts of the application to fail.
|
||
Audit:
|
||
1. Configuration Inspection:
|
||
Run the following command to analyze the fully loaded NGINX configuration for any
|
||
method-limiting directives:
|
||
nginx -T 2>/dev/null | grep -E '(\$request_method|limit_except)'
|
||
Review the configuration to ensure that either a limit_except block or an if
|
||
($request_method) block is correctly implemented for the relevant location.
|
||
|
||
|
||
|
||
|
||
Page 119
|
||
2. Active Testing:
|
||
Send a request with a non-approved method (e.g., OPTIONS or DELETE) using curl and
|
||
verify that the server responds with the correct status code, not a 200 OK or 404 Not
|
||
Found.
|
||
# Send a disallowed OPTIONS request
|
||
curl -X OPTIONS -I https://example.loc/api.html
|
||
Expected Output: The server should return either HTTP/1.1 405 Not Allowed or
|
||
HTTP/1.1 444 Connection Closed Without Response. Any other 2xx or 4xx code
|
||
indicates a potential misconfiguration.
|
||
Remediation:
|
||
There are two recommended methods to restrict HTTP verbs.
|
||
Method 1 (Preferred): Using limit_except This directive is designed for this purpose
|
||
and is considered the cleanest approach. It restricts all methods except for the ones
|
||
listed.
|
||
|
||
location /api_login/ {
|
||
|
||
# Only allow GET, HEAD, and POST methods for this location.
|
||
limit_except GET HEAD POST {
|
||
deny all;
|
||
}
|
||
|
||
# ... other directives ...
|
||
}
|
||
Method 2 (Alternative): Using an if condition This method offers more flexibility, such
|
||
as returning a non-standard status code like 444, which simply closes the connection
|
||
without sending a response header.
|
||
location / {
|
||
|
||
# If the request method is NOT one of GET, HEAD, or POST
|
||
if ($request_method !~ ^(GET|HEAD|POST)$) {
|
||
# --> close the connection immediately.
|
||
return 444;
|
||
}
|
||
|
||
# ... other directives ...
|
||
}
|
||
|
||
Default Value:
|
||
All methods are allowed.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_except
|
||
|
||
|
||
Page 120
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.10 Apply Secure Design Principles in Application
|
||
Architectures
|
||
Apply secure design principles in application architectures. Secure design
|
||
principles include the concept of least privilege and enforcing mediation to validate
|
||
v8 every operation that the user makes, promoting the concept of "never trust user
|
||
input." Examples include ensuring that explicit error checking is performed and
|
||
● ●
|
||
documented for all input, including for size, data type, and acceptable ranges or
|
||
formats. Secure design also means minimizing the application infrastructure attack
|
||
surface, such as turning off unprotected ports and services, removing unnecessary
|
||
programs and files, and renaming or removing default accounts.
|
||
|
||
|
||
9.2 Ensure Only Approved Ports, Protocols and Services
|
||
v7 Are Running ● ●
|
||
Ensure that only network ports, protocols, and services listening on a system
|
||
with validated business needs, are running on each system.
|
||
|
||
|
||
|
||
|
||
Page 121
|
||
5.2 Request Limits
|
||
|
||
|
||
|
||
|
||
Page 122
|
||
5.2.1 Ensure timeout values for reading the client header and
|
||
body are set correctly (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
To protect against slow clients holding connections open indefinitely, NGINX supports
|
||
several timeout directives. The most important ones for client-facing connections are:
|
||
|
||
• client_header_timeout: Sets the maximum time the server will wait for a client
|
||
to send the request header.
|
||
• client_body_timeout: Sets the maximum time allowed between sequential
|
||
read operations when receiving the request body. This timer does not apply to
|
||
the total time of the transfer.
|
||
• send_timeout: Sets the maximum time allowed between sequential write
|
||
operations when sending a response to the client.
|
||
|
||
If any of these timeouts are reached, the server closes the connection, freeing up
|
||
resources.
|
||
Rationale:
|
||
Aggressively low timeout values are a primary defense against slow-read Denial of
|
||
Service (DoS) attacks. These attacks attempt to exhaust server resources by opening
|
||
many connections and keeping them alive for as long as possible by sending data
|
||
extremely slowly. By setting low timeouts, NGINX efficiently closes these malicious
|
||
connections, preserving resources for legitimate users.
|
||
Impact:
|
||
Setting these values too low can terminate legitimate connections too early. For
|
||
example, a user uploading a large file over a slow mobile connection could be cut off if
|
||
client_body_timeout is too aggressive. The values must be carefully evaluated to
|
||
achieve a balance between security (low values) and functionality (higher values for
|
||
specific use cases such as file uploads).
|
||
|
||
|
||
|
||
|
||
Page 123
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for the
|
||
relevant directives:
|
||
nginx -T 2>/dev/null | grep -E
|
||
'^\s*(client_header_timeout|client_body_timeout|send_timeout)'
|
||
Verify that these values are explicitly set to a reasonably low, non-default value (e.g.,
|
||
10–20 seconds) in the main http or server context. If the application requires long-
|
||
running connections, verify that client_body_timeout is overridden to a higher value
|
||
in the specific location block that handles uploads.
|
||
|
||
Remediation:
|
||
Set reasonably low timeout values globally in your http block. If specific locations
|
||
require longer timeouts (e.g., for file uploads), override them within that location
|
||
block.
|
||
Example Configuration:
|
||
http {
|
||
|
||
# Set a global default of 15 seconds, which overrides the default of 60s.
|
||
|
||
client_header_timeout 15s;
|
||
client_body_timeout 15s;
|
||
send_timeout 15s;
|
||
|
||
server {
|
||
# ... other settings ...
|
||
|
||
# This location handles large file uploads and needs a longer
|
||
timeout.
|
||
location /upload {
|
||
|
||
client_body_timeout 300s; # Allow 5 minutes between read
|
||
operations for uploads
|
||
# ...
|
||
}
|
||
}
|
||
}
|
||
|
||
Default Value:
|
||
The default value for client_header_timeout, client_body_timeout, and
|
||
send_timeout is 60 seconds. This is often considered too high for public-facing
|
||
servers, because it allows slow or malicious clients to uphold connections and
|
||
resources for an extended period.
|
||
|
||
|
||
|
||
|
||
Page 124
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_timeout
|
||
2. https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_timeout
|
||
3. https://nginx.org/en/docs/http/ngx_http_core_module.html#send_timeout
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 125
|
||
5.2.2 Ensure the maximum request body size is set correctly
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The client_max_body_size directive defines the maximum permissible size for a
|
||
client request body, as indicated by the Content-Length header. If a request exceeds
|
||
this size, NGINX will immediately reject it with a 413 Request Entity Too Large
|
||
error, preventing the oversized request from being processed further or passed to a
|
||
backend application.
|
||
Rationale:
|
||
Limiting the request body size is a crucial defense against resource exhaustion DoS
|
||
attacks and prevents oversized, potentially malicious payloads from reaching
|
||
application backends. By setting a logical default limit and only increasing it for specific
|
||
application endpoints that require it (e.g., file uploads), the principle of least functionality
|
||
is enforced, significantly reducing the attack surface.
|
||
Impact:
|
||
Setting this value too low is a common cause of application failure. Legitimate user
|
||
actions, such as uploading files or submitting large forms, will be blocked with a 413
|
||
error if they exceed the configured limit. It is essential that this value is determined
|
||
based on application requirements and not set to an arbitrary low number.
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration for the
|
||
client_max_body_size directive:
|
||
nginx -T 2>/dev/null | grep -E '^\s*(client_max_body_size)'
|
||
Verify that a global client_max_body_size is set in the http block. For any location
|
||
blocks that override this value (e.g., for uploads), manually verify that the increased
|
||
limit is documented and aligns with the application's functional requirements.
|
||
|
||
|
||
|
||
|
||
Page 126
|
||
Remediation:
|
||
Define a restrictive global limit in the http block. For specific application endpoints that
|
||
need to accept larger request bodies, override this directive within the corresponding
|
||
location block.
|
||
Example Configuration:
|
||
http {
|
||
|
||
# Set a restrictive global default of 2 Megabytes. This prevents
|
||
unexpected large requests on most endpoints.
|
||
client_max_body_size 2M;
|
||
|
||
server {
|
||
# ...
|
||
|
||
# This location handles API requests with potentially large JSON
|
||
payloads.
|
||
location /api/v1/data {
|
||
client_max_body_size 10M; # Allow up to 10MB
|
||
# ...
|
||
}
|
||
|
||
# This location is for large file uploads.
|
||
location /uploads {
|
||
client_max_body_size 50M; # Allow up to 50MB
|
||
# ...
|
||
}
|
||
}
|
||
}
|
||
|
||
Default Value:
|
||
The default value is 1 megabyte (1m). It's recommended to set this value explicitly in
|
||
the http block to serve as a documented, global default. For many API endpoints or
|
||
static content locations, even this default may be more permissive than necessary.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
|
||
|
||
|
||
|
||
|
||
Page 127
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 128
|
||
5.2.3 Ensure the maximum buffer size for URIs is defined
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
• Level 1 - Proxy
|
||
• Level 1 - Loadbalancer
|
||
Description:
|
||
The large_client_header_buffers directive allocates the maximum number and
|
||
size of buffers for reading the entire client request header, which includes both the
|
||
request line (e.g., GET /path HTTP/1.1) and all subsequent header fields (e.g.,
|
||
Host:, Cookie:, Authorization:). If a client's request header exceeds the total
|
||
allocated buffer space, NGINX immediately rejects the request with a 400 Bad
|
||
Request error. Also, the client's request line cannot exceed the size of one buffer,
|
||
otherwise the 414 Request-URI Too Large error will be returned to the client.
|
||
|
||
Rationale:
|
||
While NGINX itself is not vulnerable to buffer overflows from large headers, this
|
||
directive serves two critical purposes:
|
||
1. Denial of Service (DoS) Mitigation: It prevents malicious clients from attempting to
|
||
exhaust server memory by sending excessively large headers.
|
||
2. Downstream Protection: It acts as a gatekeeper, ensuring that malformed or
|
||
oversized headers do not reach backend applications that might be more fragile or
|
||
could exhibit undefined behavior when processing them.
|
||
The default value is intentionally generous to handle legitimate modern web traffic, and
|
||
lowering it without reason can be counterproductive.
|
||
Impact:
|
||
Setting this value too low is a common cause of difficult-to-diagnose application failures.
|
||
Legitimate requests from modern web applications, especially those using complex
|
||
authentication (OAuth/SAML), or analytics, can easily generate headers larger than a
|
||
few kilobytes. An overly restrictive value will cause these legitimate requests to fail with
|
||
a generic 400 Bad Request error, which is often mistakenly attributed to the application
|
||
itself rather than the NGINX configuration.
|
||
|
||
|
||
|
||
|
||
Page 129
|
||
Audit:
|
||
This is a manual check that requires context.
|
||
1. Run the following command to see if the directive is explicitly set:
|
||
nginx -T 2>/dev/null | grep 'large_client_header_buffers'
|
||
2. Evaluate the result:
|
||
|
||
• No output: The server is using the default value of 4 8k. This is considered
|
||
compliant and secure for general use.
|
||
• Output shows large_client_header_buffers 4 8k;: The default is explicitly
|
||
set. This is compliant.
|
||
• Output shows a different value (e.g., 2 1k;): This is a deviation from the
|
||
default. Manually verify if this restrictive value is documented, intentional, and
|
||
necessary for the specific application. If not, it represents a potential availability
|
||
risk and should be flagged.
|
||
|
||
Remediation:
|
||
Unless you have a specific, documented requirement to restrict header sizes further, the
|
||
recommended action is to rely on the secure default value. If your configuration has an
|
||
overly restrictive value set without a clear reason, remove the
|
||
large_client_header_buffers directive from your http or server blocks to allow
|
||
NGINX to fall back to its default.
|
||
Remediation Action:
|
||
Remove any custom, overly restrictive large_client_header_buffers lines from your
|
||
configuration files.
|
||
# REMOVE THIS LINE if it exists without good reason:
|
||
large_client_header_buffers 2 1k;
|
||
|
||
Default Value:
|
||
The default value is large_client_header_buffers 4 8k;, meaning NGINX will
|
||
allocate up to 4x buffers of 8 kilobytes each per request. This generous default is
|
||
designed to accommodate the vast majority of legitimate web traffic, including requests
|
||
with large cookies or complex authorization headers, without problems. It is considered
|
||
the secure and recommended baseline.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_b
|
||
uffers
|
||
|
||
|
||
|
||
|
||
Page 130
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 131
|
||
5.2.4 Ensure the number of connections per IP address is limited
|
||
(Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Webserver
|
||
• Level 2 - Proxy
|
||
• Level 2 - Loadbalancer
|
||
Description:
|
||
NGINX's ngx_http_limit_conn_module provides a mechanism to limit the number of
|
||
simultaneous connections from a single client IP address. This is achieved in two steps:
|
||
|
||
• limit_conn_zone: This directive, typically defined in the http block, creates a
|
||
shared memory zone to store the state for each client IP address.
|
||
• limit_conn: This directive, applied in a server or location block, enforces a
|
||
specific connection limit using the previously defined zone.
|
||
|
||
When a client exceeds this limit, NGINX will reject new connections with a 503
|
||
Service Temporarily Unavailable error.
|
||
|
||
Rationale:
|
||
The primary purpose of connection limiting is to mitigate resource exhaustion and
|
||
certain types of Denial of Service (DoS) attacks where an attacker opens many
|
||
connections and holds them open for as long as possible. It is a different tool than rate
|
||
limiting (limit_req), which is designed to stop rapid-fire requests (like brute-force
|
||
attacks). By enforcing a reasonable connection limit, the server can prevent a single
|
||
malicious or misconfigured client from consuming an unfair share of worker
|
||
connections.
|
||
Impact:
|
||
This is a critical consideration. A single public IP address can represent thousands of
|
||
individual users behind a large corporate NAT, a university campus, or a Carrier-Grade
|
||
NAT (CG-NAT) from a mobile provider. Setting the connection limit too low will block
|
||
legitimate users in these scenarios. The appropriate limit is a delicate balance and
|
||
depends entirely on the target audience. A public-facing website requires a much higher
|
||
limit than a restricted admin interface.
|
||
|
||
|
||
|
||
|
||
Page 132
|
||
Audit:
|
||
This is a manual check requiring context.
|
||
1. Run the following command to inspect the loaded NGINX configuration for
|
||
connection limiting rules:
|
||
nginx -T 2>/dev/null | grep -E '^\s*(limit_conn_zone|limit_conn)'
|
||
2. Manually evaluate the findings:
|
||
|
||
• Is a limit_conn_zone defined in the http block?
|
||
• Is a limit_conn directive applied in the appropriate server or location
|
||
blocks?
|
||
|
||
Critically, assess the configured limit. Is a limit of 10 appropriate for a public website
|
||
accessed by customers or large companies? Or is it a sensible value for a specific
|
||
/login location? The value must be justifiable for its context.
|
||
|
||
Remediation:
|
||
First, define a shared memory zone in the http block. Then, apply a carefully
|
||
considered limit in the server or location context.
|
||
Understanding the zone size: The memory usage for the $binary_remote_addr key
|
||
is fixed (64 bytes on a 64-bit system). Therefore, 1 megabyte of zone memory can
|
||
store approximately 16,384 states. A 10m zone can store over 160,000 states.
|
||
Example Configuration:
|
||
http {
|
||
|
||
# Define a 10MB zone named 'per_ip' to track connections by IP.
|
||
# $binary_remote_addr is more memory-efficient than $remote_addr.
|
||
limit_conn_zone $binary_remote_addr zone=per_ip:10m;
|
||
|
||
server {
|
||
|
||
# Apply a general limit of 20 connections per IP for this server.
|
||
# This might be a reasonable starting point for a public site.
|
||
limit_conn per_ip 20;
|
||
|
||
# For a resource-intensive download area, apply a stricter limit.
|
||
location /downloads/ {
|
||
limit_conn per_ip 5;
|
||
}
|
||
}
|
||
}
|
||
|
||
Default Value:
|
||
By default, no connection limits are configured. NGINX will accept as many
|
||
simultaneous connections from a single IP address as its worker processes can handle.
|
||
|
||
|
||
Page 133
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 134
|
||
5.2.5 Ensure rate limits by IP address are set (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Webserver
|
||
• Level 2 - Proxy
|
||
• Level 2 - Loadbalancer
|
||
|
||
Description:
|
||
NGINX's ngx_http_limit_req_module provides a mechanism to limit the rate of
|
||
incoming requests from a single client IP address, based on the "leaky bucket"
|
||
algorithm. This is configured in two steps:
|
||
1. limit_req_zone: This directive, defined in the http block, creates a shared memory
|
||
zone that defines the bucket's parameters, such as the average request rate.
|
||
2. limit_req: This directive, applied in a server or location block, enforces the rate
|
||
limit using a specific zone and defines how "bursts" of traffic are handled.
|
||
When a client exceeds the defined rate, NGINX will reject new requests with a 503
|
||
Service Temporarily Unavailable error.
|
||
|
||
Rationale:
|
||
Rate limiting is the primary defense against application-level, high-frequency attacks. Its
|
||
main purpose is to prevent brute-force password guessing on login endpoints, API
|
||
abuse by automated scripts, and aggressive content scraping. Unlike connection
|
||
limiting (limit_conn), which focuses on slow, simultaneous connections, rate limiting
|
||
(limit_req) targets clients making an excessive number of requests in a short period.
|
||
|
||
Impact:
|
||
Applying a global, aggressive rate limit is extremely dangerous and will almost certainly
|
||
block legitimate users accessing your site from behind a shared NAT (e.g., corporate
|
||
offices, datacenters, mobile networks). Rate limiting is a surgical tool that must be
|
||
applied only to specific, sensitive locations (e.g., /login, /api/v1/authenticate)
|
||
where abuse is likely. Incorrectly configured rate limits are a common source of user
|
||
complaints and support tickets.
|
||
Audit:
|
||
This is a manual check requiring context.
|
||
1. Run the following command to find rate-limiting rules:
|
||
nginx -T 2>/dev/null | grep -E '^\s*(limit_req_zone|limit_req)'
|
||
|
||
|
||
|
||
|
||
Page 135
|
||
2. Manually evaluate the findings:
|
||
|
||
• Is a limit_req_zone defined in the http block?
|
||
• Important, where is limit_req applied? Is it applied globally (server Block or
|
||
location /)? This is a high risk. Or is it applied to specific endpoints like
|
||
/login? Which is best practice.
|
||
• Are the rate and burst values reasonable for the protected endpoint?
|
||
|
||
Remediation:
|
||
First, define a shared memory zone in the http block. Then, apply a carefully tuned limit
|
||
only to the specific location blocks that require protection.
|
||
Understanding the "Leaky Bucket" Parameters:
|
||
|
||
• rate: The average rate at which the bucket "leaks" (requests are processed).
|
||
5r/s means 5 requests per second.
|
||
• burst: The size of the bucket. It's the number of "token" requests a client can
|
||
make in excess of the defined rate. A burst=10 allows a client to burst up to 10
|
||
requests instantly before the rate limit kicks in.
|
||
• nodelay: Without nodelay, excess requests in a burst are queued and
|
||
processed at the defined rate (slowing the client down). With nodelay, excess
|
||
requests are processed immediately as long as they fit in the burst "bucket", and
|
||
only requests beyond the burst limit are rejected. For APIs and login pages,
|
||
nodelay is generally preferred for a better user experience.
|
||
|
||
|
||
|
||
|
||
Page 136
|
||
Example Configuration:
|
||
http {
|
||
|
||
# Define a 10MB zone for login attempts.
|
||
# Rate: 1 request per second average.
|
||
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
|
||
|
||
server {
|
||
|
||
# NO global rate limit here!
|
||
|
||
location / {
|
||
# Normal traffic, no rate limit
|
||
}
|
||
|
||
# Apply a strict rate limit ONLY to the login endpoint
|
||
location /login {
|
||
|
||
# Use the 'login_limit' zone.
|
||
# Allow a burst of 5 requests, then enforce the 1r/s limit.
|
||
# 'nodelay' ensures a user can quickly resubmit a form without
|
||
being artificially slowed down.
|
||
limit_req zone=login_limit burst=5 nodelay;
|
||
|
||
# ... other settings ...
|
||
}
|
||
}
|
||
}
|
||
|
||
Default Value:
|
||
By default, no rate limits are configured. NGINX will process requests from a single IP
|
||
address as fast as the client can send them.
|
||
References:
|
||
|
||
1. https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
|
||
|
||
|
||
|
||
|
||
Page 137
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 138
|
||
5.3 Browser Security
|
||
|
||
|
||
|
||
|
||
Page 139
|
||
5.3.1 Ensure X-Content-Type-Options header is configured and
|
||
enabled (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 1 - Webserver
|
||
Description:
|
||
The X-Content-Type-Options header instructs the browser to strictly follow the MIME
|
||
types declared in the Content-Type headers and not to guess ("sniff") the content
|
||
type based on the file's actual content.
|
||
Rationale:
|
||
Implementing the X-Content-Type-Options header with the nosniff directive helps
|
||
to prevent drive-by download attacks where a user agent is sniffing content types in
|
||
responses.
|
||
This header prevents "MIME type confusion" attacks. Without this header, browsers
|
||
might interpret a file declared as text (e.g., snippet.txt) as executable if it contains
|
||
script code. Setting the nosniff directive forces the browser to reject the file if the
|
||
declared type doesn't match the context in which it's loaded (e.g., loading a text file
|
||
as a script).
|
||
Impact:
|
||
Low Risk: In rare cases, legacy applications or misconfigured servers might rely on the
|
||
browser's ability to correct wrong Content-Type headers (e.g., serving JavaScript with a
|
||
text/plain header). Enabling nosniff will break these applications because the
|
||
browser will refuse to execute the script. Ensuring correct MIME types in the NGINX
|
||
configuration (mime.types) is a prerequisite.
|
||
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration:
|
||
nginx -T 2>/dev/null | grep -i 'X-Content-Type-Options'
|
||
Verify the output:
|
||
|
||
• Verify that the configuration contains the line: add_header X-Content-Type-
|
||
Options "nosniff" always;
|
||
• Specifically check for the always parameter at the end of the directive. Without it,
|
||
the header will be missing on error pages.
|
||
|
||
|
||
|
||
|
||
Page 140
|
||
Remediation:
|
||
Open the NGINX configuration file that contains your server blocks. Add the below line
|
||
into your server block to add X-Content-Type-Options header and direct your user
|
||
agent to not sniff content types.
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
|
||
Default Value:
|
||
This header is not implemented by default.
|
||
References:
|
||
|
||
1. https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-
|
||
Content-Type-Options
|
||
2. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.ht
|
||
ml#x-content-type-options
|
||
3. https://nginx.org/en/docs/http/ngx_http_headers_module.html
|
||
|
||
Additional Information:
|
||
The always parameter ensures that the header is applied to the HTTP response no
|
||
matter what the HTTP response code is.
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 141
|
||
5.3.2 Ensure that Content Security Policy (CSP) is enabled and
|
||
configured properly (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Webserver
|
||
Description:
|
||
Content Security Policy (CSP) is an HTTP response header that allows site
|
||
administrators to declare approved sources of content that browsers are allowed to load
|
||
on that page. It is a mechanism to detect and mitigate certain types of attacks, including
|
||
Cross-Site Scripting (XSS) and data injection attacks. Furthermore, CSP's frame-
|
||
ancestors directive is the modern replacement for the X-Frame-Options header to
|
||
prevent Clickjacking.
|
||
Rationale:
|
||
A robust CSP significantly reduces the attack surface of a web application. By restricting
|
||
the domains from which scripts, styles, images, and other resources can be loaded, it
|
||
effectively neutralizes many XSS vectors. Additionally, by using the frame-ancestors
|
||
directive, it explicitly controls which parent pages are allowed to embed the
|
||
application (e.g., via <iframe>), providing a more flexible protection against
|
||
Clickjacking than the legacy X-Frame-Options header.
|
||
|
||
Impact:
|
||
Implementing a strict CSP is complex and carries a high risk of breaking application
|
||
functionality. If a legitimate resource (e.g., a CDN script, a font file, or an inline script)
|
||
is not whitelisted, the browser will block it, potentially making the site unusable. CSP
|
||
implementation should always start in "Report-Only" mode (Content-Security-
|
||
Policy-Report-Only) to gather violation reports before enforcing the policy.
|
||
|
||
Audit:
|
||
1. Run the following command to inspect the CSP configuration:
|
||
nginx -T 2>/dev/null | grep -i 'Content-Security-Policy'
|
||
2. Evaluate the policy:
|
||
|
||
• Is the header present?
|
||
• Does it include at least a restrictive default-src directive (e.g., 'self' or
|
||
'none')?
|
||
• Does it include the frame-ancestors directive to mitigate Clickjacking?
|
||
• Critically: Is unsafe-inline or unsafe-eval avoided in script-src? (Allowing
|
||
these significantly weakens the protection).
|
||
|
||
|
||
|
||
Page 142
|
||
Remediation:
|
||
CSP must be tailored to the specific application. There is no single "correct" policy.
|
||
Step 1: The Baseline Policy (High Security)
|
||
Start with a policy that denies everything by default and only allows resources from the
|
||
same origin. It also prevents the site from being framed by anyone (Clickjacking
|
||
protection).
|
||
add_header Content-Security-Policy "default-src 'self'; frame-ancestors
|
||
'self'; form-action 'self';" always;
|
||
Step 2: Adaptation (Example)
|
||
If your application loads images from a CDN and needs to be embeddable by a specific
|
||
partner site:
|
||
add_header Content-Security-Policy "default-src 'self'; img-src 'self'
|
||
https://cdn.example.com; frame-ancestors 'self' https://partner-site.com;"
|
||
always;
|
||
Note: Use Content-Security-Policy-Report-Only during the testing phase to
|
||
debug your policy without breaking the site.
|
||
Default Value:
|
||
By default, no Content Security Policy is sent. Browsers default to the standard Same-
|
||
Origin Policy, which is much less restrictive.
|
||
References:
|
||
|
||
1. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.ht
|
||
ml
|
||
2. https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sh
|
||
eet.html
|
||
3. https://developer.mozilla.org/en-
|
||
US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy
|
||
4. https://developer.mozilla.org/en-
|
||
US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy-Report-Only
|
||
5. https://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
|
||
6. https://caniuse.com/?search=frame-ancestors
|
||
|
||
|
||
|
||
|
||
Page 143
|
||
Additional Information:
|
||
OWASP HTTP Headers Cheat Sheet states:
|
||
Content Security Policy (CSP) frame-ancestors directive obsoletes X-Frame-Options
|
||
for supporting browsers (source).
|
||
X-Frame-Options header is only useful when the HTTP response where it is included
|
||
has something to interact with (e.g. links, buttons). If the HTTP response is a redirect or
|
||
an API returning JSON data, X-Frame-Options does not provide any security.
|
||
Recommendation
|
||
Use Content Security Policy (CSP) frame-ancestors directive if possible.
|
||
Almost all modern browsers do support CSP. A comprehensive overview of compatible
|
||
browsers can be found here: https://caniuse.com/?search=frame-ancestors
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
2.7 Allowlist Authorized Scripts
|
||
Use technical controls, such as digital signatures and version control, to ensure
|
||
v8 that only authorized scripts, such as specific .ps1, .py, etc., files, are allowed to ●
|
||
execute. Block unauthorized scripts from executing. Reassess bi-annually, or more
|
||
frequently.
|
||
|
||
|
||
2.9 Implement Application Whitelisting of Scripts
|
||
v7 The organization's application whitelisting software must ensure that only
|
||
authorized, digitally signed scripts (such as *.ps1, *.py, macros, etc) are allowed to
|
||
●
|
||
run on a system.
|
||
|
||
|
||
|
||
|
||
Page 144
|
||
5.3.3 Ensure the Referrer Policy is enabled and configured
|
||
properly (Manual)
|
||
Profile Applicability:
|
||
|
||
• Level 2 - Webserver
|
||
Description:
|
||
The Referrer-Policy HTTP header controls how much referrer information (sent via
|
||
the Referer header) should be included with requests. It allows site administrators to
|
||
restrict the data sent to upstream servers when a user clicks a link or loads a resource.
|
||
This is a privacy control to prevent leaking sensitive URL parameters or internal path
|
||
structures to third parties.
|
||
Rationale:
|
||
URLs often contain sensitive information such as session tokens, search queries, or
|
||
Personally Identifiable Information (PII) in their query parameters. Without a strict
|
||
Referrer Policy, this full URL is transmitted to any third-party site the user visits from
|
||
your page, potentially logging sensitive data on external servers. Configuring this
|
||
header ensures that only the necessary information (e.g., just the origin domain) is
|
||
shared, protecting user privacy and preventing data leakage.
|
||
Impact:
|
||
Choosing an overly restrictive policy like no-referrer can break functionality that relies
|
||
on knowing the source of traffic, such as web analytics, affiliate tracking, or multi-site
|
||
authentication flows. Conversely, a loose policy (unsafe-url) leaks private data. The
|
||
recommended policy strict-origin-when-cross-origin is a balance by preserving
|
||
full referrer data for internal navigation while stripping sensitive path and query data
|
||
when navigating to external sites.
|
||
Audit:
|
||
Run the following command to inspect the fully loaded NGINX configuration:
|
||
nginx -T 2>/dev/null | grep -i 'Referrer-Policy'
|
||
Evaluate the findings:
|
||
|
||
• Verify that the directive add_header Referrer-Policy "..." always; is
|
||
present.
|
||
• Check the configured value. While no-referrer is the most secure, strict-
|
||
origin-when-cross-origin is the widely accepted standard for balancing
|
||
security and functionality.
|
||
• Ensure the always parameter is present.
|
||
|
||
|
||
|
||
|
||
Page 145
|
||
Remediation:
|
||
Add the following line to your server or http block. This example uses the robust
|
||
default setting that protects privacy without breaking internal analytics.
|
||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||
If maximum privacy is required and no referrer data is needed even for internal links:
|
||
add_header Referrer-Policy "no-referrer" always;
|
||
|
||
Default Value:
|
||
NGINX does not set this header by default. However, modern browsers (Chrome,
|
||
Firefox) typically default to strict-origin-when-cross-origin if no header is
|
||
present. Explicitly setting this header ensures consistent behavior across all browsers
|
||
and prevents fallback to insecure legacy defaults.
|
||
References:
|
||
|
||
1. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.ht
|
||
ml#referrer-policy
|
||
2. https://developer.mozilla.org/en-
|
||
US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
|
||
3. https://nginx.org/en/docs/http/ngx_http_headers_module.html
|
||
|
||
CIS Controls:
|
||
|
||
Controls
|
||
Control IG 1 IG 2 IG 3
|
||
Version
|
||
|
||
16.1 Establish and Maintain a Secure Application
|
||
Development Process
|
||
Establish and maintain a secure application development process. In the
|
||
v8 process, address such items as: secure application design standards, secure coding
|
||
practices, developer training, vulnerability management, security of third-party code,
|
||
● ●
|
||
and application security testing procedures. Review and update documentation
|
||
annually, or when significant enterprise changes occur that could impact this
|
||
Safeguard.
|
||
|
||
|
||
18.1 Establish Secure Coding Practices
|
||
v7 Establish secure coding practices appropriate to the programming language and ● ●
|
||
development environment being used.
|
||
|
||
|
||
|
||
|
||
Page 146
|
||
6 Mandatory Access Control
|
||
This section is intentionally left blank / reserved.
|
||
Rationale: NGINX primarily acts as a reverse proxy and web server, relying on external
|
||
Identity Providers (IdP) or the underlying Operating System for user authentication and
|
||
authorization logic.
|
||
|
||
• For local user authentication controls (e.g., PAM, Shadow File permissions), refer
|
||
to the CIS-Benchmark™ for your Operating System.
|
||
• For network-based access controls (IP Allow/Deny), refer to Section 5 (Network
|
||
Security) of this benchmark.
|
||
• For application-level authentication (OIDC, LDAP, SAML), refer to the
|
||
documentation of the specific application or Identity Provider.
|
||
|
||
|
||
|
||
|
||
Page 147
|
||
Appendix: Summary Table
|
||
CIS Benchmark Recommendation Set
|
||
Correctly
|
||
|
||
Yes No
|
||
|
||
1 Initial Setup
|
||
|
||
1.1 Installation
|
||
|
||
1.1.1 Ensure NGINX is installed (Manual)
|
||
|
||
1.2 Configure Software Updates
|
||
|
||
1.2.1 Ensure package manager repositories are properly
|
||
configured (Manual)
|
||
|
||
1.2.2 Ensure the latest software package is installed (Manual)
|
||
|
||
2 Basic Configuration
|
||
|
||
2.1 Minimize NGINX Modules
|
||
|
||
2.1.1 Ensure only required dynamic modules are loaded
|
||
(Manual)
|
||
|
||
2.2 Account Security
|
||
|
||
2.2.1 Ensure that NGINX is run using a non-privileged,
|
||
dedicated service account (Manual)
|
||
|
||
2.2.2 Ensure the NGINX service account is locked (Manual)
|
||
|
||
2.2.3 Ensure the NGINX service account has an invalid shell
|
||
(Manual)
|
||
|
||
2.3 Permissions and Ownership
|
||
|
||
2.3.1 Ensure NGINX directories and files are owned by root
|
||
(Manual)
|
||
|
||
2.3.2 Ensure access to NGINX directories and files is
|
||
restricted (Manual)
|
||
|
||
|
||
|
||
|
||
Page 148
|
||
CIS Benchmark Recommendation Set
|
||
Correctly
|
||
|
||
Yes No
|
||
|
||
2.3.3 Ensure the NGINX process ID (PID) file is secured
|
||
(Manual)
|
||
|
||
2.4 Network Configuration
|
||
|
||
2.4.1 Ensure NGINX only listens for network connections on
|
||
authorized ports (Manual)
|
||
|
||
2.4.2 Ensure requests for unknown host names are rejected
|
||
(Manual)
|
||
|
||
2.4.3 Ensure keepalive_timeout is 10 seconds or less, but not
|
||
0 (Manual)
|
||
|
||
2.4.4 Ensure send_timeout is set to 10 seconds or less, but
|
||
not 0 (Manual)
|
||
|
||
2.5 Information Disclosure
|
||
|
||
2.5.1 Ensure server_tokens directive is set to `off` (Manual)
|
||
|
||
2.5.2 Ensure default error and index.html pages do not
|
||
reference NGINX (Manual)
|
||
|
||
2.5.3 Ensure hidden file serving is disabled (Manual)
|
||
|
||
2.5.4 Ensure the NGINX reverse proxy does not enable
|
||
information disclosure (Manual)
|
||
|
||
3 Logging
|
||
|
||
3.1 Ensure detailed logging is enabled (Manual)
|
||
|
||
3.2 Ensure access logging is enabled (Manual)
|
||
|
||
3.3 Ensure error logging is enabled and set to the info
|
||
logging level (Manual)
|
||
|
||
3.4 Ensure proxies pass source IP information (Manual)
|
||
|
||
4 Encryption
|
||
|
||
|
||
|
||
|
||
Page 149
|
||
CIS Benchmark Recommendation Set
|
||
Correctly
|
||
|
||
Yes No
|
||
|
||
4.1 TLS / SSL Configuration
|
||
|
||
4.1.1 Ensure HTTP is redirected to HTTPS (Manual)
|
||
|
||
4.1.2 Ensure a trusted certificate and trust chain is installed
|
||
(Manual)
|
||
|
||
4.1.3 Ensure private key permissions are restricted (Manual)
|
||
|
||
4.1.4 Ensure only modern TLS protocols are used (Manual)
|
||
|
||
4.1.5 Disable weak ciphers (Manual)
|
||
|
||
4.1.6 Ensure awareness of TLS 1.3 new Diffie-Hellman
|
||
parameters (Manual)
|
||
|
||
4.1.7 Ensure Online Certificate Status Protocol (OCSP)
|
||
stapling is enabled (Manual)
|
||
|
||
4.1.8 Ensure HTTP Strict Transport Security (HSTS) is
|
||
enabled (Manual)
|
||
|
||
4.1.9 Ensure upstream server traffic is authenticated with a
|
||
client certificate (Manual)
|
||
|
||
4.1.10 Ensure the upstream traffic server certificate is trusted
|
||
(Manual)
|
||
|
||
4.1.11 Ensure Secure Session Resumption is Enabled (Manual)
|
||
|
||
4.1.12 Ensure HTTP/3.0 is used (Manual)
|
||
|
||
5 Request Filtering and Restrictions
|
||
|
||
5.1 Access Control
|
||
|
||
5.1.1 Ensure allow and deny filters limit access to specific IP
|
||
addresses (Manual)
|
||
|
||
5.1.2 Ensure only approved HTTP methods are allowed
|
||
(Manual)
|
||
|
||
|
||
|
||
|
||
Page 150
|
||
CIS Benchmark Recommendation Set
|
||
Correctly
|
||
|
||
Yes No
|
||
|
||
5.2 Request Limits
|
||
|
||
5.2.1 Ensure timeout values for reading the client header and
|
||
body are set correctly (Manual)
|
||
|
||
5.2.2 Ensure the maximum request body size is set correctly
|
||
(Manual)
|
||
|
||
5.2.3 Ensure the maximum buffer size for URIs is defined
|
||
(Manual)
|
||
|
||
5.2.4 Ensure the number of connections per IP address is
|
||
limited (Manual)
|
||
|
||
5.2.5 Ensure rate limits by IP address are set (Manual)
|
||
|
||
5.3 Browser Security
|
||
|
||
5.3.1 Ensure X-Content-Type-Options header is configured
|
||
and enabled (Manual)
|
||
|
||
5.3.2 Ensure that Content Security Policy (CSP) is enabled
|
||
and configured properly (Manual)
|
||
|
||
5.3.3 Ensure the Referrer Policy is enabled and configured
|
||
properly (Manual)
|
||
|
||
6 Mandatory Access Control
|
||
|
||
|
||
|
||
|
||
Page 151
|
||
Appendix: Change History
|
||
Date Version Changes for this version
|
||
|
||
Oct 13, 2017 _Default Value,
|
||
Remediation Procedure,
|
||
Listing Order, Section_ on
|
||
**[Recommendation] 4.2
|
||
Give the NGINX User
|
||
Account an Invalid Shell**
|
||
were updated.
|
||
|
||
Feb 15, 2018 1.0.0 **[recommendation] 1.1.1P
|
||
Installing Nginx** was
|
||
created.
|
||
|
||
Nov 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 2.2.2P
|
||
Ensure the NGINX service
|
||
account is locked** was
|
||
updated.
|
||
|
||
Nov 10, 2018 1.0.0 _Listing Order, Status,
|
||
references, Default Value,
|
||
Impact Statement, Audit
|
||
Procedure, Remediation
|
||
Procedure, Rationale
|
||
Statement, Description,
|
||
Scoring Status, Title_ on
|
||
**[recommendation] 4.1.10
|
||
Ensure a Trusted Certificate
|
||
and Trust Chain is
|
||
Installed**
|
||
|
||
|
||
|
||
|
||
Page 152
|
||
Date Version Changes for this version
|
||
|
||
Nov 10, 2018 1.0.0 _Listing Order, Status,
|
||
references, Default Value,
|
||
Impact Statement, Audit
|
||
Procedure, Remediation
|
||
Procedure, Rationale
|
||
Statement, Description,
|
||
Scoring Status, Title_ on
|
||
**[recommendation] 4.1.11
|
||
Ensure Permissions on
|
||
Servers Private Key is
|
||
Protected** wer
|
||
|
||
Nov 11, 2018 1.0.0 **[recommendation] 4.1.4P
|
||
Ensure Permissions on
|
||
Servers Private Key is
|
||
Protected** was created.
|
||
|
||
Nov 13, 2018 1.0.0 **[recommendation] 4.1.4P
|
||
Restrict Weak Ciphers From
|
||
Being Used** was created.
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 1.2.1P
|
||
Ensure the Latest Software
|
||
Package is Installed** was
|
||
updated.
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 1.1.1P
|
||
Installing NGINX** was
|
||
updated.
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 1.2.1P
|
||
Ensure Package Manager
|
||
Repositories are
|
||
Configured** was updated.
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order, Rationale
|
||
Statement, Description_ on
|
||
**[recommendation] 2.5.2P
|
||
Ensure server_tokens
|
||
directive is set to Off** were
|
||
updated.
|
||
|
||
|
||
Page 153
|
||
Date Version Changes for this version
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 2.5.2P
|
||
Ensure Default Error Pages
|
||
and index.html Pages don't
|
||
reference NGINX** was
|
||
updated.
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 2.5.2P
|
||
Ensure Hidden File Serving
|
||
Is Disabled** was updated.
|
||
|
||
Dec 10, 2018 1.0.0 _Listing Order_ on
|
||
**[recommendation] 2.5.2P
|
||
Ensure Proxy does not
|
||
Enable Information
|
||
Disclosure** was updated.
|
||
|
||
Jan 7, 2019 1.0.0 _Listing Order_ on
|
||
**[recommendation] 1.1.1P
|
||
Installing NGINX** was
|
||
updated.
|
||
|
||
Sep 8, 2022 2.0.0 Update to NGINX Install
|
||
instructions to show 16.1
|
||
(Ticket 9029)
|
||
|
||
Sep 8, 2022 2.0.0 No longer score this as the
|
||
industry deprecates it.
|
||
(Ticket 9030)
|
||
|
||
Sep 19, 2022 2.0.0 Advice for
|
||
ssl_prefer_server_ciphers is
|
||
outdated (Ticket 12709)
|
||
|
||
Oct 3, 2022 2.0.0 Update Artifact (Ticket
|
||
16386)
|
||
|
||
Oct 3, 2022 2.0.0 Update Artifact (Ticket
|
||
16388)
|
||
|
||
Oct 3, 2022 2.0.0 Manual or Automated
|
||
(Ticket 16389)
|
||
|
||
|
||
|
||
|
||
Page 154
|
||
Date Version Changes for this version
|
||
|
||
Oct 7, 2022 2.0.0 Verify the Username (Ticket
|
||
16390)
|
||
|
||
Oct 11, 2022 2.0.0 UPDATE - 2.3.2 Ensure
|
||
access to NGINX directories
|
||
and files is restricted (Ticket
|
||
16392)
|
||
|
||
Oct 12, 2022 2.0.0 Question about using Error
|
||
code 404 in 2.4.2 Ensure
|
||
requests for unknown host
|
||
names are rejected (Ticket
|
||
15400)
|
||
|
||
Oct 12, 2022 2.0.0 Update prose (Ticket 16398)
|
||
|
||
Oct 12, 2022 2.0.0 Remove SHA from
|
||
recommended hash
|
||
algorithms in cipher suites.
|
||
(Ticket 16405)
|
||
|
||
Oct 13, 2022 2.0.0 Review these links with
|
||
community on next update
|
||
(Ticket 12371)
|
||
|
||
Oct 28, 2022 2.0.0 False positive on Centos 8,
|
||
zabbix appliance (Ticket
|
||
12389)
|
||
|
||
Oct 28, 2022 2.0.0 NGINX False Positives on
|
||
Debian 9 (Ticket 10847)
|
||
|
||
Oct 28, 2022 2.0.0 NGINX benchmark reports
|
||
false positives on Ubuntu
|
||
18.04 (Ticket 10791)
|
||
|
||
Oct 28, 2022 2.0.0 Can't forget OWASP Mod
|
||
Security (Ticket 7397)
|
||
|
||
Jun 15, 2023 2.0.1 Addressed AAC bug and
|
||
republished as v2.0.1
|
||
|
||
|
||
|
||
|
||
Page 155
|
||
Date Version Changes for this version
|
||
|
||
Apr 5, 2024 2.1.0 Update Artifact - 2.5.4
|
||
Ensure the NGINX reverse
|
||
proxy does not enable
|
||
information disclosure
|
||
(Ticket 18685)
|
||
|
||
Jun 4, 2024 2.1.0 add_header mistake Strict-
|
||
Transport-Security (Ticket
|
||
19160)
|
||
|
||
Jun 4, 2024 2.1.0 1.1.1 Ensure NGINX is
|
||
installed... (Ticket 18701)
|
||
|
||
Jun 4, 2024 2.1.0 2.2.1 Check NGINX user
|
||
privileges checks the wrong
|
||
user (Ticket 20513)
|
||
|
||
Jun 27, 2024 2.1.0 2.3.2 Ensure access to
|
||
NGINX directories and files
|
||
is restricted (Scored) (Ticket
|
||
21775)
|
||
|
||
Apr 1, 2025 3.0.0 Update 1.1.1 (Ticket 16385)
|
||
|
||
Aug 26, 2025 3.0.0 CIS-CAT PRO Scan: Profile
|
||
does not recognize Debian
|
||
based Systems, issue in
|
||
BASH code (Ticket 24449)
|
||
|
||
Aug 26, 2025 3.0.0 Does not recognize Debian
|
||
based systems, nor vendor
|
||
repository (Ticket 24450)
|
||
|
||
Aug 26, 2025 3.0.0 Does not recognize Debian
|
||
based systems and more
|
||
current up-to-date vendor
|
||
repository (Ticket 24451)
|
||
|
||
Oct 28, 2025 3.0.0 DROP - Ensure NGINX is
|
||
installed from source (Ticket
|
||
24414)
|
||
|
||
|
||
|
||
|
||
Page 156
|
||
Date Version Changes for this version
|
||
|
||
Oct 28, 2025 3.0.0 BUG in NGINX detection -->
|
||
1.1 Installation Fail 1.1.1
|
||
Ensure NGINX is installed
|
||
(Ticket 22454)
|
||
|
||
Oct 28, 2025 3.0.0 DROP - Ensure HTTP
|
||
WebDAV module is not
|
||
installed (Ticket 25959)
|
||
|
||
Oct 28, 2025 3.0.0 DROP - Ensure modules
|
||
with gzip functionality are
|
||
disabled (Ticket 25960)
|
||
|
||
Oct 28, 2025 3.0.0 DROP -Ensure the
|
||
autoindex module is
|
||
disabled (Ticket 25961)
|
||
|
||
Oct 28, 2025 3.0.0 Need Artifact added (Ticket
|
||
16399)
|
||
|
||
Oct 28, 2025 3.0.0 Need clarification on what
|
||
are considered as server
|
||
keys (Ticket 24134)
|
||
|
||
Oct 28, 2025 3.0.0 UPDATE - 2.1.1 Ensure
|
||
only required modules are
|
||
loaded (Ticket 25958)
|
||
|
||
Oct 28, 2025 3.0.0 Should this be automated
|
||
(Ticket 16393)
|
||
|
||
Oct 28, 2025 3.0.0 DROP - Ensure the core
|
||
dump directory is secured
|
||
(Ticket 26451)
|
||
|
||
Nov 4, 2025 3.0.0 UPDATE - Disable weak
|
||
ciphers - Look into making
|
||
this an automated check
|
||
(Ticket 16739)
|
||
|
||
Nov 12, 2025 3.0.0 UPDATE - 4.1.6 Ensure
|
||
custom Diffie-Hellman
|
||
parameters are used (Ticket
|
||
26523)
|
||
|
||
|
||
|
||
Page 157
|
||
Date Version Changes for this version
|
||
|
||
Nov 12, 2025 3.0.0 This recommendation
|
||
should carry a condition
|
||
(Ticket 24678)
|
||
|
||
Nov 12, 2025 3.0.0 UPDATE - 4.1.8 Ensure
|
||
HTTP Strict Transport
|
||
Security (HSTS) is enabled
|
||
(Ticket 26524)
|
||
|
||
Nov 12, 2025 3.0.0 UPDATE - 4.1.9 Ensure
|
||
upstream server traffic is
|
||
authenticated with a client
|
||
certificate (Ticket 26570)
|
||
|
||
Nov 12, 2025 3.0.0 UPDATE - 4.1.10 Ensure
|
||
the upstream traffic server
|
||
certificate is trusted (Ticket
|
||
26571)
|
||
|
||
Nov 12, 2025 3.0.0 DROP - 4.1.11 Ensure your
|
||
domain is preloaded (Ticket
|
||
26572)
|
||
|
||
Nov 12, 2025 3.0.0 UPDATE - 4.1.11 Ensure
|
||
Secure Session Resumption
|
||
is Enabled (Ticket 26573)
|
||
|
||
Nov 12, 2025 3.0.0 http 3.0 (Ticket 16380)
|
||
|
||
Nov 12, 2025 3.0.0 DROP - Ensure only Perfect
|
||
Forward Secrecy Ciphers
|
||
are Leveraged (Ticket
|
||
26575)
|
||
|
||
Nov 20, 2025 3.0.0 UPDATE - 5.1.1 Ensure
|
||
allow and deny filters limit
|
||
access to specific IP
|
||
addresses (Ticket 26621)
|
||
|
||
Nov 20, 2025 3.0.0 UPDATE - 5.1.2 Ensure
|
||
only approved HTTP
|
||
methods are allowed (Ticket
|
||
26622)
|
||
|
||
|
||
|
||
|
||
Page 158
|
||
Date Version Changes for this version
|
||
|
||
Nov 20, 2025 3.0.0 Recommendation 5.2.1 is
|
||
"Automated", but the correct
|
||
timeout values are not
|
||
clearly specified. (Ticket
|
||
23703)
|
||
|
||
Nov 20, 2025 3.0.0 UPDATE - 5.2.2 Ensure the
|
||
maximum request body size
|
||
is set correctly (Ticket
|
||
26623)
|
||
|
||
Nov 20, 2025 3.0.0 UPDATE - 5.2.3 Ensure the
|
||
maximum buffer size for
|
||
URIs is defined (Ticket
|
||
26624)
|
||
|
||
Nov 20, 2025 3.0.0 UPDATE - 5.2.4 Ensure the
|
||
number of connections per
|
||
IP address is limited (Ticket
|
||
26625)
|
||
|
||
Nov 20, 2025 3.0.0 UPDATE - 5.2.5 Ensure rate
|
||
limits by IP address are set
|
||
(Ticket 26626)
|
||
|
||
Nov 25, 2025 3.0.0 Additional information for
|
||
http3 module in NGINX
|
||
repository (Ticket 26628)
|
||
|
||
Nov 25, 2025 3.0.0 Update Artifact (Ticket
|
||
16391)
|
||
|
||
Nov 25, 2025 3.0.0 Check configuration for
|
||
http3 for recommendation
|
||
2.4.1 (Ticket 26627)
|
||
|
||
Nov 25, 2025 3.0.0 UPDATE - 5.3.2 Ensure X-
|
||
Content-Type-Options
|
||
header is configured and
|
||
enabled (Ticket 26675)
|
||
|
||
|
||
|
||
|
||
Page 159
|
||
Date Version Changes for this version
|
||
|
||
Nov 25, 2025 3.0.0 UPDATE - 5.3.3 Ensure that
|
||
Content Security Policy
|
||
(CSP) is enabled and
|
||
configured properly (Ticket
|
||
26676)
|
||
|
||
Nov 25, 2025 3.0.0 UPDATE - 5.3.4 Ensure the
|
||
Referrer Policy is enabled
|
||
and configured properly
|
||
(Ticket 26677)
|
||
|
||
Nov 25, 2025 3.0.0 DROP - 5.3.1 Ensure X-
|
||
Frame-Options header is
|
||
configured and enabled
|
||
(Ticket 26674)
|
||
|
||
Dec 2, 2025 3.0.0 DROP - 3.4 Ensure log files
|
||
are rotated (Ticket 26769)
|
||
|
||
Dec 2, 2025 3.0.0 DROP - 3.5 Ensure error
|
||
logs are sent to a remote
|
||
SIEM server (Ticket 26770)
|
||
|
||
Dec 2, 2025 3.0.0 DROP - 3.6 Ensure access
|
||
logs are sent to a remote
|
||
syslog server (Ticket 26771)
|
||
|
||
|
||
|
||
|
||
Page 160
|
||
|