1 //------------------------------------------------------------------------------
2 // <copyright file="AppSettings.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
11 using System.Collections.Specialized;
12 using System.Configuration;
13 using System.Diagnostics.CodeAnalysis;
14 using System.Security.Permissions;
15 using System.Web.Configuration;
16 using System.Web.Hosting;
18 namespace System.Web.Util {
20 internal static class AppSettings
22 private static volatile bool _settingsInitialized = false;
23 private static object _appSettingsLock = new object();
24 private static void EnsureSettingsLoaded() {
26 if (!_settingsInitialized) {
27 lock (_appSettingsLock) {
28 if (!_settingsInitialized) {
30 NameValueCollection settings = null;
33 settings = GetAppSettingsSection();
36 // GetApplicationPathData may throw. That's fine. Let the user see the exception
37 // once, but just fall back on default settings for the future.
38 if (settings == null || !Boolean.TryParse(settings["aspnet:UseHostHeaderForRequestUrl"], out _useHostHeaderForRequestUrl))
39 _useHostHeaderForRequestUrl = false;
41 if (settings == null || !Boolean.TryParse(settings["aspnet:AllowAnonymousImpersonation"], out _allowAnonymousImpersonation))
42 _allowAnonymousImpersonation = false;
44 if (settings == null || !Boolean.TryParse(settings["aspnet:ScriptResourceAllowNonJsFiles"], out _scriptResourceAllowNonJsFiles))
45 _scriptResourceAllowNonJsFiles = false;
47 if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyEncryption"], out _useLegacyEncryption))
48 _useLegacyEncryption = false;
50 if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyMachineKeyEncryption"], out _useLegacyMachineKeyEncryption))
51 _useLegacyMachineKeyEncryption = false;
53 if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyFormsAuthenticationTicketCompatibility"], out _useLegacyFormsAuthenticationTicketCompatibility))
54 _useLegacyFormsAuthenticationTicketCompatibility = false;
56 if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyEventValidationCompatibility"], out _useLegacyEventValidationCompatibility))
57 _useLegacyEventValidationCompatibility = false;
59 _allowInsecureDeserialization = GetNullableBooleanValue(settings, "aspnet:AllowInsecureDeserialization");
61 if (settings == null || !Boolean.TryParse(settings["aspnet:AlwaysIgnoreViewStateValidationErrors"], out _alwaysIgnoreViewStateValidationErrors))
62 _alwaysIgnoreViewStateValidationErrors = false;
64 if (settings == null || !Boolean.TryParse(settings["aspnet:AllowRelaxedHttpUserName"], out _allowRelaxedHttpUserName))
65 _allowRelaxedHttpUserName = false;
67 if (settings == null || !Boolean.TryParse(settings["aspnet:JavaScriptDoNotEncodeAmpersand"], out _javaScriptDoNotEncodeAmpersand))
68 _javaScriptDoNotEncodeAmpersand = false;
70 if (settings == null || !Boolean.TryParse(settings["aspnet:UseTaskFriendlySynchronizationContext"], out _useTaskFriendlySynchronizationContext))
71 _useTaskFriendlySynchronizationContext = (BinaryCompatibility.Current.TargetsAtLeastFramework45) ? true : false;
73 if (settings == null || !Boolean.TryParse(settings["aspnet:AllowAsyncDuringSyncStages"], out _allowAsyncDuringSyncStages))
74 _allowAsyncDuringSyncStages = false;
76 if (settings == null || !int.TryParse(settings["aspnet:MaxHttpCollectionKeys"], out _maxHttpCollectionKeys) || _maxHttpCollectionKeys < 0)
77 _maxHttpCollectionKeys = DefaultMaxHttpCollectionKeys;
79 if (settings == null || !int.TryParse(settings["aspnet:MaxJsonDeserializerMembers"], out _maxJsonDeserializerMembers) || _maxJsonDeserializerMembers < 0)
80 _maxJsonDeserializerMembers = DefaultMaxJsonDeserializerMembers;
82 if (settings == null || !Boolean.TryParse(settings["aspnet:DoNotDisposeSpecialHttpApplicationInstances"], out _doNotDisposeSpecialHttpApplicationInstances))
83 _doNotDisposeSpecialHttpApplicationInstances = false;
86 _formsAuthReturnUrlVar = settings["aspnet:FormsAuthReturnUrlVar"];
88 if (settings == null || !Boolean.TryParse(settings["aspnet:RestrictXmlControls"], out _restrictXmlControls))
89 _restrictXmlControls = false;
91 if (settings == null || !Boolean.TryParse(settings["aspnet:AllowRelaxedRelativeUrl"], out _allowRelaxedRelativeUrl))
92 _allowRelaxedRelativeUrl = false;
94 if (settings == null || !Boolean.TryParse(settings["aspnet:UseLegacyRequestUrlGeneration"], out _useLegacyRequestUrlGeneration))
95 _useLegacyRequestUrlGeneration = false;
97 if (settings == null || !Boolean.TryParse(settings["aspnet:AllowUtf7RequestContentEncoding"], out _allowUtf7RequestContentEncoding))
98 _allowUtf7RequestContentEncoding = false;
100 if (settings == null || !Boolean.TryParse(settings["aspnet:AllowRelaxedUnicodeDecoding"], out _allowRelaxedUnicodeDecoding))
101 _allowRelaxedUnicodeDecoding = false;
103 if (settings == null || !Boolean.TryParse(settings["aspnet:DontUsePercentUUrlEncoding"], out _dontUsePercentUUrlEncoding))
104 _dontUsePercentUUrlEncoding = BinaryCompatibility.Current.TargetsAtLeastFramework452; // default value is keyed off of <httpRuntime targetFramework="4.5.2" />
106 if (settings == null || !int.TryParse(settings["aspnet:UpdatePanelMaxScriptLength"], out _updatePanelMaxScriptLength) || _updatePanelMaxScriptLength < 0)
107 _updatePanelMaxScriptLength = 0;
109 // AppSettings override allows users to build against 4.5 but run against 4.0 or 4.5
110 _maxConcurrentCompilations = GetNullableIntegerValue(settings, "aspnet:MaxConcurrentCompilations");
112 if (settings == null || !int.TryParse(settings["aspnet:MaxAcceptLanguageFallbackCount"], out _maxAcceptLanguageFallbackCount) || _maxAcceptLanguageFallbackCount <= 0)
113 _maxAcceptLanguageFallbackCount = DefaultMaxAcceptLanguageFallbackCount;
115 if (settings == null || !Boolean.TryParse(settings["aspnet:PortableCompilationOutput"], out _portableCompilationOutput))
116 _portableCompilationOutput = false;
118 if (settings == null || string.IsNullOrWhiteSpace(_portableCompilationOutputSnapshotType = settings["aspnet:PortableCompilationOutputSnapshotType"]))
119 _portableCompilationOutputSnapshotType = null;
121 if (settings == null || string.IsNullOrWhiteSpace(_portableCompilationOutputSnapshotTypeOptions = settings["aspnet:PortableCompilationOutputSnapshotTypeOptions"]))
122 _portableCompilationOutputSnapshotTypeOptions = null;
124 if (settings == null || !Boolean.TryParse(settings["aspnet:EnsureSessionStateLockedOnFlush"], out _ensureSessionStateLockedOnFlush))
125 _ensureSessionStateLockedOnFlush = false;
127 if (settings == null || !Boolean.TryParse(settings["aspnet:UseRandomizedStringHashAlgorithm"], out _useRandomizedStringHashAlgorithm))
128 _useRandomizedStringHashAlgorithm = false;
130 if (settings == null || !Boolean.TryParse(settings["aspnet:EnableAsyncModelBinding"], out _enableAsyncModelBinding))
131 _enableAsyncModelBinding = BinaryCompatibility.Current.TargetsAtLeastFramework46;
133 if (settings == null || !int.TryParse(settings["aspnet:RequestQueueLimitPerSession"], out _requestQueueLimitPerSession) || _requestQueueLimitPerSession < 0)
134 _requestQueueLimitPerSession = BinaryCompatibility.Current.TargetsAtLeastFramework463 ? DefaultRequestQueueLimitPerSession : UnlimitedRequestsPerSession;
136 _settingsInitialized = true;
143 [ConfigurationPermission(SecurityAction.Assert, Unrestricted = true)]
144 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "This assert is safe since we control the callers; they're not leaking information.")]
145 private static NameValueCollection GetAppSettingsSection() {
146 // DevDiv #353926 - Fall back to reading App.config if System.Web types are being consumed outside an ASP.NET application
147 if (!HostingEnvironment.IsHosted) {
148 return ConfigurationManager.AppSettings;
151 // Check the app-level config. Ignore configuration errors
152 CachedPathData appPathData = CachedPathData.GetApplicationPathData();
154 if (appPathData != null && appPathData.ConfigRecord != null)
155 return appPathData.ConfigRecord.GetSection("appSettings") as NameValueCollection;
161 // helper function to read a tri-state boolean from config
162 private static bool? GetNullableBooleanValue(NameValueCollection settings, string key) {
164 return (settings != null && Boolean.TryParse(settings[key], out value)) ? value : (bool?)null;
167 // helper function to read a nullable int from config
168 private static int? GetNullableIntegerValue(NameValueCollection settings, string key) {
170 return (settings != null && int.TryParse(settings[key], out value)) ? value : (int?)null;
173 private static bool _useHostHeaderForRequestUrl;
174 internal static bool UseHostHeaderForRequestUrl {
176 EnsureSettingsLoaded();
177 return _useHostHeaderForRequestUrl;
181 private static bool _allowAnonymousImpersonation;
182 internal static bool AllowAnonymousImpersonation {
184 EnsureSettingsLoaded();
185 return _allowAnonymousImpersonation;
189 // false [default] to allow ScriptResource.axd to serve static files only if they are *.js;
190 // true to allow ScriptResource.axd to serve any static file (bad practice)
191 private static bool _scriptResourceAllowNonJsFiles;
192 internal static bool ScriptResourceAllowNonJsFiles {
194 EnsureSettingsLoaded();
195 return _scriptResourceAllowNonJsFiles;
199 // false [default] to use encrypt+sign for all except Membership and MachineKey public API
200 // true to use encrypt only (dangerous!)
201 private static bool _useLegacyEncryption;
202 internal static bool UseLegacyEncryption {
204 EnsureSettingsLoaded();
205 return _useLegacyEncryption;
209 // false [default] to use encrypt+sign for MachineKey public API
210 // true to use encrypt only for MachineKey public API (dangerous!)
211 private static bool _useLegacyMachineKeyEncryption;
212 internal static bool UseLegacyMachineKeyEncryption {
214 EnsureSettingsLoaded();
215 return _useLegacyMachineKeyEncryption;
219 // false [default] to use secure semantics when generating FormsAuthentication tickets
220 // true to allow ticketCompatibilityMode="Framework20" (dangerous!)
221 private static bool _useLegacyFormsAuthenticationTicketCompatibility;
222 internal static bool UseLegacyFormsAuthenticationTicketCompatibility {
224 EnsureSettingsLoaded();
225 return _useLegacyFormsAuthenticationTicketCompatibility;
229 // false [default] to use a cryptographic hash algorithm when generating the __EVENTVALIDATION field
230 // true to use String.GetHashCode() instead (dangerous!)
231 // more info: DevDiv #233564 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=233564)
232 private static bool _useLegacyEventValidationCompatibility;
233 internal static bool UseLegacyEventValidationCompatibility {
235 EnsureSettingsLoaded();
236 return _useLegacyEventValidationCompatibility;
240 // false to always enforce EnableViewStateMac=false
241 // true to allow the developer to specify EnableViewStateMac=false (dangerous, leads to RCE!)
242 // null [default] to make the decision based on a registry key
243 // more info: DevDiv #461378 (http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/461378)
244 private static bool? _allowInsecureDeserialization;
245 internal static bool? AllowInsecureDeserialization {
247 EnsureSettingsLoaded();
248 return _allowInsecureDeserialization;
252 // false [default] to use the default heuristic for determining when to suppress __VIEWSTATE MAC validation errors and when to display a YSOD
253 // true to always ---- errors and never show a YSOD
254 private static bool _alwaysIgnoreViewStateValidationErrors;
255 internal static bool AlwaysIgnoreViewStateValidationErrors {
257 EnsureSettingsLoaded();
258 return _alwaysIgnoreViewStateValidationErrors;
262 // false [default] to restrict dangerous characters in any user name passed to native code
263 // true to allow dangerous characters
264 private static bool _allowRelaxedHttpUserName;
265 internal static bool AllowRelaxedHttpUserName {
267 EnsureSettingsLoaded();
268 return _allowRelaxedHttpUserName;
272 // false [default] to encode '&' characters in HttpUtility.JavaScriptStringEncode()
273 // true to not encode '&' characters (bad practice)
274 private static bool _javaScriptDoNotEncodeAmpersand;
275 internal static bool JavaScriptDoNotEncodeAmpersand {
277 EnsureSettingsLoaded();
278 return _javaScriptDoNotEncodeAmpersand;
282 // false [default] to use the updated AspNetSynchronizationContext type (needed for TAP methods)
283 // true to use LegacyAspNetSynchronizationContext (needed for some back-compat scenarios)
284 private static bool _useTaskFriendlySynchronizationContext ;
285 internal static bool UseTaskFriendlySynchronizationContext {
287 EnsureSettingsLoaded();
288 return _useTaskFriendlySynchronizationContext ;
292 // false [default] if the updated AspNetSynchronizationContext type should throw when it encounters invalid operations
293 // true to allow some invalid operations through the system (needed for some back-compat scenarios)
294 // more info: see doc on HttpContext.AllowAsyncDuringSyncStages
295 private static bool _allowAsyncDuringSyncStages;
296 internal static bool AllowAsyncDuringSyncStages {
298 EnsureSettingsLoaded();
299 return _allowAsyncDuringSyncStages;
303 // maximum number of keys a HttpValueCollection, HttpFileCollection or HttpCookieCollection is allowed to have
304 private const int DefaultMaxHttpCollectionKeys = Int32.MaxValue;
305 private static int _maxHttpCollectionKeys = DefaultMaxHttpCollectionKeys;
306 internal static int MaxHttpCollectionKeys {
308 EnsureSettingsLoaded();
309 return _maxHttpCollectionKeys;
313 // maximum number of entries a Json deserialized dictionary is allowed to have
314 private const int DefaultMaxJsonDeserializerMembers = Int32.MaxValue;
315 private static int _maxJsonDeserializerMembers = DefaultMaxJsonDeserializerMembers;
316 internal static int MaxJsonDeserializerMembers {
318 EnsureSettingsLoaded();
319 return _maxJsonDeserializerMembers;
323 // false [default] to call HttpApplication.Dispose() on all HttpApplication instances instantiated by ASP.NET
324 // true to suppress calling HttpApplication.Dispose() on "special" (i.e., associated with App_Start, App_End, etc.) HttpApplication instances
325 // see DevDiv #109006 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=109006): HttpModules attached to the "Special" HttpApplication instance are not disposed
326 private static bool _doNotDisposeSpecialHttpApplicationInstances;
327 internal static bool DoNotDisposeSpecialHttpApplicationInstances {
329 EnsureSettingsLoaded();
330 return _doNotDisposeSpecialHttpApplicationInstances;
334 // none [default] to use the existing /loging.aspx?ReturnUrl=
335 // <FormsAuthReturnUrlVar> to use /loging.aspx?<FormsAuthReturnUrlVar>=
336 private static string _formsAuthReturnUrlVar;
337 internal static string FormsAuthReturnUrlVar {
339 EnsureSettingsLoaded();
340 return _formsAuthReturnUrlVar;
344 // false [default] to use potentially unsafe System.Xml API's that were in use for 1.0 through 4.0
345 // true to avoid using vulnerable System.Xml API's.
346 private static bool _restrictXmlControls;
347 internal static bool RestrictXmlControls {
349 EnsureSettingsLoaded();
350 return _restrictXmlControls;
354 // false [default] - requires relative url to be more strict
355 // true - use less strict policy
356 private static bool _allowRelaxedRelativeUrl;
357 internal static bool AllowRelaxedRelativeUrl {
359 EnsureSettingsLoaded();
360 return _allowRelaxedRelativeUrl;
364 // false [default] - attempts to re-encode HttpRequest.Url so that it is equivalent to the actual incoming URL
365 // true - does not attempt to re-encode the Url, which could allow the Uri class to perform double-decoding (dangerous!)
366 // See also DevDiv #703232 (http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/703232)
367 private static bool _useLegacyRequestUrlGeneration;
368 internal static bool UseLegacyRequestUrlGeneration {
370 EnsureSettingsLoaded();
371 return _useLegacyRequestUrlGeneration;
375 // false [default] - disallow UTF-7 as an incoming ContentEncoding
376 // true - allow UTF-7 as an incoming ContentEncoding (dangerous!)
377 private static bool _allowUtf7RequestContentEncoding;
378 internal static bool AllowUtf7RequestContentEncoding {
380 EnsureSettingsLoaded();
381 return _allowUtf7RequestContentEncoding;
385 // false [default] - disallow invalid UTF-16 (like unpaired surrogates) when deserializing JSON strings and inside UrlDecode
386 // true - allow malformed strings like "\ud800" and "%ud800" to be deserialized (dangerous!)
387 private static bool _allowRelaxedUnicodeDecoding;
388 internal static bool AllowRelaxedUnicodeDecoding {
390 EnsureSettingsLoaded();
391 return _allowRelaxedUnicodeDecoding;
395 // false - use UrlEncodeUnicode for some URL generation within ASP.NET, which can produce non-compliant results
396 // true - use UTF8 encoding for things like <form action>, which works with modern browsers
397 // defaults to true when targeting >= 4.5.2, otherwise false
398 // See DevDiv #762975 for more information.
399 private static bool _dontUsePercentUUrlEncoding;
400 internal static bool DontUsePercentUUrlEncoding {
402 EnsureSettingsLoaded();
403 return _dontUsePercentUUrlEncoding;
407 // maximum length for UpdatePanel script block
408 private static int _updatePanelMaxScriptLength;
409 internal static int UpdatePanelMaxScriptLength {
411 EnsureSettingsLoaded();
412 return _updatePanelMaxScriptLength;
416 // maximum number of concurrent compilations
417 private static int? _maxConcurrentCompilations;
418 internal static int? MaxConcurrentCompilations {
420 EnsureSettingsLoaded();
421 return _maxConcurrentCompilations;
425 // Controls how deep we look when trying to get a CultureInfo from an Accept-Language header.
426 // For example, a value of 3 with Accept-Language: en-us, en, fr-FR, zh-CN will cause us to
427 // look for "en-us", "en", and "fr-FR" in order, taking the first hit, but we won't look
428 // for "zh-CN" if the first three fail. Setting this value too high could result in DoS.
429 private const int DefaultMaxAcceptLanguageFallbackCount = 3;
430 private static int _maxAcceptLanguageFallbackCount;
431 internal static int MaxAcceptLanguageFallbackCount {
433 EnsureSettingsLoaded();
434 return _maxAcceptLanguageFallbackCount;
440 private static bool _portableCompilationOutput;
441 internal static bool PortableCompilationOutput {
443 EnsureSettingsLoaded();
444 return _portableCompilationOutput;
449 // complete type name as string
450 private static string _portableCompilationOutputSnapshotType;
451 internal static string PortableCompilationOutputSnapshotType {
453 EnsureSettingsLoaded();
454 return _portableCompilationOutputSnapshotType;
460 private static string _portableCompilationOutputSnapshotTypeOptions;
461 internal static string PortableCompilationOutputSnapshotTypeOptions {
463 EnsureSettingsLoaded();
464 return _portableCompilationOutputSnapshotTypeOptions;
468 // false [default] - allow delayed store of session state item (possibly concurrent access on new session)
469 // true - ensure the session state item is stored before another request is allowed to proceed with the same session
470 private static bool _ensureSessionStateLockedOnFlush;
471 internal static bool EnsureSessionStateLockedOnFlush {
473 EnsureSettingsLoaded();
474 return _ensureSessionStateLockedOnFlush;
478 // false [default] - don't force randomized hash code algorithm. Exisiting behavior
479 // true - use randomized hash code algorithm
480 private static bool _useRandomizedStringHashAlgorithm;
481 internal static bool UseRandomizedStringHashAlgorithm {
483 EnsureSettingsLoaded();
484 return _useRandomizedStringHashAlgorithm;
488 // false - disable async model binding
489 // true - enable async model binding
490 // defaults to true when targeting >= 4.6, otherwise false
491 private static bool _enableAsyncModelBinding;
492 internal static bool EnableAsyncModelBinding {
494 EnsureSettingsLoaded();
495 return _enableAsyncModelBinding;
499 internal const int UnlimitedRequestsPerSession = Int32.MaxValue;
500 internal const int DefaultRequestQueueLimitPerSession = 50;
501 // Limit of queued requests per session
502 private static int _requestQueueLimitPerSession;
503 internal static int RequestQueueLimitPerSession {
505 EnsureSettingsLoaded();
506 return _requestQueueLimitPerSession;