ServicePass Web.config Hardening Recommendations
This is a summary of security recommendations from existing documentation and guides. Many of these are probably already implemented but a period review is important.
Web.config Security Hardening
This document describes the hardening recommendations for Microsoft .net Framework 4.8 web.config files, and why each change is recommended.
Apply and validate these changes in a staging environment first. A few settings (notably
Content-Security-PolicyandrequestValidationMode="4.5") can affect application behavior and may require page-level tuning.
1. Secrets stored in clear text (highest priority)
The following values are secrets and are currently stored in plain text:
| Setting | Location |
|---|---|
recaptchaPrivateKey |
<appSettings> |
Server.CompanyAccountKey |
<appSettings> |
SMTP password |
<system.net><mailSettings><smtp><network> |
Recommendation
- Encrypt the sensitive configuration sections with the ASP.NET protected configuration provider, e.g.:
aspnet_regiis -pe "appSettings" -app "/YourApp"
aspnet_regiis -pe "system.net/mailSettings/smtp" -app "/YourApp" - Or move secrets out of the file entirely (environment variables, a vault, or a machine-protected store) and reference them at runtime.
- Rotate any secret that has ever been committed to source control (the repo already contains sample keys).
- SurePassID provides the Installation Manager app that can be used to rotate all encryption keys in a cadence that matches your organization policies.
- See Protecting sensitive configuration data for more info.
The hardened file keeps the values in place but flags each one with a comment; the encryption/relocation step must be performed as part of deployment.
2. system.web changes
2.1 Custom errors
- Before:
customErrors mode="On"with a single404 -> home.aspx. - After:
mode="RemoteOnly"(developers still see errors locally), an explicitdefaultRedirect, plus generic redirects for403,404, and500. - Why: Guarantees clients never receive detailed exception/stack-trace pages while still covering server errors.
<customErrors mode="RemoteOnly" defaultRedirect="home.aspx">
<error statusCode="403" redirect="home.aspx" />
<error statusCode="404" redirect="home.aspx" />
<error statusCode="500" redirect="home.aspx" />
</customErrors>
2.2 Session state
- Before:
sessionState timeout="60"(1 hours). - After:
timeout="20",cookieless="UseCookies",regenerateExpiredSessionId="true". - Why: A 60-minute idle session dramatically widens the window for session hijacking on shared machines. Twenty minutes is a common baseline; adjust to policy. Forcing cookie-based sessions prevents session IDs from appearing in URLs.
<sessionState timeout="20" mode="InProc" cookieless="UseCookies" regenerateExpiredSessionId="true" />
2.3 Cookies
- Before:
httpOnlyCookies="true" requireSSL="true". - After: adds
sameSite="Strict". - Why:
SameSite=Strictmitigates CSRF by preventing cookies from being sent on cross-site navigations. UseLaxif strict breaks a required cross-site flow.
<httpCookies httpOnlyCookies="true" requireSSL="true" sameSite="Strict" />
2.4 Compilation / tracing
- After: added
debug="false"on<compilation>and<trace enabled="false" localOnly="true" />. - Why: Debug builds disable optimizations and can leak information; ASP.NET tracing can expose sensitive request data. Both are explicitly disabled.
<compilation targetFramework="4.0" debug="false" />
<trace enabled="false" localOnly="true" />
2.5 httpRuntime
- Before:
requestValidationMode="2.0",enableVersionHeader="false". - After:
requestValidationMode="4.5", addedmaxRequestLength="4096"(KB) andtargetFramework="4.5". - Why: Request validation mode 2.0 uses the weaker legacy validation pipeline. Mode 4.5 applies validation earlier and more consistently. Capping request length reduces exposure to large-payload denial-of-service.
- Note: Validate that no page legitimately posts HTML/markup; if it does, scope the relaxation to that page rather than globally downgrading validation.
<httpRuntime enableVersionHeader="false" requestValidationMode="4.5" maxRequestLength="4096" targetFramework="4.5" />
2.6 machineKey (optional)
- Added a commented
<machineKey>template. - Why: Setting explicit, per-deployment keys ensures ViewState/Forms tokens are not portable across environments and are not derived from defaults. Generate unique values.
<machineKey validationKey="GENERATE-ME" decryptionKey="GENERATE-ME" validation="HMACSHA256" decryption="AES" />
3. SMTP (system.net)
enableSsl="true"is already present and retained.- The
passwordattribute is flagged as a secret (see Section 1).
<system.net>
<mailSettings>
<smtp><network defaultCredentials="false" host="" port="587" userName="" password="" enableSsl="true" /></smtp>
</mailSettings>
</system.net>
4. system.webServer changes
4.1 Managed modules
- Before:
runAllManagedModulesForAllRequests="true". - After:
false. - Why: Running every managed module for static/every request is unnecessary, increases attack surface, and hurts performance.
<modules runAllManagedModulesForAllRequests="false" />
4.2 Response headers
| Header | Before | After | Reason |
|---|---|---|---|
X-Frame-Options |
SAMEORIGIN |
DENY |
The portal should not be framed at all; blocks clickjacking. Use SAMEORIGIN only if self-framing is required. |
X-XSS-Protection |
1; mode=block |
0 |
The legacy XSS auditor is deprecated and can itself be abused; modern browsers ignore it. CSP replaces it. |
X-Content-Type-Options |
nosniff |
nosniff |
Unchanged (good). |
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
adds preload |
Stronger HTTPS enforcement. Only keep preload if you intend to submit the domain to the HSTS preload list. |
Referrer-Policy |
(none) | no-referrer |
Prevents leaking URLs (which may contain tokens) to third parties. |
Permissions-Policy |
(none) | geolocation=(), microphone=(), camera=() |
Disables powerful browser features the app does not use. |
Content-Security-Policy |
(none) | restrictive policy | Primary defense against XSS/data injection; frame-ancestors 'none' also reinforces anti-framing. |
X-Powered-By |
removed | removed | Unchanged (good). |
- CSP note: The supplied policy (
default-src 'self', nounsafe-inline) is a strong starting point but will likely block inline scripts/styles and external CDNs. Tune it to your actual assets, preferably using nonces or hashes rather thanunsafe-inline.
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="DENY" />
<add name="X-XSS-Protection" value="0" />
<add name="X-Content-Type-Options" value="nosniff" />
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" />
<add name="Referrer-Policy" value="no-referrer" />
<add name="Permissions-Policy" value="geolocation=(), microphone=(), camera=()" />
<add name="Content-Security-Policy" value="default-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests" />
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
4.3 Request filtering
- Server header: added
removeServerHeader="true"to strip theServerbanner (requires IIS 10+). Remove this attribute on older IIS. - Verbs: added an allow-list (
GET,POST,HEAD) withallowUnlisted="false"to blockTRACE,PUT,DELETE, etc. Add verbs if the app requires them. - Request limits:
maxAllowedContentLength="4194304"(4 MB),maxUrl, andmaxQueryStringcaps to reduce DoS/overflow surface. - Existing
hiddenSegments(account_setup,Trace) are retained.
<security>
<requestFiltering removeServerHeader="true">
<hiddenSegments>
<add segment="account_setup" />
<add segment="Trace" />
</hiddenSegments>
<verbs allowUnlisted="false">
<add verb="GET" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="HEAD" allowed="true" />
</verbs>
<requestLimits maxAllowedContentLength="4194304" maxUrl="4096" maxQueryString="2048" />
</requestFiltering>
</security>
4.4 HTTP errors
- Added
<httpErrors errorMode="Custom" existingResponse="Auto" />. - Why: Prevents detailed IIS-level error pages from reaching remote clients, complementing the ASP.NET
customErrorssetting.
<httpErrors errorMode="Custom" existingResponse="Auto" />
4.5 Directory browsing
- Added
<directoryBrowse enabled="false" />. - Why: Explicitly ensures directory contents cannot be enumerated.
<directoryBrowse enabled="false" />
5. Deployment checklist
- Encrypt or externalize all secrets (Section 1) and rotate exposed keys.
- Generate and set a unique
machineKeyper environment. - Confirm the site is served exclusively over HTTPS (HSTS assumes this).
- Deploy to staging, then exercise every page to validate:
Content-Security-Policydoes not block required scripts/styles/resources.requestValidationMode="4.5"does not reject legitimate form input.- Verb allow-list covers all endpoints the app uses.
- Verify
removeServerHeaderis supported by the target IIS version (10+); remove the attribute otherwise. - Confirm session timeout aligns with your security policy.
- Re-run a security header scan (e.g., securityheaders.com or an internal scanner) after deployment.