Browse Source

Replace NWebsec.Owin with NWebsec.Middleware

pull/39/head
David McCullough 10 years ago
committed by Kévin Chalet
parent
commit
b14089a570
  1. 23
      OpenIddict.sln
  2. 625
      external/NWebsec.Annotations/Annotations.cs
  3. 20
      external/NWebsec.Annotations/NWebsec.Annotations.xproj
  4. 26
      external/NWebsec.Annotations/Properties/AssemblyInfo.cs
  5. 18
      external/NWebsec.Annotations/project.json
  6. 13
      external/NWebsec.Core/Exceptions/RedirectValidationException.cs
  7. 21
      external/NWebsec.Core/Extensions/HttpContextExtensions.cs
  8. 53
      external/NWebsec.Core/Fluent/IFluentInterface.cs
  9. 29
      external/NWebsec.Core/Helpers/HeaderResultHandler.cs
  10. 12
      external/NWebsec.Core/Helpers/IHeaderResultHandler.cs
  11. 11
      external/NWebsec.Core/Helpers/X509/TlvTripletHeader.cs
  12. 252
      external/NWebsec.Core/Helpers/X509/X509Helper.cs
  13. 52
      external/NWebsec.Core/HttpHeaders/Configuration/CspConfiguration.cs
  14. 26
      external/NWebsec.Core/HttpHeaders/Configuration/CspDirectiveConfiguration.cs
  15. 9
      external/NWebsec.Core/HttpHeaders/Configuration/CspHeaderConfiguration.cs
  16. 20
      external/NWebsec.Core/HttpHeaders/Configuration/CspPluginTypesDirectiveConfiguration.cs
  17. 19
      external/NWebsec.Core/HttpHeaders/Configuration/CspReportUriDirectiveConfiguration.cs
  18. 15
      external/NWebsec.Core/HttpHeaders/Configuration/CspSandboxDirectiveConfiguration.cs
  19. 11
      external/NWebsec.Core/HttpHeaders/Configuration/CspUpgradeDirectiveConfiguration.cs
  20. 16
      external/NWebsec.Core/HttpHeaders/Configuration/HpkpConfiguration.cs
  21. 15
      external/NWebsec.Core/HttpHeaders/Configuration/HstsConfiguration.cs
  22. 30
      external/NWebsec.Core/HttpHeaders/Configuration/ICspConfiguration.cs
  23. 42
      external/NWebsec.Core/HttpHeaders/Configuration/ICspDirectiveBasicConfiguration.cs
  24. 19
      external/NWebsec.Core/HttpHeaders/Configuration/ICspDirectiveConfiguration.cs
  25. 26
      external/NWebsec.Core/HttpHeaders/Configuration/ICspDirectiveUnsafeInlineConfiguration.cs
  26. 9
      external/NWebsec.Core/HttpHeaders/Configuration/ICspHeaderConfiguration.cs
  27. 24
      external/NWebsec.Core/HttpHeaders/Configuration/ICspPluginTypesDirectiveConfiguration.cs
  28. 15
      external/NWebsec.Core/HttpHeaders/Configuration/ICspReportUriDirectiveConfiguration.cs
  29. 49
      external/NWebsec.Core/HttpHeaders/Configuration/ICspSandboxDirectiveConfiguration.cs
  30. 14
      external/NWebsec.Core/HttpHeaders/Configuration/ICspUpgradeDirectiveConfiguration.cs
  31. 14
      external/NWebsec.Core/HttpHeaders/Configuration/IHpkpCertConfiguration.cs
  32. 16
      external/NWebsec.Core/HttpHeaders/Configuration/IHpkpConfiguration.cs
  33. 9
      external/NWebsec.Core/HttpHeaders/Configuration/IHpkpPinConfiguration.cs
  34. 19
      external/NWebsec.Core/HttpHeaders/Configuration/IHstsConfiguration.cs
  35. 18
      external/NWebsec.Core/HttpHeaders/Configuration/IRedirectValidationConfiguration.cs
  36. 10
      external/NWebsec.Core/HttpHeaders/Configuration/ISameHostHttpsRedirectConfiguration.cs
  37. 9
      external/NWebsec.Core/HttpHeaders/Configuration/ISimpleBooleanConfiguration.cs
  38. 9
      external/NWebsec.Core/HttpHeaders/Configuration/IXFrameOptionsConfiguration.cs
  39. 23
      external/NWebsec.Core/HttpHeaders/Configuration/IXRobotsTagConfiguration.cs
  40. 11
      external/NWebsec.Core/HttpHeaders/Configuration/IXXssProtectionConfiguration.cs
  41. 19
      external/NWebsec.Core/HttpHeaders/Configuration/RedirectValidationConfiguration.cs
  42. 15
      external/NWebsec.Core/HttpHeaders/Configuration/SameHostHttpsRedirectConfiguration.cs
  43. 9
      external/NWebsec.Core/HttpHeaders/Configuration/SimpleBooleanConfiguration.cs
  44. 55
      external/NWebsec.Core/HttpHeaders/Configuration/Validation/HpkpConfigurationValidator.cs
  45. 24
      external/NWebsec.Core/HttpHeaders/Configuration/Validation/HstsConfigurationValidator.cs
  46. 51
      external/NWebsec.Core/HttpHeaders/Configuration/Validation/Rfc2045MediaTypeValidator.cs
  47. 24
      external/NWebsec.Core/HttpHeaders/Configuration/Validation/XRobotsTagConfigurationValidator.cs
  48. 9
      external/NWebsec.Core/HttpHeaders/Configuration/XFrameOptionsConfiguration.cs
  49. 16
      external/NWebsec.Core/HttpHeaders/Configuration/XRobotsTagConfiguration.cs
  50. 10
      external/NWebsec.Core/HttpHeaders/Configuration/XXssProtectionConfiguration.cs
  51. 12
      external/NWebsec.Core/HttpHeaders/Csp/CspSourceParseResult.cs
  52. 172
      external/NWebsec.Core/HttpHeaders/Csp/CspUriSource.cs
  53. 15
      external/NWebsec.Core/HttpHeaders/Csp/InvalidCspSourceException.cs
  54. 47
      external/NWebsec.Core/HttpHeaders/HeaderConstants.cs
  55. 383
      external/NWebsec.Core/HttpHeaders/HeaderGenerator.cs
  56. 25
      external/NWebsec.Core/HttpHeaders/HeaderResult.cs
  57. 31
      external/NWebsec.Core/HttpHeaders/IHeaderGenerator.cs
  58. 22
      external/NWebsec.Core/HttpHeaders/XXssPolicy.cs
  59. 22
      external/NWebsec.Core/HttpHeaders/XfoPolicy.cs
  60. 21
      external/NWebsec.Core/NWebsec.Core.xproj
  61. 28
      external/NWebsec.Core/NWebsecContext.cs
  62. 25
      external/NWebsec.Core/Properties/AssemblyInfo.cs
  63. 83
      external/NWebsec.Core/RedirectValidator.cs
  64. 34
      external/NWebsec.Core/project.json
  65. 197
      external/NWebsec.Middleware/ApplicationBuilderExtensions.cs
  66. 49
      external/NWebsec.Middleware/Core/OwinEnvironment.cs
  67. 28
      external/NWebsec.Middleware/Core/OwinKeys.cs
  68. 44
      external/NWebsec.Middleware/Core/RequestHeaders.cs
  69. 47
      external/NWebsec.Middleware/Core/ResponseHeaders.cs
  70. 37
      external/NWebsec.Middleware/CspDirective.cs
  71. 114
      external/NWebsec.Middleware/CspDirectiveExtensions.cs
  72. 161
      external/NWebsec.Middleware/CspOptions.cs
  73. 40
      external/NWebsec.Middleware/CspReportUriDirective.cs
  74. 49
      external/NWebsec.Middleware/FluentCspPluginTypesDirective.cs
  75. 45
      external/NWebsec.Middleware/FluentCspSandboxDirective.cs
  76. 21
      external/NWebsec.Middleware/Helpers/CspUpgradeHelper.cs
  77. 124
      external/NWebsec.Middleware/HpkpOptions.cs
  78. 33
      external/NWebsec.Middleware/HpkpOptionsConfiguration.cs
  79. 52
      external/NWebsec.Middleware/HstsOptions.cs
  80. 32
      external/NWebsec.Middleware/HstsOptionsConfiguration.cs
  81. 142
      external/NWebsec.Middleware/IFluentCspOptions.cs
  82. 14
      external/NWebsec.Middleware/IFluentCspPluginTypesDirective.cs
  83. 18
      external/NWebsec.Middleware/IFluentCspReportUriDirective.cs
  84. 39
      external/NWebsec.Middleware/IFluentCspSandboxDirective.cs
  85. 62
      external/NWebsec.Middleware/IFluentHpkpOptions.cs
  86. 59
      external/NWebsec.Middleware/IFluentHstsOptions.cs
  87. 26
      external/NWebsec.Middleware/IFluentRedirectValidationOptions.cs
  88. 22
      external/NWebsec.Middleware/IFluentXFrameOptions.cs
  89. 54
      external/NWebsec.Middleware/IFluentXRobotsTagOptions.cs
  90. 27
      external/NWebsec.Middleware/IFluentXXssProtectionOptions.cs
  91. 88
      external/NWebsec.Middleware/Middleware/CspMiddleware.cs
  92. 39
      external/NWebsec.Middleware/Middleware/HpkpMiddleware.cs
  93. 46
      external/NWebsec.Middleware/Middleware/HstsMiddleware.cs
  94. 39
      external/NWebsec.Middleware/Middleware/MiddleWareBase.cs
  95. 39
      external/NWebsec.Middleware/Middleware/RedirectValidationMiddleware.cs
  96. 34
      external/NWebsec.Middleware/Middleware/XContentTypeOptionsMiddleware.cs
  97. 34
      external/NWebsec.Middleware/Middleware/XDownloadOptionsMiddleware.cs
  98. 36
      external/NWebsec.Middleware/Middleware/XRobotsTagMiddleware.cs
  99. 34
      external/NWebsec.Middleware/Middleware/XXssMiddleware.cs
  100. 33
      external/NWebsec.Middleware/Middleware/XfoMiddleware.cs

23
OpenIddict.sln

@ -23,6 +23,14 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Core", "src\Open
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Mvc", "src\OpenIddict.Mvc\OpenIddict.Mvc.xproj", "{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Mvc", "src\OpenIddict.Mvc\OpenIddict.Mvc.xproj", "{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{DE26CC68-28BA-44BB-B28E-43B949C6C606}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NWebsec.Annotations", "external\NWebsec.Annotations\NWebsec.Annotations.xproj", "{144226E7-3EB3-459D-89C8-4D8720E1EF1E}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NWebsec.Core", "external\NWebsec.Core\NWebsec.Core.xproj", "{E9575361-5C4B-425A-BCA5-0E8249C6C5FB}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NWebsec.Middleware", "external\NWebsec.Middleware\NWebsec.Middleware.xproj", "{38C8E88F-1D01-466F-B47D-6D67F13C1594}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -61,6 +69,18 @@ Global
{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Debug|Any CPU.Build.0 = Debug|Any CPU {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Release|Any CPU.ActiveCfg = Release|Any CPU {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Release|Any CPU.Build.0 = Release|Any CPU {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84}.Release|Any CPU.Build.0 = Release|Any CPU
{144226E7-3EB3-459D-89C8-4D8720E1EF1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{144226E7-3EB3-459D-89C8-4D8720E1EF1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{144226E7-3EB3-459D-89C8-4D8720E1EF1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{144226E7-3EB3-459D-89C8-4D8720E1EF1E}.Release|Any CPU.Build.0 = Release|Any CPU
{E9575361-5C4B-425A-BCA5-0E8249C6C5FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9575361-5C4B-425A-BCA5-0E8249C6C5FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9575361-5C4B-425A-BCA5-0E8249C6C5FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9575361-5C4B-425A-BCA5-0E8249C6C5FB}.Release|Any CPU.Build.0 = Release|Any CPU
{38C8E88F-1D01-466F-B47D-6D67F13C1594}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38C8E88F-1D01-466F-B47D-6D67F13C1594}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38C8E88F-1D01-466F-B47D-6D67F13C1594}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38C8E88F-1D01-466F-B47D-6D67F13C1594}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -74,5 +94,8 @@ Global
{D2450929-ED0E-420D-B475-327924F9701C} = {D544447C-D701-46BB-9A5B-C76C612A596B} {D2450929-ED0E-420D-B475-327924F9701C} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{E60CF8CA-6313-4359-BE43-AFCBB927EA30} = {D544447C-D701-46BB-9A5B-C76C612A596B} {E60CF8CA-6313-4359-BE43-AFCBB927EA30} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84} = {D544447C-D701-46BB-9A5B-C76C612A596B} {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{144226E7-3EB3-459D-89C8-4D8720E1EF1E} = {DE26CC68-28BA-44BB-B28E-43B949C6C606}
{E9575361-5C4B-425A-BCA5-0E8249C6C5FB} = {DE26CC68-28BA-44BB-B28E-43B949C6C606}
{38C8E88F-1D01-466F-B47D-6D67F13C1594} = {DE26CC68-28BA-44BB-B28E-43B949C6C606}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

625
external/NWebsec.Annotations/Annotations.cs

@ -0,0 +1,625 @@
using System;
using System.Diagnostics;
#pragma warning disable 1591
// ReSharper disable UnusedMember.Global
// ReSharper disable UnusedParameter.Local
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable IntroduceOptionalParameters.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable InconsistentNaming
namespace NWebsec.Annotations
{
/// <summary>
/// Indicates that the value of the marked element could be <c>null</c> sometimes,
/// so the check for <c>null</c> is necessary before its usage
/// </summary>
/// <example><code>
/// [CanBeNull] public object Test() { return null; }
/// public void UseTest() {
/// var p = Test();
/// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
internal sealed class CanBeNullAttribute : Attribute { }
/// <summary>
/// Indicates that the value of the marked element could never be <c>null</c>
/// </summary>
/// <example><code>
/// [NotNull] public object Foo() {
/// return null; // Warning: Possible 'null' assignment
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter |
AttributeTargets.Property | AttributeTargets.Delegate |
AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
internal sealed class NotNullAttribute : Attribute { }
/// <summary>
/// Indicates that the marked method builds string by format pattern and (optional) arguments.
/// Parameter, which contains format string, should be given in constructor. The format string
/// should be in <see cref="string.Format(IFormatProvider,string,object[])"/>-like form
/// </summary>
/// <example><code>
/// [StringFormatMethod("message")]
/// public void ShowError(string message, params object[] args) { /* do something */ }
/// public void Foo() {
/// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(
AttributeTargets.Constructor | AttributeTargets.Method,
AllowMultiple = false, Inherited = true)]
internal sealed class StringFormatMethodAttribute : Attribute
{
/// <param name="formatParameterName">
/// Specifies which parameter of an annotated method should be treated as format-string
/// </param>
public StringFormatMethodAttribute(string formatParameterName)
{
FormatParameterName = formatParameterName;
}
public string FormatParameterName { get; private set; }
}
/// <summary>
/// Indicates that the function argument should be string literal and match one
/// of the parameters of the caller function. For example, ReSharper annotates
/// the parameter of <see cref="System.ArgumentNullException"/>
/// </summary>
/// <example><code>
/// public void Foo(string param) {
/// if (param == null)
/// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
internal sealed class InvokerParameterNameAttribute : Attribute { }
/// <summary>
/// Indicates that the method is contained in a type that implements
/// <see cref="System.ComponentModel.INotifyPropertyChanged"/> interface
/// and this method is used to notify that some property value changed
/// </summary>
/// <remarks>
/// The method should be non-static and conform to one of the supported signatures:
/// <list>
/// <item><c>NotifyChanged(string)</c></item>
/// <item><c>NotifyChanged(params string[])</c></item>
/// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>
/// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>
/// <item><c>SetProperty{T}(ref T, T, string)</c></item>
/// </list>
/// </remarks>
/// <example><code>
/// public class Foo : INotifyPropertyChanged {
/// public event PropertyChangedEventHandler PropertyChanged;
/// [NotifyPropertyChangedInvocator]
/// protected virtual void NotifyChanged(string propertyName) { ... }
///
/// private string _name;
/// public string Name {
/// get { return _name; }
/// set { _name = value; NotifyChanged("LastName"); /* Warning */ }
/// }
/// }
/// </code>
/// Examples of generated notifications:
/// <list>
/// <item><c>NotifyChanged("Property")</c></item>
/// <item><c>NotifyChanged(() =&gt; Property)</c></item>
/// <item><c>NotifyChanged((VM x) =&gt; x.Property)</c></item>
/// <item><c>SetProperty(ref myField, value, "Property")</c></item>
/// </list>
/// </example>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute
{
public NotifyPropertyChangedInvocatorAttribute() { }
public NotifyPropertyChangedInvocatorAttribute(string parameterName)
{
ParameterName = parameterName;
}
public string ParameterName { get; private set; }
}
/// <summary>
/// Describes dependency between method input and output
/// </summary>
/// <syntax>
/// <p>Function Definition Table syntax:</p>
/// <list>
/// <item>FDT ::= FDTRow [;FDTRow]*</item>
/// <item>FDTRow ::= Input =&gt; Output | Output &lt;= Input</item>
/// <item>Input ::= ParameterName: Value [, Input]*</item>
/// <item>Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>
/// <item>Value ::= true | false | null | notnull | canbenull</item>
/// </list>
/// If method has single input parameter, it's name could be omitted.<br/>
/// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same)
/// for method output means that the methos doesn't return normally.<br/>
/// <c>canbenull</c> annotation is only applicable for output parameters.<br/>
/// You can use multiple <c>[ContractAnnotation]</c> for each FDT row,
/// or use single attribute with rows separated by semicolon.<br/>
/// </syntax>
/// <examples><list>
/// <item><code>
/// [ContractAnnotation("=> halt")]
/// public void TerminationMethod()
/// </code></item>
/// <item><code>
/// [ContractAnnotation("halt &lt;= condition: false")]
/// public void Assert(bool condition, string text) // regular assertion method
/// </code></item>
/// <item><code>
/// [ContractAnnotation("s:null => true")]
/// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
/// </code></item>
/// <item><code>
/// // A method that returns null if the parameter is null, and not null if the parameter is not null
/// [ContractAnnotation("null => null; notnull => notnull")]
/// public object Transform(object data)
/// </code></item>
/// <item><code>
/// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")]
/// public bool TryParse(string s, out Person result)
/// </code></item>
/// </list></examples>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
internal sealed class ContractAnnotationAttribute : Attribute
{
public ContractAnnotationAttribute([NotNull] string contract)
: this(contract, false) { }
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
{
Contract = contract;
ForceFullStates = forceFullStates;
}
public string Contract { get; private set; }
public bool ForceFullStates { get; private set; }
}
/// <summary>
/// Indicates that marked element should be localized or not
/// </summary>
/// <example><code>
/// [LocalizationRequiredAttribute(true)]
/// public class Foo {
/// private string str = "my string"; // Warning: Localizable string
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
internal sealed class LocalizationRequiredAttribute : Attribute
{
public LocalizationRequiredAttribute() : this(true) { }
public LocalizationRequiredAttribute(bool required)
{
Required = required;
}
public bool Required { get; private set; }
}
/// <summary>
/// Indicates that the value of the marked type (or its derivatives)
/// cannot be compared using '==' or '!=' operators and <c>Equals()</c>
/// should be used instead. However, using '==' or '!=' for comparison
/// with <c>null</c> is always permitted.
/// </summary>
/// <example><code>
/// [CannotApplyEqualityOperator]
/// class NoEquality { }
/// class UsesNoEquality {
/// public void Test() {
/// var ca1 = new NoEquality();
/// var ca2 = new NoEquality();
/// if (ca1 != null) { // OK
/// bool condition = ca1 == ca2; // Warning
/// }
/// }
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(
AttributeTargets.Interface | AttributeTargets.Class |
AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { }
/// <summary>
/// When applied to a target attribute, specifies a requirement for any type marked
/// with the target attribute to implement or inherit specific type or types.
/// </summary>
/// <example><code>
/// [BaseTypeRequired(typeof(IComponent)] // Specify requirement
/// public class ComponentAttribute : Attribute { }
/// [Component] // ComponentAttribute requires implementing IComponent interface
/// public class MyComponent : IComponent { }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
[BaseTypeRequired(typeof(Attribute))]
internal sealed class BaseTypeRequiredAttribute : Attribute
{
public BaseTypeRequiredAttribute([NotNull] Type baseType)
{
BaseType = baseType;
}
[NotNull]
public Type BaseType { get; private set; }
}
/// <summary>
/// Indicates that the marked symbol is used implicitly
/// (e.g. via reflection, in external library), so this symbol
/// will not be marked as unused (as well as by other usage inspections)
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
internal sealed class UsedImplicitlyAttribute : Attribute
{
public UsedImplicitlyAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default) { }
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags) { }
public UsedImplicitlyAttribute(
ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
public ImplicitUseKindFlags UseKindFlags { get; private set; }
public ImplicitUseTargetFlags TargetFlags { get; private set; }
}
/// <summary>
/// Should be used on attributes and causes ReSharper
/// to not mark symbols marked with such attributes as unused
/// (as well as by other usage inspections)
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
internal sealed class MeansImplicitUseAttribute : Attribute
{
public MeansImplicitUseAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default) { }
public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags) { }
public MeansImplicitUseAttribute(
ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
[UsedImplicitly]
public ImplicitUseKindFlags UseKindFlags { get; private set; }
[UsedImplicitly]
public ImplicitUseTargetFlags TargetFlags { get; private set; }
}
[Flags]
internal enum ImplicitUseKindFlags
{
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
/// <summary>Only entity marked with attribute considered used</summary>
Access = 1,
/// <summary>Indicates implicit assignment to a member</summary>
Assign = 2,
/// <summary>
/// Indicates implicit instantiation of a type with fixed constructor signature.
/// That means any unused constructor parameters won't be reported as such.
/// </summary>
InstantiatedWithFixedConstructorSignature = 4,
/// <summary>Indicates implicit instantiation of a type</summary>
InstantiatedNoFixedConstructorSignature = 8,
}
/// <summary>
/// Specify what is considered used implicitly
/// when marked with <see cref="MeansImplicitUseAttribute"/>
/// or <see cref="UsedImplicitlyAttribute"/>
/// </summary>
[Flags]
internal enum ImplicitUseTargetFlags
{
Default = Itself,
Itself = 1,
/// <summary>Members of entity marked with attribute are considered used</summary>
Members = 2,
/// <summary>Entity marked with attribute and all its members considered used</summary>
WithMembers = Itself | Members
}
/// <summary>
/// This attribute is intended to mark publicly available API
/// which should not be removed and so is treated as used
/// </summary>
[Conditional("DEBUG"), MeansImplicitUse]
internal sealed class PublicAPIAttribute : Attribute
{
public PublicAPIAttribute() { }
public PublicAPIAttribute([NotNull] string comment)
{
Comment = comment;
}
[NotNull]
public string Comment { get; private set; }
}
/// <summary>
/// Tells code analysis engine if the parameter is completely handled
/// when the invoked method is on stack. If the parameter is a delegate,
/// indicates that delegate is executed while the method is executed.
/// If the parameter is an enumerable, indicates that it is enumerated
/// while the method is executed
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter, Inherited = true)]
internal sealed class InstantHandleAttribute : Attribute { }
/// <summary>
/// Indicates that a method does not make any observable state changes.
/// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>
/// </summary>
/// <example><code>
/// [Pure] private int Multiply(int x, int y) { return x * y; }
/// public void Foo() {
/// const int a = 2, b = 2;
/// Multiply(a, b); // Waring: Return value of pure method is not used
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Method, Inherited = true)]
internal sealed class PureAttribute : Attribute { }
/// <summary>
/// Indicates that a parameter is a path to a file or a folder
/// within a web project. Path can be relative or absolute,
/// starting from web root (~)
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal class PathReferenceAttribute : Attribute
{
public PathReferenceAttribute() { }
public PathReferenceAttribute([PathReference] string basePath)
{
BasePath = basePath;
}
[NotNull]
public string BasePath { get; private set; }
}
// ASP.NET MVC attributes
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute
{
public AspMvcAreaMasterLocationFormatAttribute(string format) { }
}
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute
{
public AspMvcAreaPartialViewLocationFormatAttribute(string format) { }
}
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute
{
public AspMvcAreaViewLocationFormatAttribute(string format) { }
}
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class AspMvcMasterLocationFormatAttribute : Attribute
{
public AspMvcMasterLocationFormatAttribute(string format) { }
}
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute
{
public AspMvcPartialViewLocationFormatAttribute(string format) { }
}
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class AspMvcViewLocationFormatAttribute : Attribute
{
public AspMvcViewLocationFormatAttribute(string format) { }
}
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC action. If applied to a method, the MVC action name is calculated
/// implicitly from the context. Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcActionAttribute : Attribute
{
public AspMvcActionAttribute() { }
public AspMvcActionAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
[NotNull]
public string AnonymousProperty { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC area.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcAreaAttribute : PathReferenceAttribute
{
public AspMvcAreaAttribute() { }
public AspMvcAreaAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
[NotNull]
public string AnonymousProperty { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that
/// the parameter is an MVC controller. If applied to a method,
/// the MVC controller name is calculated implicitly from the context.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcControllerAttribute : Attribute
{
public AspMvcControllerAttribute() { }
public AspMvcControllerAttribute([NotNull] string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
[NotNull]
public string AnonymousProperty { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Controller.View(String, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcMasterAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Controller.View(String, Object)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcModelTypeAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that
/// the parameter is an MVC partial view. If applied to a method,
/// the MVC partial view name is calculated implicitly from the context.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcPartialViewAttribute : PathReferenceAttribute { }
/// <summary>
/// ASP.NET MVC attribute. Allows disabling all inspections
/// for MVC views within a class or a method.
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
internal sealed class AspMvcSupressViewErrorAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcDisplayTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcEditorTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC template.
/// Use this attribute for custom wrappers similar to
/// <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter)]
internal sealed class AspMvcTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC view. If applied to a method, the MVC view name is calculated implicitly
/// from the context. Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Controller.View(Object)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
internal sealed class AspMvcViewAttribute : PathReferenceAttribute { }
/// <summary>
/// ASP.NET MVC attribute. When applied to a parameter of an attribute,
/// indicates that this parameter is an MVC action name
/// </summary>
/// <example><code>
/// [ActionName("Foo")]
/// public ActionResult Login(string returnUrl) {
/// ViewBag.ReturnUrl = Url.Action("Foo"); // OK
/// return RedirectToAction("Bar"); // Error: Cannot resolve action
/// }
/// </code></example>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
internal sealed class AspMvcActionSelectorAttribute : Attribute { }
[Conditional("DEBUG"), AttributeUsage(
AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Field, Inherited = true)]
internal sealed class HtmlElementAttributesAttribute : Attribute
{
public HtmlElementAttributesAttribute() { }
public HtmlElementAttributesAttribute([NotNull] string name)
{
Name = name;
}
[NotNull]
public string Name { get; private set; }
}
[Conditional("DEBUG"), AttributeUsage(
AttributeTargets.Parameter | AttributeTargets.Field |
AttributeTargets.Property, Inherited = true)]
internal sealed class HtmlAttributeValueAttribute : Attribute
{
public HtmlAttributeValueAttribute([NotNull] string name)
{
Name = name;
}
[NotNull]
public string Name { get; private set; }
}
// Razor attributes
/// <summary>
/// Razor attribute. Indicates that a parameter or a method is a Razor section.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>
/// </summary>
[Conditional("DEBUG"), AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, Inherited = true)]
internal sealed class RazorSectionAttribute : Attribute { }
}

20
external/NWebsec.Annotations/NWebsec.Annotations.xproj

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>144226e7-3eb3-459d-89c8-4d8720e1ef1e</ProjectGuid>
<RootNamespace>NWebsec.Annotations</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

26
external/NWebsec.Annotations/Properties/AssemblyInfo.cs

@ -0,0 +1,26 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NWebsec.Annotations")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NWebsec.Annotations")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("144226e7-3eb3-459d-89c8-4d8720e1ef1e")]
[assembly: InternalsVisibleTo("NWebsec.Core")]
[assembly: InternalsVisibleTo("NWebsec.Middleware")]
[assembly: InternalsVisibleTo("NWebsec.Mvc")]

18
external/NWebsec.Annotations/project.json

@ -0,0 +1,18 @@
{
"version": "1.0.0-*",
"description": "NWebsec.Annotations Class Library",
"authors": [ "klings" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"frameworks": {
"dnx451": { },
"net451": { },
"dotnet5.4": {
"dependencies": {
"Microsoft.CSharp": "4.0.1-beta-23516"
}
}
}
}

13
external/NWebsec.Core/Exceptions/RedirectValidationException.cs

@ -0,0 +1,13 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Core.Exceptions
{
public class RedirectValidationException : Exception
{
public RedirectValidationException(string message) : base(message)
{
}
}
}

21
external/NWebsec.Core/Extensions/HttpContextExtensions.cs

@ -0,0 +1,21 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using NWebsec.Annotations;
namespace NWebsec.Core.Extensions
{
public static class HttpContextExtensions
{
[NotNull]
public static NWebsecContext GetNWebsecContext(this HttpContext context)
{
if (!context.Items.ContainsKey(NWebsecContext.ContextKey))
{
context.Items[NWebsecContext.ContextKey] = new NWebsecContext();
}
return context.Items[NWebsecContext.ContextKey] as NWebsecContext;
}
}
}

53
external/NWebsec.Core/Fluent/IFluentInterface.cs

@ -0,0 +1,53 @@
//This code was lended from http://bit.ly/ifluentinterface, and is not covered by the regular NWebsec lisence.
using System;
using System.ComponentModel;
namespace NWebsec.Core.Fluent
{
/// <summary>
/// Interface that is used to build fluent interfaces and hides methods declared by <see cref="object" /> from
/// IntelliSense.
/// </summary>
/// <remarks>
/// Code that consumes implementations of this interface should expect one of two things:
/// <list type="number">
/// <item>
/// When referencing the interface from within the same solution (project reference), you will still see the
/// methods this interface is meant to hide.
/// </item>
/// <item>
/// When referencing the interface through the compiled output assembly (external reference), the standard
/// Object methods will be hidden as intended.
/// </item>
/// </list>
/// See http://bit.ly/ifluentinterface for more information.
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IFluentInterface
{
/// <summary>
/// Redeclaration that hides the <see cref="object.GetType()" /> method from IntelliSense.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
Type GetType();
/// <summary>
/// Redeclaration that hides the <see cref="object.GetHashCode()" /> method from IntelliSense.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
int GetHashCode();
/// <summary>
/// Redeclaration that hides the <see cref="object.ToString()" /> method from IntelliSense.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
string ToString();
/// <summary>
/// Redeclaration that hides the <see cref="object.Equals(object)" /> method from IntelliSense.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
bool Equals(object obj);
}
}

29
external/NWebsec.Core/Helpers/HeaderResultHandler.cs

@ -0,0 +1,29 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using NWebsec.Core.HttpHeaders;
namespace NWebsec.Core.Helpers
{
public class HeaderResultHandler : IHeaderResultHandler
{
public void HandleHeaderResult(HttpResponse response, HeaderResult result)
{
if (result == null)
{
return;
}
switch (result.Action)
{
case HeaderResult.ResponseAction.Set:
response.Headers[result.Name] = result.Value;
return;
case HeaderResult.ResponseAction.Remove:
response.Headers.Remove(result.Name);
return;
}
}
}
}

12
external/NWebsec.Core/Helpers/IHeaderResultHandler.cs

@ -0,0 +1,12 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using NWebsec.Core.HttpHeaders;
namespace NWebsec.Core.Helpers
{
public interface IHeaderResultHandler
{
void HandleHeaderResult(HttpResponse response, HeaderResult result);
}
}

11
external/NWebsec.Core/Helpers/X509/TlvTripletHeader.cs

@ -0,0 +1,11 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.Helpers.X509
{
internal class TlvTripletHeader
{
public byte Tag { get; set; }
public int Length { get; set; }
public byte[] RawData { get; set; }
}
}

252
external/NWebsec.Core/Helpers/X509/X509Helper.cs

@ -0,0 +1,252 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace NWebsec.Core.Helpers.X509
{
public class X509Helper
{
private const byte AsnInteger = 0x02;
private const byte AsnBitString = 0x03;
private const byte AsnSequence = 0x30;
private const byte AsnOptional = 0xA0;
private static readonly byte[] AsnTags = { AsnInteger, AsnBitString, AsnSequence, AsnOptional };
//TODO cleanup. Perhaps a test or two.
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands"), SecuritySafeCritical]
public X509Certificate2 GetCertByThumbprint(string thumbprint, StoreLocation storeLocation, StoreName storeName)
{
X509Store certStore = null;
X509Certificate2Collection certs = null;
try
{
certStore = new X509Store(storeName, storeLocation);
certStore.Open(OpenFlags.ReadOnly);
certs = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certs.Count > 1)
{
var message = string.Format("Something went horribly wrong, found more than one cert with thumbprint {0} in store location {1}, storename {2}", thumbprint, storeLocation, storeName);
throw new Exception(message);
}
if (certs.Count == 0)
{
var message = string.Format("No certificate with thumbprint {0} in store location {1}, storename {2}", thumbprint, storeLocation, storeName);
throw new ArgumentException(message);
}
//Returns new cert, all existing certs will be cleaned up
return certs[0];
}
catch
{
if (certs != null)
{
foreach (var cert in certs)
{
CleanupCert(cert);
}
}
if (certStore != null)
{
foreach (var cert in certStore.Certificates)
{
CleanupCert(cert);
}
#if DNX451
certStore.Close();
#elif NET451
certStore.Close();
#else
certStore.Dispose();
#endif
}
throw;
}
}
/// <summary>
/// Returns a string suitable for inclusion in an HPKP header, including hash algoritm.
/// </summary>
/// <param name="cert"></param>
/// <returns></returns>
public string GetSubjectPublicKeyInfoPinValue(X509Certificate2 cert)
{
var spki = GetRawSubjectPublicKeyInfo(cert);
using (var sha256 = SHA256.Create())
{
var hash = Convert.ToBase64String(sha256.ComputeHash(spki));
return string.Format("sha256=\"" + hash + "\"");
}
}
private static byte[] GetRawSubjectPublicKeyInfo(X509Certificate2 cert)
{
if (cert.Version != 3)
{
throw new ArgumentException("Only X.509 certificate version 3 is supported. This cert was version " + cert.Version);
}
var rawCert = cert.RawData;
using (var ms = new MemoryStream(rawCert))
{
//Get outer cert sequence header
var tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence, for start of certificate.");
//Console.WriteLine("Got certficate sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
//Get tbs cert sequence header
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence, for start of tbc cert.");
//Console.WriteLine("Got tbs certficate sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
//Get cert version integer header
tlv = ReadTlvTripletHeader(ms);
while (tlv.Tag == AsnOptional)
{
//Console.WriteLine("Got optional TLV, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
tlv = ReadTlvTripletHeader(ms);
}
if (tlv.Tag != AsnInteger) throw new Exception("Expected ASN integer cert version.");
//Console.WriteLine("Got the certficate version, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
var version = ms.ReadByte();
if (version == -1) throw new Exception("Could not read version byte");
//Console.WriteLine("Cert version: " + version);
//Get serial number
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnInteger) throw new Exception("Expected ASN integer serial number.");
//Console.WriteLine("Got the cert serial number, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
var serialNumber = new byte[tlv.Length];
var read = ms.Read(serialNumber, 0, serialNumber.Length);
if (read < serialNumber.Length) throw new Exception("Expected reading " + tlv.Length + " serial number bytes, got " + read);
//Skip signature sequence
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence signature.");
//Console.WriteLine("Got the cert signature sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
ms.Seek(tlv.Length, SeekOrigin.Current);
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
//Skip issuer sequence
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence issuer.");
//Console.WriteLine("Got the cert issuer sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
ms.Seek(tlv.Length, SeekOrigin.Current);
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
//Skip validity sequence
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence validity.");
//Console.WriteLine("Got the cert validity sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
ms.Seek(tlv.Length, SeekOrigin.Current);
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
//Skip subject sequence
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence subject.");
//Console.WriteLine("Got the cert subject sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
ms.Seek(tlv.Length, SeekOrigin.Current);
//Console.WriteLine("Skipped ahead " + tlv.Length + " bytes.");
//Skip subject sequence
tlv = ReadTlvTripletHeader(ms);
if (tlv.Tag != AsnSequence) throw new Exception("Expected ASN sequence SPKI.");
//Console.WriteLine("Got the cert SPKI sequence, parsed length: " + tlv.Length + " " + BitConverter.ToString(tlv.RawData));
//New array for both tlv bits and content bits.
var spkiChunk = new byte[tlv.RawData.Length + tlv.Length];
Array.Copy(tlv.RawData, spkiChunk, tlv.RawData.Length);
read = ms.Read(spkiChunk, tlv.RawData.Length, tlv.Length);
if (read > tlv.Length) throw new Exception("Got " + read + " SPKI bytes, expected " + spkiChunk.Length);
return spkiChunk;
}
}
private static TlvTripletHeader ReadTlvTripletHeader(MemoryStream ms)
{
var firstBytes = new byte[2];
var read = ms.Read(firstBytes, 0, firstBytes.Length);
if (read < 1)
{
throw new Exception("No data read!");
}
if (!AsnTags.Any(t => t == firstBytes[0])) throw new Exception("Unexptected ASN.1 tag byte: " + BitConverter.ToString(firstBytes, 0, 1));
if (read < 2)
{
throw new Exception("No length byte read!");
}
if (firstBytes[1] < 0x80)
{
return new TlvTripletHeader() { Tag = firstBytes[0], Length = firstBytes[1], RawData = firstBytes };
}
//Handle multi-byte length.
var numberOfLengthBytes = firstBytes[1] - 0x80;
if (numberOfLengthBytes < 1) throw new Exception("Invalid length byte. Indicated multibyte length, with length 0.");
if (numberOfLengthBytes > 4) throw new NotSupportedException("Leading length byte indicates more than 4 length bytes, which is not supported. Indicated length bytes: " + numberOfLengthBytes);
//Get the bytes
var lengthBytes = new byte[numberOfLengthBytes];
var bytesRead = ms.Read(lengthBytes, 0, lengthBytes.Length);
if (bytesRead != lengthBytes.Length) throw new Exception(string.Format("Expected {0} length bytes, got {1}", lengthBytes.Length, bytesRead));
//Got the bytes, make an int.
var length = 0;
//Console.WriteLine("Adding length bytes: " + BitConverter.ToString(lengthBytes));
foreach (var lengthByte in lengthBytes)
{
//Shift existing bytes so they become more significant. Avoid platform dependent bit fiddling.
//Console.WriteLine("Length tweak starting: " + BitConverter.ToString(BitConverter.GetBytes(length)));
length = length * 256;
//Console.WriteLine("Shifted length to make room for next byte: " + BitConverter.ToString(BitConverter.GetBytes(length)));
length += lengthByte;
//Console.WriteLine("Added next byte: " + BitConverter.ToString(BitConverter.GetBytes(length)));
}
var rawbytes = new byte[firstBytes.Length + lengthBytes.Length];
Array.Copy(firstBytes, rawbytes, firstBytes.Length);
Array.Copy(lengthBytes, 0, rawbytes, firstBytes.Length, lengthBytes.Length);
return new TlvTripletHeader { Tag = firstBytes[0], Length = length, RawData = rawbytes };
}
private void CleanupCert(X509Certificate2 cert)
{
#if NET451
cert.Reset();
#elif DNX451
cert.Reset();
#else
cert.Dispose();
#endif
}
}
}

52
external/NWebsec.Core/HttpHeaders/Configuration/CspConfiguration.cs

@ -0,0 +1,52 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspConfiguration : ICspConfiguration
{
public CspConfiguration(bool initializeDirectives=true)
{
if (!initializeDirectives)
{
return;
}
DefaultSrcDirective = new CspDirectiveConfiguration();
ScriptSrcDirective = new CspDirectiveConfiguration();
ObjectSrcDirective = new CspDirectiveConfiguration();
StyleSrcDirective = new CspDirectiveConfiguration();
ImgSrcDirective = new CspDirectiveConfiguration();
MediaSrcDirective = new CspDirectiveConfiguration();
FrameSrcDirective = new CspDirectiveConfiguration();
FontSrcDirective = new CspDirectiveConfiguration();
ConnectSrcDirective = new CspDirectiveConfiguration();
BaseUriDirective = new CspDirectiveConfiguration();
ChildSrcDirective = new CspDirectiveConfiguration();
FormActionDirective = new CspDirectiveConfiguration();
FrameAncestorsDirective = new CspDirectiveConfiguration();
PluginTypesDirective = new CspPluginTypesDirectiveConfiguration();
SandboxDirective = new CspSandboxDirectiveConfiguration();
UpgradeInsecureRequestsDirective = new CspUpgradeDirectiveConfiguration();
ReportUriDirective = new CspReportUriDirectiveConfiguration();
}
public bool Enabled { get; set; }
public ICspDirectiveConfiguration DefaultSrcDirective { get; set; }
public ICspDirectiveConfiguration ScriptSrcDirective { get; set; }
public ICspDirectiveConfiguration ObjectSrcDirective { get; set; }
public ICspDirectiveConfiguration StyleSrcDirective { get; set; }
public ICspDirectiveConfiguration ImgSrcDirective { get; set; }
public ICspDirectiveConfiguration MediaSrcDirective { get; set; }
public ICspDirectiveConfiguration FrameSrcDirective { get; set; }
public ICspDirectiveConfiguration FontSrcDirective { get; set; }
public ICspDirectiveConfiguration ConnectSrcDirective { get; set; }
public ICspDirectiveConfiguration BaseUriDirective { get; set; }
public ICspDirectiveConfiguration ChildSrcDirective { get; set; }
public ICspDirectiveConfiguration FormActionDirective { get; set; }
public ICspDirectiveConfiguration FrameAncestorsDirective { get; set; }
public ICspPluginTypesDirectiveConfiguration PluginTypesDirective { get; set; }
public ICspSandboxDirectiveConfiguration SandboxDirective { get; set; }
public ICspUpgradeDirectiveConfiguration UpgradeInsecureRequestsDirective { get; set; }
public ICspReportUriDirectiveConfiguration ReportUriDirective { get; set; }
}
}

26
external/NWebsec.Core/HttpHeaders/Configuration/CspDirectiveConfiguration.cs

@ -0,0 +1,26 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspDirectiveConfiguration : ICspDirectiveConfiguration
{
private static readonly string[] EmptySources = new string[0];
public CspDirectiveConfiguration()
{
Enabled = true;
CustomSources = EmptySources;
}
public bool Enabled { get; set; }
public bool NoneSrc { get; set; }
public bool SelfSrc { get; set; }
public bool UnsafeInlineSrc { get; set; }
public bool UnsafeEvalSrc { get; set; }
public IEnumerable<string> CustomSources { get; set; }
public string Nonce { get; set; }
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/CspHeaderConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspHeaderConfiguration : ICspHeaderConfiguration
{
public bool Enabled { get; set; }
}
}

20
external/NWebsec.Core/HttpHeaders/Configuration/CspPluginTypesDirectiveConfiguration.cs

@ -0,0 +1,20 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspPluginTypesDirectiveConfiguration : ICspPluginTypesDirectiveConfiguration
{
private static readonly string[] EmptySources = new string[0];
public bool Enabled { get; set; }
public IEnumerable<string> MediaTypes { get; set; }
public CspPluginTypesDirectiveConfiguration()
{
Enabled = true;
MediaTypes = EmptySources;
}
}
}

19
external/NWebsec.Core/HttpHeaders/Configuration/CspReportUriDirectiveConfiguration.cs

@ -0,0 +1,19 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspReportUriDirectiveConfiguration : ICspReportUriDirectiveConfiguration
{
public CspReportUriDirectiveConfiguration()
{
ReportUris = new string[0];
}
public bool Enabled { get; set; }
//TODO figure out what to do with this property
public bool EnableBuiltinHandler { get; set; }
public IEnumerable<string> ReportUris { get; set; }
}
}

15
external/NWebsec.Core/HttpHeaders/Configuration/CspSandboxDirectiveConfiguration.cs

@ -0,0 +1,15 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspSandboxDirectiveConfiguration : ICspSandboxDirectiveConfiguration
{
public bool Enabled { get; set; }
public bool AllowForms { get; set; }
public bool AllowPointerLock { get; set; }
public bool AllowPopups { get; set; }
public bool AllowSameOrigin { get; set; }
public bool AllowScripts { get; set; }
public bool AllowTopNavigation { get; set; }
}
}

11
external/NWebsec.Core/HttpHeaders/Configuration/CspUpgradeDirectiveConfiguration.cs

@ -0,0 +1,11 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class CspUpgradeDirectiveConfiguration : ICspUpgradeDirectiveConfiguration
{
public bool Enabled { get; set; }
public int HttpsPort { get; set; } = 443;
}
}

16
external/NWebsec.Core/HttpHeaders/Configuration/HpkpConfiguration.cs

@ -0,0 +1,16 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class HpkpConfiguration : IHpkpConfiguration
{
public IEnumerable<string> Pins { get; set; }
public TimeSpan MaxAge { get; set; }
public bool IncludeSubdomains { get; set; }
public string ReportUri { get; set; }
public bool HttpsOnly { get; set; }
}
}

15
external/NWebsec.Core/HttpHeaders/Configuration/HstsConfiguration.cs

@ -0,0 +1,15 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class HstsConfiguration : IHstsConfiguration
{
public TimeSpan MaxAge { get; set; }
public bool IncludeSubdomains { get; set; }
public bool Preload { get; set; }
public bool HttpsOnly { get; set; }
public bool UpgradeInsecureRequests { get; set; }
}
}

30
external/NWebsec.Core/HttpHeaders/Configuration/ICspConfiguration.cs

@ -0,0 +1,30 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface ICspConfiguration
{
bool Enabled { get; set; }
ICspDirectiveConfiguration DefaultSrcDirective { get; set; }
ICspDirectiveConfiguration ScriptSrcDirective { get; set; }
ICspDirectiveConfiguration ObjectSrcDirective { get; set; }
ICspDirectiveConfiguration StyleSrcDirective { get; set; }
ICspDirectiveConfiguration ImgSrcDirective { get; set; }
ICspDirectiveConfiguration MediaSrcDirective { get; set; }
ICspDirectiveConfiguration FrameSrcDirective { get; set; }
ICspDirectiveConfiguration FontSrcDirective { get; set; }
ICspDirectiveConfiguration ConnectSrcDirective { get; set; }
ICspReportUriDirectiveConfiguration ReportUriDirective { get; set; }
//CSP 2
ICspDirectiveConfiguration BaseUriDirective { get; set; }
ICspDirectiveConfiguration ChildSrcDirective { get; set; }
ICspDirectiveConfiguration FormActionDirective { get; set; }
ICspDirectiveConfiguration FrameAncestorsDirective { get; set; }
ICspPluginTypesDirectiveConfiguration PluginTypesDirective { get; set; }
ICspSandboxDirectiveConfiguration SandboxDirective { get; set; }
//Upgrade insecure requests
ICspUpgradeDirectiveConfiguration UpgradeInsecureRequestsDirective { get; set; }
}
}

42
external/NWebsec.Core/HttpHeaders/Configuration/ICspDirectiveBasicConfiguration.cs

@ -0,0 +1,42 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using NWebsec.Core.Fluent;
namespace NWebsec.Core.HttpHeaders.Configuration
{
/// <summary>
/// Defines the properties required for CSP directive configuration.
/// </summary>
public interface ICspDirectiveBasicConfiguration : IFluentInterface
{
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
bool Enabled { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
bool NoneSrc { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
bool SelfSrc { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerable<string> CustomSources { get; set; }
}
}

19
external/NWebsec.Core/HttpHeaders/Configuration/ICspDirectiveConfiguration.cs

@ -0,0 +1,19 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.ComponentModel;
namespace NWebsec.Core.HttpHeaders.Configuration
{
/// <summary>
/// Defines the properties required for CSP directive configuration.
/// </summary>
public interface ICspDirectiveConfiguration : ICspDirectiveUnsafeInlineConfiguration
{
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
bool UnsafeEvalSrc { get; set; }
}
}

26
external/NWebsec.Core/HttpHeaders/Configuration/ICspDirectiveUnsafeInlineConfiguration.cs

@ -0,0 +1,26 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.ComponentModel;
namespace NWebsec.Core.HttpHeaders.Configuration
{
/// <summary>
/// Defines the properties required for CSP directive configuration.
/// </summary>
public interface ICspDirectiveUnsafeInlineConfiguration : ICspDirectiveBasicConfiguration
{
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
bool UnsafeInlineSrc { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
string Nonce { get; set; }
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/ICspHeaderConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface ICspHeaderConfiguration
{
bool Enabled { get; set; }
}
}

24
external/NWebsec.Core/HttpHeaders/Configuration/ICspPluginTypesDirectiveConfiguration.cs

@ -0,0 +1,24 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
/// <summary>
/// Defines the properties required for CSP sandbox directive configuration.
/// </summary>
public interface ICspPluginTypesDirectiveConfiguration
{
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool Enabled { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
IEnumerable<string> MediaTypes { get; set; }
}
}

15
external/NWebsec.Core/HttpHeaders/Configuration/ICspReportUriDirectiveConfiguration.cs

@ -0,0 +1,15 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface ICspReportUriDirectiveConfiguration
{
bool Enabled { get; set; }
bool EnableBuiltinHandler { get; set; }
IEnumerable<string> ReportUris { get; set; }
}
}

49
external/NWebsec.Core/HttpHeaders/Configuration/ICspSandboxDirectiveConfiguration.cs

@ -0,0 +1,49 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
/// <summary>
/// Defines the properties required for CSP sandbox directive configuration.
/// </summary>
public interface ICspSandboxDirectiveConfiguration
{
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool Enabled { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool AllowForms { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool AllowPointerLock { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool AllowPopups { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool AllowSameOrigin { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool AllowScripts { get; set; }
/// <summary>
/// Infrastructure. Not intended to be used by your code directly. An attempt to hide this from Intellisense has been
/// made.
/// </summary>
bool AllowTopNavigation { get; set; }
}
}

14
external/NWebsec.Core/HttpHeaders/Configuration/ICspUpgradeDirectiveConfiguration.cs

@ -0,0 +1,14 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
/// <summary>
/// Defines the properties required for CSP sandbox directive configuration.
/// </summary>
public interface ICspUpgradeDirectiveConfiguration
{
bool Enabled { get; set; }
int HttpsPort { get; set; }
}
}

14
external/NWebsec.Core/HttpHeaders/Configuration/IHpkpCertConfiguration.cs

@ -0,0 +1,14 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Security.Cryptography.X509Certificates;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IHpkpCertConfiguration
{
string ThumbPrint { get; set; }
StoreLocation StoreLocation { get; set; }
StoreName Storename { get; set; }
string SpkiPinValue { get; set; }
}
}

16
external/NWebsec.Core/HttpHeaders/Configuration/IHpkpConfiguration.cs

@ -0,0 +1,16 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IHpkpConfiguration
{
IEnumerable<string> Pins { get; set; }
TimeSpan MaxAge { get; set; }
bool IncludeSubdomains { get; set; }
string ReportUri { get; set; }
bool HttpsOnly { get; set; }
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/IHpkpPinConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IHpkpPinConfiguration
{
string Pin { get; set; }
}
}

19
external/NWebsec.Core/HttpHeaders/Configuration/IHstsConfiguration.cs

@ -0,0 +1,19 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IHstsConfiguration
{
TimeSpan MaxAge { get; set; }
bool IncludeSubdomains { get; set; }
bool Preload { get; set; }
bool HttpsOnly { get; set; }
bool UpgradeInsecureRequests { get; set; }
}
}

18
external/NWebsec.Core/HttpHeaders/Configuration/IRedirectValidationConfiguration.cs

@ -0,0 +1,18 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IRedirectValidationConfiguration
{
bool Enabled { get; set; }
/// <summary>
/// URIs allowed for redirect. Strings in this list should be created with Uri.AbsoluteUri to assure consistency.
/// </summary>
IEnumerable<string> AllowedUris { get; set; }
ISameHostHttpsRedirectConfiguration SameHostRedirectConfiguration { get; set; }
}
}

10
external/NWebsec.Core/HttpHeaders/Configuration/ISameHostHttpsRedirectConfiguration.cs

@ -0,0 +1,10 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface ISameHostHttpsRedirectConfiguration
{
bool Enabled { get; set; }
int[] Ports { get; set; }
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/ISimpleBooleanConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface ISimpleBooleanConfiguration
{
bool Enabled { get; set; }
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/IXFrameOptionsConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IXFrameOptionsConfiguration
{
XfoPolicy Policy { get; set; }
}
}

23
external/NWebsec.Core/HttpHeaders/Configuration/IXRobotsTagConfiguration.cs

@ -0,0 +1,23 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IXRobotsTagConfiguration
{
bool Enabled { get; set; }
bool NoIndex { get; set; }
bool NoFollow { get; set; }
bool NoSnippet { get; set; }
bool NoArchive { get; set; }
bool NoOdp { get; set; }
bool NoTranslate { get; set; }
bool NoImageIndex { get; set; }
}
}

11
external/NWebsec.Core/HttpHeaders/Configuration/IXXssProtectionConfiguration.cs

@ -0,0 +1,11 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public interface IXXssProtectionConfiguration
{
XXssPolicy Policy { get; set; }
bool BlockMode { get; set; }
}
}

19
external/NWebsec.Core/HttpHeaders/Configuration/RedirectValidationConfiguration.cs

@ -0,0 +1,19 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace NWebsec.Core.HttpHeaders.Configuration
{
internal class RedirectValidationConfiguration : IRedirectValidationConfiguration
{
public RedirectValidationConfiguration()
{
AllowedUris = new string[0];
SameHostRedirectConfiguration = new SameHostHttpsRedirectConfiguration();
}
public bool Enabled { get; set; }
public IEnumerable<string> AllowedUris { get; set; }
public ISameHostHttpsRedirectConfiguration SameHostRedirectConfiguration { get; set; }
}
}

15
external/NWebsec.Core/HttpHeaders/Configuration/SameHostHttpsRedirectConfiguration.cs

@ -0,0 +1,15 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class SameHostHttpsRedirectConfiguration : ISameHostHttpsRedirectConfiguration
{
public SameHostHttpsRedirectConfiguration()
{
Ports = new int[0];
}
public bool Enabled { get; set; }
public int[] Ports { get; set; }
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/SimpleBooleanConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class SimpleBooleanConfiguration : ISimpleBooleanConfiguration
{
public bool Enabled { get; set; }
}
}

55
external/NWebsec.Core/HttpHeaders/Configuration/Validation/HpkpConfigurationValidator.cs

@ -0,0 +1,55 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace NWebsec.Core.HttpHeaders.Configuration.Validation
{
public class HpkpConfigurationValidator
{
private static readonly string[] ValidSchemes = { "http", "https" };
public void ValidateNumberOfPins(IHpkpConfiguration hpkpConfig)
{
if (hpkpConfig.MaxAge > TimeSpan.Zero && hpkpConfig.Pins.Count() < 2)
{
throw new Exception("You must supply two or more HPKP pins. One should represent a certificate currently in use, you should also include a backup pin for a cert/key not (yet) in use.");
}
}
public void ValidateRawPin(string pin)
{
var bytes = Convert.FromBase64String(pin);
if (bytes.Length != 32)
{
throw new Exception("Expected a 256 bit pin value, it was " + bytes.Length * 8 + " bits: " + pin);
}
}
public void ValidateThumbprint(string thumbPrint)
{
if (Regex.IsMatch(thumbPrint, "^([a-fA-F0-9]{2} ?){19}[a-fA-F0-9]{2}$"))
{
return;
}
throw new Exception("Malformed thumbprint, expected 20 HEX octets without any leading or trailing whitespace, was: " + thumbPrint);
}
public void ValidateReportUri(string reportUri)
{
Uri result;
if (!Uri.TryCreate(reportUri, UriKind.Absolute, out result))
{
throw new Exception("Report URIs must be absolute URIs. This is not: " + reportUri);
}
if (!ValidSchemes.Any(s => s.Equals(result.Scheme)))
{
throw new Exception("Report URIs must have the http or https scheme. Got: " + reportUri);
}
}
}
}

24
external/NWebsec.Core/HttpHeaders/Configuration/Validation/HstsConfigurationValidator.cs

@ -0,0 +1,24 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Core.HttpHeaders.Configuration.Validation
{
public class HstsConfigurationValidator
{
public void Validate(IHstsConfiguration hstsConfig)
{
if (!hstsConfig.Preload) return;
if (hstsConfig.UpgradeInsecureRequests)
{
throw new Exception("The Preload setting cannot be combined with the UpgradeInsecureRequests setting. Use one or the other.");
}
if (hstsConfig.MaxAge.TotalSeconds < 10886400 || !hstsConfig.IncludeSubdomains)
{
throw new Exception("HSTS max age must be at least 18 weeks and includesubdomains must be enabled to use the preload directive.");
}
}
}
}

51
external/NWebsec.Core/HttpHeaders/Configuration/Validation/Rfc2045MediaTypeValidator.cs

@ -0,0 +1,51 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace NWebsec.Core.HttpHeaders.Configuration.Validation
{
public class Rfc2045MediaTypeValidator
{
private static readonly string[] ValidTypes = { "application", "audio", "image","model", "text", "video" };
public void Validate(string mediaType)
{
if (String.IsNullOrEmpty(mediaType)) throw new ArgumentException("String was null or empty", "mediaType");
var components = mediaType.Split(new[] { '/' }, 2);
var type = components[0];
if (!ValidTypes.Any(t => t.Equals(type, StringComparison.OrdinalIgnoreCase)))
{
var message = String.Format("Media type \"{0}\" did not match any of the expected types: {1}", mediaType, String.Join(", ", ValidTypes));
throw new Exception(message);
}
if (components.Length != 2)
{
throw new Exception("Invalid format for media type. Expected \"type/subtype\" but was: " + mediaType);
}
var subType = components[1];
if (!Regex.IsMatch(subType, @"^[\x00-\x7F]*$"))
{
throw new Exception("Subtype contained characters from outside the US-ASCII range, was: " + subType);
}
if (Regex.IsMatch(subType, @"[\x00-\x20\x7F]+"))
{
throw new Exception("Subtype contained the space character, or an ASCII control character.");
}
var escapedTspecials = @"[()<>@,;:""\\/[\]?=]+";
if (Regex.IsMatch(subType, escapedTspecials))
{
throw new Exception("Subtype contained one of the forbidden tspecial characters: " + Regex.Unescape(escapedTspecials));
}
}
}
}

24
external/NWebsec.Core/HttpHeaders/Configuration/Validation/XRobotsTagConfigurationValidator.cs

@ -0,0 +1,24 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Core.HttpHeaders.Configuration.Validation
{
public class XRobotsTagConfigurationValidator
{
public void Validate(IXRobotsTagConfiguration xRobotsConfig)
{
if (!xRobotsConfig.Enabled) return;
if (xRobotsConfig.NoArchive ||
xRobotsConfig.NoFollow ||
xRobotsConfig.NoImageIndex ||
xRobotsConfig.NoIndex ||
xRobotsConfig.NoOdp ||
xRobotsConfig.NoSnippet ||
xRobotsConfig.NoTranslate) return;
throw new Exception(
"One or more directives must be enabled when header is enabled. Enable directives or disable header.");
}
}
}

9
external/NWebsec.Core/HttpHeaders/Configuration/XFrameOptionsConfiguration.cs

@ -0,0 +1,9 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class XFrameOptionsConfiguration : IXFrameOptionsConfiguration
{
public XfoPolicy Policy { get; set; }
}
}

16
external/NWebsec.Core/HttpHeaders/Configuration/XRobotsTagConfiguration.cs

@ -0,0 +1,16 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class XRobotsTagConfiguration : IXRobotsTagConfiguration
{
public bool Enabled { get; set; }
public bool NoIndex { get; set; }
public bool NoFollow { get; set; }
public bool NoSnippet { get; set; }
public bool NoArchive { get; set; }
public bool NoOdp { get; set; }
public bool NoTranslate { get; set; }
public bool NoImageIndex { get; set; }
}
}

10
external/NWebsec.Core/HttpHeaders/Configuration/XXssProtectionConfiguration.cs

@ -0,0 +1,10 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Configuration
{
public class XXssProtectionConfiguration : IXXssProtectionConfiguration
{
public XXssPolicy Policy { get; set; }
public bool BlockMode { get; set; }
}
}

12
external/NWebsec.Core/HttpHeaders/Csp/CspSourceParseResult.cs

@ -0,0 +1,12 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders.Csp
{
internal class CspSourceParseResult
{
public string Scheme { get; set; }
public string Host { get; set; }
public string Port { get; set; }
public string PathAndQuery { get; set; }
}
}

172
external/NWebsec.Core/HttpHeaders/Csp/CspUriSource.cs

@ -0,0 +1,172 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace NWebsec.Core.HttpHeaders.Csp
{
public class CspUriSource
{
private const string HostRegex = @"^(\*\.)?([\p{Ll}\p{Lu}0-9\-]+)(\.[\p{Ll}\p{Lu}0-9\-]+)*$";
private static readonly string SchemeOnlyRegex = "^[a-zA-Z]*[a-zA-Z0-9" + Regex.Escape("+.-") + "]:$";
private static readonly string[] KnownSchemes = { "http", "https", "data", "ws", "wss" };
private readonly string _source;
private CspUriSource(string source)
{
_source = source;
}
// Returns the source as a string encoded according to the CSP spec.
public override string ToString()
{
return _source;
}
public static string EncodeUri(Uri uri)
{
if (!uri.IsAbsoluteUri)
{
var uriString = uri.IsWellFormedOriginalString() ? uri.ToString() : Uri.EscapeUriString(uri.ToString());
return EscapeReservedCspChars(uriString);
}
var host = uri.Host;
var encodedHost = EncodeHostname(host);
var needsReplacement = !host.Equals(encodedHost);
var authority = uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped);
if (needsReplacement)
{
authority = authority.Replace(host, encodedHost);
}
if (uri.PathAndQuery.Equals("/"))
{
return authority;
}
return authority + EscapeReservedCspChars(uri.PathAndQuery);
}
public static CspUriSource Parse(string source)
{
if (String.IsNullOrEmpty(source)) throw new ArgumentException("Value was null or empty", "source");
if (source.Equals("*")) return new CspUriSource(source);
Uri uriResult; //TODO figure out what happened to known schemes.
if (Uri.TryCreate(source, UriKind.Absolute, out uriResult) && KnownSchemes.Contains(uriResult.Scheme))
{
return new CspUriSource(EncodeUri(uriResult));
}
//Scheme only source
if (Regex.IsMatch(source, SchemeOnlyRegex)) return new CspUriSource(source.ToLower());
var parseResult = ParseSourceComponents(source);
var sb = new StringBuilder();
if (!String.IsNullOrEmpty(parseResult.Scheme))
{
if (!Regex.IsMatch(parseResult.Scheme, SchemeOnlyRegex))
{
throw new InvalidCspSourceException("Invalid scheme in CSP source: " + source);
}
sb.Append(parseResult.Scheme.ToLower()).Append("//");
}
if (String.IsNullOrEmpty(parseResult.Host))
{
throw new InvalidCspSourceException("Could not parse host in CSP source: " + source);
}
if (!Regex.IsMatch(parseResult.Host, HostRegex))
{
throw new InvalidCspSourceException("Invalid host in CSP source: " + source);
}
sb.Append(EncodeHostname(parseResult.Host.ToLower()));
if (!String.IsNullOrEmpty(parseResult.Port))
{
if (!ValidatePort(parseResult.Port))
{
throw new InvalidCspSourceException("Invalid port in CSP source: " + source);
}
sb.Append(":").Append(parseResult.Port);
}
if (!String.IsNullOrEmpty(parseResult.PathAndQuery))
{
sb.Append(EscapeReservedCspChars(Uri.EscapeUriString(parseResult.PathAndQuery)));
}
return new CspUriSource(sb.ToString());
}
private static CspSourceParseResult ParseSourceComponents(string uri)
{
const string regex = @"^((?<scheme>.*?:)\/\/)?" + // match anything up to ://
@"(?<host>.*?[^:\/])" + //then match anything up to a : or /
@"(:(?<port>(.*?[^\/])))?" + //then match port if exists up to a /
@"(?<pathAndQuery>\/.*)?$"; //grab the rest
var re = new Regex(regex, RegexOptions.ExplicitCapture);
var result = re.Match(uri);
if (!result.Success)
{
throw new InvalidCspSourceException("Malformed CSP source: " + uri);
}
return new CspSourceParseResult
{
Scheme = result.Groups["scheme"].Value,
Host = result.Groups["host"].Value,
Port = result.Groups["port"].Value,
PathAndQuery = result.Groups["pathAndQuery"].Value
};
}
private static string EncodeHostname(string hostname)
{
var idn = new IdnMapping();
return idn.GetAscii(hostname);
}
private static string EscapeReservedCspChars(string pathAndQuery)
{
char[] encodeChars = { ';', ',' };
if (pathAndQuery.IndexOfAny(encodeChars) == -1)
{
return pathAndQuery;
}
var sb = new StringBuilder(pathAndQuery);
sb.Replace(";", "%3B");
sb.Replace(",", "%2C");
return sb.ToString();
}
private static bool ValidatePort(string port)
{
if (port.Equals("*")) return true;
int portNumber;
var isInt = Int32.TryParse(port, out portNumber);
return isInt && portNumber > 0 && portNumber <= 65535;
}
}
}

15
external/NWebsec.Core/HttpHeaders/Csp/InvalidCspSourceException.cs

@ -0,0 +1,15 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Core.HttpHeaders.Csp
{
//[Serializable]
public class InvalidCspSourceException : Exception
{
public InvalidCspSourceException(string s)
: base(s)
{
}
}
}

47
external/NWebsec.Core/HttpHeaders/HeaderConstants.cs

@ -0,0 +1,47 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders
{
public class HeaderConstants
{
public static readonly string XFrameOptionsHeader = "X-Frame-Options";
public static readonly string XRobotsTagHeader = "X-Robots-Tag";
public static readonly string StrictTransportSecurityHeader = "Strict-Transport-Security";
public static readonly string XContentTypeOptionsHeader = "X-Content-Type-Options";
public static readonly string XDownloadOptionsHeader = "X-Download-Options";
public static readonly string XXssProtectionHeader = "X-XSS-Protection";
public static readonly string ContentSecurityPolicyHeader = "Content-Security-Policy";
public static readonly string ContentSecurityPolicyReportOnlyHeader = "Content-Security-Policy-Report-Only";
public static readonly string HpkpHeader = "Public-Key-Pins";
public static readonly string HpkpReportOnlyHeader = "Public-Key-Pins-Report-Only";
public static readonly string[] CspSourceList =
{
"'none'",
"'self'",
"'unsafe-inline'",
"'unsafe-eval'"
};
public static readonly string[] CspDirectives =
{
"default-src",
"script-src",
"object-src",
"style-src",
"img-src",
"media-src",
"frame-src",
"font-src",
"connect-src",
"report-uri"
};
public static readonly string[] CspSchemes =
{
"data:",
"https:",
"http:"
};
}
}

383
external/NWebsec.Core/HttpHeaders/HeaderGenerator.cs

@ -0,0 +1,383 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NWebsec.Annotations;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Core.HttpHeaders
{
public class HeaderGenerator : IHeaderGenerator
{
[CanBeNull]
public HeaderResult CreateXRobotsTagResult(IXRobotsTagConfiguration xRobotsTagConfig,
IXRobotsTagConfiguration oldXRobotsTagConfig = null)
{
if (oldXRobotsTagConfig != null && oldXRobotsTagConfig.Enabled && xRobotsTagConfig.Enabled == false)
{
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XRobotsTagHeader);
}
if (xRobotsTagConfig.Enabled == false)
{
return null;
}
var sb = new StringBuilder();
sb.Append(xRobotsTagConfig.NoIndex ? "noindex, " : String.Empty);
sb.Append(xRobotsTagConfig.NoFollow ? "nofollow, " : String.Empty);
sb.Append(xRobotsTagConfig.NoSnippet && !xRobotsTagConfig.NoIndex ? "nosnippet, " : String.Empty);
sb.Append(xRobotsTagConfig.NoArchive && !xRobotsTagConfig.NoIndex ? "noarchive, " : String.Empty);
sb.Append(xRobotsTagConfig.NoOdp && !xRobotsTagConfig.NoIndex ? "noodp, " : String.Empty);
sb.Append(xRobotsTagConfig.NoTranslate && !xRobotsTagConfig.NoIndex ? "notranslate, " : String.Empty);
sb.Append(xRobotsTagConfig.NoImageIndex ? "noimageindex" : String.Empty);
var value = sb.ToString().TrimEnd(' ', ',');
if (value.Length == 0) return null;
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XRobotsTagHeader, value);
}
[CanBeNull]
public HeaderResult CreateHstsResult(IHstsConfiguration hstsConfig)
{
if (hstsConfig.MaxAge < TimeSpan.Zero) return null;
if (hstsConfig.Preload && (hstsConfig.MaxAge.TotalSeconds < 10886400 || !hstsConfig.IncludeSubdomains))
{
return null;
}
var seconds = (int)hstsConfig.MaxAge.TotalSeconds;
var includeSubdomains = (hstsConfig.IncludeSubdomains ? "; includeSubdomains" : "");
var preload = (hstsConfig.Preload ? "; preload" : "");
var value = string.Format("max-age={0}{1}{2}", seconds, includeSubdomains, preload);
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.StrictTransportSecurityHeader,
value);
}
[CanBeNull]
public HeaderResult CreateXContentTypeOptionsResult(ISimpleBooleanConfiguration xContentTypeOptionsConfig,
ISimpleBooleanConfiguration oldXContentTypeOptionsConfig = null)
{
if (oldXContentTypeOptionsConfig != null && oldXContentTypeOptionsConfig.Enabled &&
!xContentTypeOptionsConfig.Enabled)
{
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XContentTypeOptionsHeader);
}
return xContentTypeOptionsConfig.Enabled
? new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XContentTypeOptionsHeader, "nosniff")
: null;
}
[CanBeNull]
public HeaderResult CreateXDownloadOptionsResult(ISimpleBooleanConfiguration xDownloadOptionsConfig,
ISimpleBooleanConfiguration oldXDownloadOptionsConfig = null)
{
if (oldXDownloadOptionsConfig != null && oldXDownloadOptionsConfig.Enabled &&
!xDownloadOptionsConfig.Enabled)
{
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XDownloadOptionsHeader);
}
return xDownloadOptionsConfig.Enabled
? new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XDownloadOptionsHeader, "noopen")
: null;
}
[CanBeNull]
public HeaderResult CreateXXssProtectionResult(IXXssProtectionConfiguration xXssProtectionConfig,
IXXssProtectionConfiguration oldXXssProtectionConfig = null)
{
if (oldXXssProtectionConfig != null && oldXXssProtectionConfig.Policy != XXssPolicy.Disabled &&
xXssProtectionConfig.Policy == XXssPolicy.Disabled)
{
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XXssProtectionHeader);
}
string value;
switch (xXssProtectionConfig.Policy)
{
case XXssPolicy.Disabled:
return null;
case XXssPolicy.FilterDisabled:
value = "0";
break;
case XXssPolicy.FilterEnabled:
value = (xXssProtectionConfig.BlockMode ? "1; mode=block" : "1");
break;
default:
throw new NotImplementedException("Somebody apparently forgot to implement support for: " +
xXssProtectionConfig.Policy);
}
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XXssProtectionHeader, value);
}
[CanBeNull]
public HeaderResult CreateXfoResult(IXFrameOptionsConfiguration xfoConfig,
IXFrameOptionsConfiguration oldXfoConfig = null)
{
if (oldXfoConfig != null && oldXfoConfig.Policy != XfoPolicy.Disabled &&
xfoConfig.Policy == XfoPolicy.Disabled)
{
return new HeaderResult(HeaderResult.ResponseAction.Remove, HeaderConstants.XFrameOptionsHeader);
}
switch (xfoConfig.Policy)
{
case XfoPolicy.Disabled:
return null;
case XfoPolicy.Deny:
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XFrameOptionsHeader, "Deny");
case XfoPolicy.SameOrigin:
return new HeaderResult(HeaderResult.ResponseAction.Set, HeaderConstants.XFrameOptionsHeader,
"SameOrigin");
default:
throw new NotImplementedException("Apparently someone forgot to implement support for: " +
xfoConfig.Policy);
}
}
[CanBeNull]
public HeaderResult CreateHpkpResult(IHpkpConfiguration hpkpConfig, bool reportOnly)
{
if (hpkpConfig.MaxAge < TimeSpan.Zero || hpkpConfig.Pins == null || !hpkpConfig.Pins.Any()) return null;
var headerName = reportOnly ? HeaderConstants.HpkpReportOnlyHeader : HeaderConstants.HpkpHeader;
var seconds = (int)hpkpConfig.MaxAge.TotalSeconds;
//Unpinning. Save a few bytes by ignoring other directives.
if (seconds == 0)
{
return new HeaderResult(HeaderResult.ResponseAction.Set, headerName, "max-age=" + seconds);
}
var sb = new StringBuilder();
sb.Append("max-age=").Append(seconds).Append(";");
if (hpkpConfig.IncludeSubdomains)
{
sb.Append("includeSubdomains;");
}
foreach (var pin in hpkpConfig.Pins)
{
sb.Append("pin-").Append(pin).Append(";");
}
if (string.IsNullOrEmpty(hpkpConfig.ReportUri))
{
sb.Remove(sb.Length - 1, 1);
}
else
{
sb.Append("report-uri=\"").Append(hpkpConfig.ReportUri).Append("\"");
}
var value = sb.ToString();
return new HeaderResult(HeaderResult.ResponseAction.Set, headerName, value);
}
[CanBeNull]
public HeaderResult CreateCspResult(ICspConfiguration cspConfig, bool reportOnly,
string builtinReportHandlerUri = null, ICspConfiguration oldCspConfig = null)
{
var headerValue = cspConfig.Enabled ? CreateCspHeaderValue(cspConfig, builtinReportHandlerUri) : null;
if (oldCspConfig != null && oldCspConfig.Enabled)
{
if (!cspConfig.Enabled || headerValue == null)
{
return new HeaderResult(HeaderResult.ResponseAction.Remove,
(reportOnly ? HeaderConstants.ContentSecurityPolicyReportOnlyHeader : HeaderConstants.ContentSecurityPolicyHeader));
}
}
if (!cspConfig.Enabled || headerValue == null)
{
return null;
}
return new HeaderResult(HeaderResult.ResponseAction.Set,
(reportOnly ? HeaderConstants.ContentSecurityPolicyReportOnlyHeader : HeaderConstants.ContentSecurityPolicyHeader), headerValue);
}
[CanBeNull]
private string CreateCspHeaderValue(ICspConfiguration config, string builtinReportHandlerUri = null)
{
var sb = new StringBuilder();
AppendDirective(sb, "default-src", GetDirectiveList(config.DefaultSrcDirective));
AppendDirective(sb, "script-src", GetDirectiveList(config.ScriptSrcDirective));
AppendDirective(sb, "object-src", GetDirectiveList(config.ObjectSrcDirective));
AppendDirective(sb, "style-src", GetDirectiveList(config.StyleSrcDirective));
AppendDirective(sb, "img-src", GetDirectiveList(config.ImgSrcDirective));
AppendDirective(sb, "media-src", GetDirectiveList(config.MediaSrcDirective));
AppendDirective(sb, "frame-src", GetDirectiveList(config.FrameSrcDirective));
AppendDirective(sb, "font-src", GetDirectiveList(config.FontSrcDirective));
AppendDirective(sb, "connect-src", GetDirectiveList(config.ConnectSrcDirective));
AppendDirective(sb, "base-uri", GetDirectiveList(config.BaseUriDirective));
AppendDirective(sb, "child-src", GetDirectiveList(config.ChildSrcDirective));
AppendDirective(sb, "form-action", GetDirectiveList(config.FormActionDirective));
AppendDirective(sb, "frame-ancestors", GetDirectiveList(config.FrameAncestorsDirective));
AppendDirective(sb, "plugin-types", GetPluginTypesDirectiveList(config.PluginTypesDirective));
AppendDirective(sb, "sandbox", GetSandboxDirectiveList(config.SandboxDirective));
AppendUpgradeDirective(sb, "upgrade-insecure-requests", config.UpgradeInsecureRequestsDirective);
if (sb.Length == 0) return null;
AppendDirective(sb, "report-uri",
GetReportUriList(config.ReportUriDirective, builtinReportHandlerUri));
//Get rid of trailing ;
sb.Length--;
return sb.ToString();
}
private void AppendDirective(StringBuilder sb, string directiveName, List<string> sources)
{
if (sources == null) return;
sb.Append(directiveName);
foreach (var source in sources)
{
sb.Append(' ').Append(source);
}
sb.Append(';');
}
private void AppendUpgradeDirective(StringBuilder sb, string directiveName, ICspUpgradeDirectiveConfiguration config)
{
if (!config.Enabled) return;
sb.Append(directiveName);
sb.Append(';');
}
private List<string> GetDirectiveList(ICspDirectiveConfiguration directive)
{
if (directive == null || !directive.Enabled)
return null;
var sources = new List<string>();
if (directive.NoneSrc)
{
sources.Add("'none'");
}
if (directive.SelfSrc)
{
sources.Add("'self'");
}
if (directive.UnsafeInlineSrc)
{
sources.Add("'unsafe-inline'");
}
if (!String.IsNullOrEmpty(directive.Nonce))
{
var nonce = $"'nonce-{directive.Nonce}'";
sources.Add(nonce);
}
if (directive.UnsafeEvalSrc)
{
sources.Add("'unsafe-eval'");
}
if (directive.CustomSources != null)
{
sources.AddRange(directive.CustomSources);
}
return sources.Count > 0 ? sources : null;
}
private List<string> GetPluginTypesDirectiveList(ICspPluginTypesDirectiveConfiguration directive)
{
if (directive == null || !directive.Enabled || !directive.MediaTypes.Any())
return null;
//We know there are MediaTypes, so not null.
return new List<string>(directive.MediaTypes);
}
private List<string> GetSandboxDirectiveList(ICspSandboxDirectiveConfiguration directive)
{
if (directive == null || !directive.Enabled)
return null;
var sources = new List<string>();
if (directive.AllowForms)
{
sources.Add("allow-forms");
}
if (directive.AllowPointerLock)
{
sources.Add("allow-pointer-lock");
}
if (directive.AllowPopups)
{
sources.Add("allow-popups");
}
if (directive.AllowSameOrigin)
{
sources.Add("allow-same-origin");
}
if (directive.AllowScripts)
{
sources.Add("allow-scripts");
}
if (directive.AllowTopNavigation)
{
sources.Add("allow-top-navigation");
}
return sources; //We want to return empty list and not null
}
private List<string> GetReportUriList(ICspReportUriDirectiveConfiguration directive,
string builtinReportHandlerUri = null)
{
if (directive == null || !directive.Enabled)
return null;
var reportUris = new List<string>();
if (directive.EnableBuiltinHandler)
{
reportUris.Add(builtinReportHandlerUri);
}
if (directive.ReportUris != null)
{
reportUris.AddRange(directive.ReportUris);
}
return reportUris.Count > 0 ? reportUris : null;
}
}
}

25
external/NWebsec.Core/HttpHeaders/HeaderResult.cs

@ -0,0 +1,25 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders
{
public class HeaderResult
{
public enum ResponseAction
{
Set = 0,
Remove = 1
}
public HeaderResult(ResponseAction action, string name, string value = null)
{
Action = action;
Name = name;
Value = value;
}
public ResponseAction Action { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
}

31
external/NWebsec.Core/HttpHeaders/IHeaderGenerator.cs

@ -0,0 +1,31 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Core.HttpHeaders
{
public interface IHeaderGenerator
{
HeaderResult CreateXRobotsTagResult(IXRobotsTagConfiguration xRobotsTagConfig,
IXRobotsTagConfiguration oldXRobotsTagConfig = null);
HeaderResult CreateHstsResult(IHstsConfiguration hstsConfig);
HeaderResult CreateXContentTypeOptionsResult(ISimpleBooleanConfiguration xContentTypeOptionsConfig,
ISimpleBooleanConfiguration oldXContentTypeOptionsConfig = null);
HeaderResult CreateXDownloadOptionsResult(ISimpleBooleanConfiguration xDownloadOptionsConfig,
ISimpleBooleanConfiguration oldXDownloadOptionsConfig = null);
HeaderResult CreateXXssProtectionResult(IXXssProtectionConfiguration xXssProtectionConfig,
IXXssProtectionConfiguration oldXXssProtectionConfig = null);
HeaderResult CreateXfoResult(IXFrameOptionsConfiguration xfoConfig,
IXFrameOptionsConfiguration oldXfoConfig = null);
HeaderResult CreateCspResult(ICspConfiguration cspConfig, bool reportOnly,
string builtinReportHandlerUri = null, ICspConfiguration oldCspConfig = null);
HeaderResult CreateHpkpResult(IHpkpConfiguration hpkpConfig, bool reportOnly);
}
}

22
external/NWebsec.Core/HttpHeaders/XXssPolicy.cs

@ -0,0 +1,22 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders
{
public enum XXssPolicy
{
/// <summary>Specifies that the X-Xss-Protection header should not be set in the HTTP response.</summary>
Disabled,
/// <summary>
/// Specifies that the X-Xss-Protection header should be set in the HTTP response, explicitly disabling the IE XSS
/// filter.
/// </summary>
FilterDisabled,
/// <summary>
/// Specifies that the X-Xss-Protection header should be set in the HTTP response, explicitly enabling the IE XSS
/// filter.
/// </summary>
FilterEnabled
}
}

22
external/NWebsec.Core/HttpHeaders/XfoPolicy.cs

@ -0,0 +1,22 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Core.HttpHeaders
{
public enum XfoPolicy
{
/// <summary>Specifies that the X-Frame-Options header should not be set in the HTTP response.</summary>
Disabled,
/// <summary>
/// Specifies that the X-Frame-Options header should be set in the HTTP response, instructing the browser to not
/// display the page when it is loaded in an iframe.
/// </summary>
Deny,
/// <summary>
/// Specifies that the X-Frame-Options header should be set in the HTTP response, instructing the browser to
/// display the page when it is loaded in an iframe - but only if the iframe is from the same origin as the page.
/// </summary>
SameOrigin
}
}

21
external/NWebsec.Core/NWebsec.Core.xproj

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>e9575361-5c4b-425a-bca5-0e8249c6c5fb</ProjectGuid>
<RootNamespace>NWebsec.Core</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

28
external/NWebsec.Core/NWebsecContext.cs

@ -0,0 +1,28 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Core
{
public class NWebsecContext
{
public static readonly string ContextKey = "nwebsec.Context";
public ISimpleBooleanConfiguration NoCacheHeaders { get; set; }
public ISimpleBooleanConfiguration XContentTypeOptions { get; set; }
public ISimpleBooleanConfiguration XDownloadOptions { get; set; }
public IXFrameOptionsConfiguration XFrameOptions { get; set; }
public IXRobotsTagConfiguration XRobotsTag { get; set; }
public IXXssProtectionConfiguration XXssProtection { get; set; }
public ICspConfiguration Csp { get; set; }
public ICspConfiguration CspReportOnly { get; set; }
public ConfigurationOverrides ConfigOverrides { get; set; }
}
public class ConfigurationOverrides
{
public ICspConfiguration CspOverride { get; set; }
public ICspConfiguration CspReportOnlyOverride { get; set; }
}
}

25
external/NWebsec.Core/Properties/AssemblyInfo.cs

@ -0,0 +1,25 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NWebsec.Core")]
[assembly: AssemblyDescription("Core features for the NWebsec security libraries.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Andre N. Klingsheim")]
[assembly: AssemblyProduct("NWebsec.Core")]
[assembly: AssemblyCopyright("Copyright © 2013 - 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("e9575361-5c4b-425a-bca5-0e8249c6c5fb")]
[assembly:InternalsVisibleTo("NWebsec.Core.Tests")]

83
external/NWebsec.Core/RedirectValidator.cs

@ -0,0 +1,83 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Linq;
using NWebsec.Core.Exceptions;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Core
{
public class RedirectValidator
{
public void ValidateRedirect(int statusCode, string locationHeader, Uri requestAuthority,
IRedirectValidationConfiguration config)
{
if (!config.Enabled)
{
return;
}
//Not a redirect
if (!IsRedirectStatusCode(statusCode))
{
return;
}
//No location header
if (String.IsNullOrEmpty(locationHeader))
{
return;
}
Uri locationUri;
if (!Uri.TryCreate(locationHeader, UriKind.RelativeOrAbsolute, out locationUri))
{
throw new Exception("Unable to parse location header value as URI. Value was: " + locationHeader);
}
//Relative Uri
if (!locationUri.IsAbsoluteUri)
{
return;
}
// Same origin TODO look into URL encoding
if (locationUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped).Equals(requestAuthority.GetComponents(UriComponents.SchemeAndServer,UriFormat.SafeUnescaped)))
{
return;
}
//Same host https
if (config.SameHostRedirectConfiguration.Enabled && locationUri.Scheme.Equals("https") && requestAuthority.Host.Equals(locationUri.Host))
{
var sameHostConfig = config.SameHostRedirectConfiguration;
if (sameHostConfig.Ports.Length == 0 && locationUri.IsDefaultPort)
{
return;
}
if (sameHostConfig.Ports.Contains(locationUri.Port))
{
return;
}
throw new RedirectValidationException("A potentially dangerous redirect was detected. Allow same host redirects to this port number in configuration if the redirect was intended. Offending redirect: " + locationHeader);
}
// Allowed Uri
if (config.AllowedUris.Any(locationUri.AbsoluteUri.StartsWith))
{
return;
}
throw new RedirectValidationException(
"A potentially dangerous redirect was detected. Add the destination to the whitelist in configuration if the redirect was intended. Offending redirect: " +
locationHeader);
}
public bool IsRedirectStatusCode(int statusCode)
{
return statusCode >= 300 && statusCode < 400 && statusCode != 304;
}
}
}

34
external/NWebsec.Core/project.json

@ -0,0 +1,34 @@
{
"version": "2.0.0-gamma-custom-*",
"title": "NWebsec.Core (ASP.NET 5)",
"description": "This library provides core functionality for the NWebsec security libraries.",
"releaseNotes": "This release includes core functions for the NWebsec security libaries.",
"authors": [ "André N. Klingsheim" ],
"owners": [ "André N. Klingsheim" ],
"copyright": "Copyright © 2014 - 2015",
"tags": [ "NWebsec Security AspNet AppSec" ],
"projectUrl": "https://docs.nwebsec.com/en/latest/",
"licenseUrl": "https://github.com/NWebsec/NWebsec/blob/master/LICENSE",
"iconUrl": "https://klings.blob.core.windows.net/nwebsecicon/nwebsec_nuget.png",
"frameworks": {
"dnx451": { },
"net451": { },
"dotnet5.4": {
"dependencies": {
"Microsoft.CSharp": "4.0.1-beta-23516",
"System.Runtime": "4.0.21-beta-23516",
"System.Security.Cryptography.Algorithms": "4.0.0-beta-23516",
"System.Security.Cryptography.X509Certificates": "4.0.0-beta-23516",
"System.Text.RegularExpressions": "4.0.11-beta-23516"
}
}
},
"dependencies": {
"Microsoft.AspNet.Http.Abstractions": "1.0.0-rc2-*",
"NWebsec.Annotations": {
"version": "1.0.0-*",
"type": "build"
}
}
}

197
external/NWebsec.Middleware/ApplicationBuilderExtensions.cs

@ -0,0 +1,197 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using NWebsec.Core.HttpHeaders.Configuration.Validation;
using NWebsec.Middleware;
using NWebsec.Middleware.Middleware;
// ReSharper disable once CheckNamespace
namespace Microsoft.AspNet.Builder
{
public static class ApplicationBuilderExtensions
{
/// <summary>
/// Adds a middleware to the pipeline that validates redirects.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseRedirectValidation(this IApplicationBuilder app)
{
if (app == null) throw new ArgumentNullException(nameof(app));
var options = new RedirectValidationOptions();
return app.UseMiddleware<RedirectValidationMiddleware>(options);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that validates redirects.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseRedirectValidation(this IApplicationBuilder app, Action<IFluentRedirectValidationOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new RedirectValidationOptions();
configurer(options);
return app.UseMiddleware<RedirectValidationMiddleware>(options);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the Strict-Transport-Security header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseHsts(this IApplicationBuilder app, Action<IFluentHstsOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new HstsOptions();
configurer(options);
new HstsConfigurationValidator().Validate(options);
return app.UseMiddleware<HstsMiddleware>(options);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the Public-Key-Pins header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseHpkp(this IApplicationBuilder app, Action<IFluentHpkpOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new HpkpOptions();
configurer(options);
new HpkpConfigurationValidator().ValidateNumberOfPins(options.Config);
return app.UseMiddleware<HpkpMiddleware>(options, false);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the Public-Key-Pins-Report-Only header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseHpkpReportOnly(this IApplicationBuilder app, Action<IFluentHpkpOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new HpkpOptions();
configurer(options);
new HpkpConfigurationValidator().ValidateNumberOfPins(options.Config);
return app.UseMiddleware<HpkpMiddleware>(options, true);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the X-Content-Type-Options header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseXContentTypeOptions(this IApplicationBuilder app)
{
if (app == null) throw new ArgumentNullException(nameof(app));
return app.UseMiddleware<XContentTypeOptionsMiddleware>();
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the X-Download-Options header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseXDownloadOptions(this IApplicationBuilder app)
{
if (app == null) throw new ArgumentNullException(nameof(app));
return app.UseMiddleware<XDownloadOptionsMiddleware>();
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the X-Frame-Options header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseXfo(this IApplicationBuilder app, Action<IFluentXFrameOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new XFrameOptions();
configurer(options);
return app.UseMiddleware<XfoMiddleware>(options);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the X-Robots-Tag header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseXRobotsTag(this IApplicationBuilder app, Action<IFluentXRobotsTagOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new XRobotsTagOptions();
configurer(options);
return app.UseMiddleware<XRobotsTagMiddleware>(options);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the X-Xss-Protection header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseXXssProtection(this IApplicationBuilder app, Action<IFluentXXssProtectionOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new XXssProtectionOptions();
configurer(options);
return app.UseMiddleware<XXssMiddleware>(options);
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the Content-Security-Policy header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseCsp(this IApplicationBuilder app, Action<IFluentCspOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new CspOptions();
configurer(options);
return app.UseMiddleware<CspMiddleware>(options, false); //Last param indicates it's not reportOnly.
}
/// <summary>
/// Adds a middleware to the ASP.NET pipeline that sets the Content-Security-Policy-Report-Only header.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder" /> to which the middleware is added.</param>
/// <param name="configurer">An <see cref="Action" /> that configures the options for the middleware.</param>
/// <returns>The <see cref="IApplicationBuilder" /> supplied in the app parameter.</returns>
public static IApplicationBuilder UseCspReportOnly(this IApplicationBuilder app, Action<IFluentCspOptions> configurer)
{
if (app == null) throw new ArgumentNullException(nameof(app));
if (configurer == null) throw new ArgumentNullException(nameof(configurer));
var options = new CspOptions();
configurer(options);
return app.UseMiddleware<CspMiddleware>(options, true); //Last param indicates it's reportOnly.
}
}
}

49
external/NWebsec.Middleware/Core/OwinEnvironment.cs

@ -0,0 +1,49 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
using NWebsec.Core;
namespace NWebsec.Middleware.Core
{
//TODO Get rid of these?
internal class OwinEnvironment
{
private readonly IDictionary<string, object> _environment;
internal OwinEnvironment(IDictionary<string, object> env)
{
_environment = env;
RequestHeaders = new RequestHeaders((IDictionary<string, string[]>)_environment[OwinKeys.RequestHeaders]);
ResponseHeaders = new ResponseHeaders((IDictionary<string, string[]>)_environment[OwinKeys.ResponseHeaders]);
}
internal string RequestScheme => (string)_environment[OwinKeys.RequestScheme];
internal string RequestPathBase => (string)_environment[OwinKeys.RequestPathBase];
internal string RequestPath => (string)_environment[OwinKeys.RequestPath];
internal int ResponseStatusCode
{
get { return (int)_environment[OwinKeys.ResponseStatusCode]; }
set { _environment[OwinKeys.ResponseStatusCode] = value; }
}
internal RequestHeaders RequestHeaders { get; private set; }
internal ResponseHeaders ResponseHeaders { get; private set; }
internal NWebsecContext NWebsecContext
{
get
{
if (!_environment.ContainsKey(NWebsecContext.ContextKey))
{
_environment[NWebsecContext.ContextKey] = new NWebsecContext();
}
return _environment[NWebsecContext.ContextKey] as NWebsecContext;
}
}
}
}

28
external/NWebsec.Middleware/Core/OwinKeys.cs

@ -0,0 +1,28 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
namespace NWebsec.Middleware.Core
{
internal static class OwinKeys
{
//Request
internal static string RequestBody = "owin.RequestBody";
internal static string RequestHeaders = "owin.RequestHeaders";
internal static string RequestMethod = "owin.RequestMethod";
internal static string RequestPath = "owin.RequestPath";
internal static string RequestPathBase = "owin.RequestPathBase";
internal static string RequestProtocol = "owin.RequestProtocol";
internal static string RequestQueryString = "owin.RequestQueryString";
internal static string RequestScheme = "owin.RequestScheme";
//Response
internal static string ResponseBody = "owin.ResponseBody";
internal static string ResponseHeaders = "owin.ResponseHeaders";
internal static string ResponseStatusCode = "owin.ResponseStatusCode";
internal static string ResponseReasonPhrase = "owin.ResponseReasonPhrase";
internal static string ResponseProtocol = "owin.ResponseProtocol";
//Other
internal static string CallCancelled = "owin.CallCancelled";
internal static string Version = "owin.Version";
}
}

44
external/NWebsec.Middleware/Core/RequestHeaders.cs

@ -0,0 +1,44 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace NWebsec.Middleware.Core
{
internal class RequestHeaders
{
private readonly IDictionary<string, string[]> _headers;
internal RequestHeaders(IDictionary<string, string[]> headers)
{
_headers = headers;
}
public string Host
{
get
{
try
{
return _headers.ContainsKey("Host") ? _headers["Host"].Single() : null;
}
catch (Exception)
{
throw new Exception("Multiple Host headers detected: " + String.Join(" ", _headers["Host"]));
}
}
}
/// <summary>
/// Gets the value of a header
/// </summary>
/// <param name="headername"></param>
/// <returns>The header's values as a comma separated list, null if the header is not set.</returns>
public string GetHeaderValue(string headername)
{
string[] values;
return _headers.TryGetValue(headername, out values) ? String.Join(",", values) : null;
}
}
}

47
external/NWebsec.Middleware/Core/ResponseHeaders.cs

@ -0,0 +1,47 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace NWebsec.Middleware.Core
{
internal class ResponseHeaders
{
private readonly IDictionary<string, string[]> _headers;
internal ResponseHeaders(IDictionary<string, string[]> headers)
{
_headers = headers;
}
/// <summary>
/// Gets the value of the Location header if present. Otherwise returns null.
/// </summary>
public string Location
{
get
{
try
{
return _headers.ContainsKey("Location") ? _headers["Location"].Single() : null;
}
catch (Exception)
{
throw new Exception("Multiple Location headers detected: " + String.Join(" ", _headers["Location"]));
}
}
set { _headers["Location"] = new[] { value }; }
}
internal void SetHeader(string name, string value)
{
_headers[name] = new[] { value };
}
internal void RemoveHeader(string name)
{
_headers.Remove(name);
}
}
}

37
external/NWebsec.Middleware/CspDirective.cs

@ -0,0 +1,37 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware
{
public class CspDirective : ICspDirectiveConfiguration
{
public CspDirective()
{
Enabled = true;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public bool Enabled { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool NoneSrc { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool SelfSrc { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool UnsafeInlineSrc { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool UnsafeEvalSrc { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public string Nonce { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<string> CustomSources { get; set; }
}
}

114
external/NWebsec.Middleware/CspDirectiveExtensions.cs

@ -0,0 +1,114 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Linq;
using NWebsec.Core.HttpHeaders.Configuration;
using NWebsec.Core.HttpHeaders.Csp;
namespace NWebsec.Middleware
{
public static class CspDirectiveExtensions
{
/// <summary>
/// Sets the "none" source for the CSP directive. This source cannot be combined with other sources on a CSP directive.
/// </summary>
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
/// <param name="directive">The CSP directive configuration object.</param>
/// <exception cref="InvalidOperationException">Thrown when sources have already been configured for the directive.</exception>
public static void None<T>(this T directive) where T : class, ICspDirectiveBasicConfiguration
{
if (directive == null) throw new ArgumentNullException("directive");
ValidateBeforeSettingNoneSource(directive);
directive.NoneSrc = true;
}
/// <summary>
/// Sets the "self" source for the CSP directive.
/// </summary>
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
/// <param name="directive">The CSP directive configuration object.</param>
/// <returns>The CSP directive configuration object.</returns>
public static T Self<T>(this T directive) where T : class, ICspDirectiveBasicConfiguration
{
if (directive == null) throw new ArgumentNullException("directive");
directive.SelfSrc = true;
return directive;
}
/// <summary>
/// Sets custom sources for the CSP directive.
/// </summary>
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
/// <param name="directive">The CSP directive configuration object.</param>
/// <param name="sources">One or more custom sources.</param>
/// <returns>The CSP directive configuration object.</returns>
public static T CustomSources<T>(this T directive, params string[] sources) where T : class, ICspDirectiveBasicConfiguration
{
if (directive == null) throw new ArgumentNullException("directive");
if (sources.Length == 0) throw new ArgumentException("You must supply at least one source.", "sources");
try
{
directive.CustomSources = sources.Select(s => CspUriSource.Parse(s).ToString()).ToArray();
}
catch (InvalidCspSourceException e)
{
throw new ArgumentException("Invalid source. Details: " + e.Message, "sources", e);
}
return directive;
}
/// <summary>
/// Sets the "unsafe-inline" source for the CSP directive.
/// </summary>
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
/// <param name="directive">The CSP directive configuration object.</param>
/// <returns>The CSP directive configuration object.</returns>
public static T UnsafeInline<T>(this T directive) where T : class, ICspDirectiveUnsafeInlineConfiguration
{
if (directive == null) throw new ArgumentNullException("directive");
directive.UnsafeInlineSrc = true;
return directive;
}
/// <summary>
/// Sets the "unsafe-eval" source for the CSP directive.
/// </summary>
/// <typeparam name="T">The type of the CSP directive configuration object.</typeparam>
/// <param name="directive">The CSP directive configuration object.</param>
/// <returns>The CSP directive configuration object.</returns>
public static T UnsafeEval<T>(this T directive) where T : class, ICspDirectiveConfiguration
{
if (directive == null) throw new ArgumentNullException("directive");
directive.UnsafeEvalSrc = true;
return directive;
}
private static void ValidateBeforeSettingNoneSource(ICspDirectiveBasicConfiguration directive)
{
if (directive.SelfSrc || (directive.CustomSources != null && directive.CustomSources.Any()))
{
throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources.");
}
var unsafeInline = directive as ICspDirectiveUnsafeInlineConfiguration;
if (unsafeInline != null && unsafeInline.UnsafeInlineSrc)
{
throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources.");
}
var unsafeEval = directive as ICspDirectiveConfiguration;
if (unsafeEval != null && unsafeEval.UnsafeEvalSrc)
{
throw new InvalidOperationException("It is a logical error to combine the \"None\" source with other sources.");
}
}
}
}

161
external/NWebsec.Middleware/CspOptions.cs

@ -0,0 +1,161 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware
{
public class CspOptions : ICspConfiguration, IFluentCspOptions
{
public bool Enabled { get; set; } = true;
public ICspDirectiveConfiguration DefaultSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration ScriptSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration ObjectSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration StyleSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration ImgSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration MediaSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration FrameSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration FontSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration ConnectSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration BaseUriDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration ChildSrcDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration FormActionDirective { get; set; } = new CspDirective();
public ICspDirectiveConfiguration FrameAncestorsDirective { get; set; } = new CspDirective();
public ICspPluginTypesDirectiveConfiguration PluginTypesDirective { get; set; } = new FluentCspPluginTypesDirective();
public ICspSandboxDirectiveConfiguration SandboxDirective { get; set; } = new FluentCspSandboxDirective();
public ICspUpgradeDirectiveConfiguration UpgradeInsecureRequestsDirective { get; set; } = new CspUpgradeDirectiveConfiguration();
public ICspReportUriDirectiveConfiguration ReportUriDirective { get; set; } = new CspReportUriDirective();
public IFluentCspOptions DefaultSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(DefaultSrcDirective);
return this;
}
public IFluentCspOptions ScriptSources(Action<ICspDirectiveConfiguration> configurer)
{
configurer(ScriptSrcDirective);
return this;
}
public IFluentCspOptions ObjectSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(ObjectSrcDirective);
return this;
}
public IFluentCspOptions StyleSources(Action<ICspDirectiveUnsafeInlineConfiguration> configurer)
{
configurer(StyleSrcDirective);
return this;
}
public IFluentCspOptions ImageSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(ImgSrcDirective);
return this;
}
public IFluentCspOptions MediaSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(MediaSrcDirective);
return this;
}
public IFluentCspOptions FrameSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(FrameSrcDirective);
return this;
}
public IFluentCspOptions FontSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(FontSrcDirective);
return this;
}
public IFluentCspOptions ConnectSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(ConnectSrcDirective);
return this;
}
public IFluentCspOptions BaseUris(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(BaseUriDirective);
return this;
}
public IFluentCspOptions ChildSources(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(ChildSrcDirective);
return this;
}
public IFluentCspOptions FormActions(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(FormActionDirective);
return this;
}
public IFluentCspOptions FrameAncestors(Action<ICspDirectiveBasicConfiguration> configurer)
{
configurer(FrameAncestorsDirective);
return this;
}
public IFluentCspOptions PluginTypes(Action<IFluentCspPluginTypesDirective> configurer)
{
configurer((IFluentCspPluginTypesDirective)PluginTypesDirective);
return this;
}
public IFluentCspOptions Sandbox()
{
SandboxDirective.Enabled = true;
return this;
}
public IFluentCspOptions Sandbox(Action<IFluentCspSandboxDirective> configurer)
{
SandboxDirective.Enabled = true;
configurer((IFluentCspSandboxDirective)SandboxDirective);
return this;
}
public IFluentCspOptions UpgradeInsecureRequests(int httpsPort = 443)
{
if (httpsPort < 1 || httpsPort > 65535)
{
throw new ArgumentOutOfRangeException(nameof(httpsPort),"The port number must be in the range 1-65535.");
}
UpgradeInsecureRequestsDirective.Enabled = true;
UpgradeInsecureRequestsDirective.HttpsPort = httpsPort;
return this;
}
public IFluentCspOptions ReportUris(Action<IFluentCspReportUriDirective> configurer)
{
configurer((IFluentCspReportUriDirective)ReportUriDirective);
return this;
}
}
}

40
external/NWebsec.Middleware/CspReportUriDirective.cs

@ -0,0 +1,40 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using NWebsec.Core.HttpHeaders.Configuration;
using NWebsec.Core.HttpHeaders.Csp;
namespace NWebsec.Middleware
{
public class CspReportUriDirective : ICspReportUriDirectiveConfiguration, IFluentCspReportUriDirective
{
internal CspReportUriDirective()
{
Enabled = true;
}
public bool Enabled { get; set; }
public bool EnableBuiltinHandler { get; set; }
public IEnumerable<string> ReportUris { get; set; }
public void Uris(params string[] reportUris)
{
if (reportUris.Length == 0) throw new ArgumentException("You must supply at least one report URI.", "reportUris");
var reportUriList = new List<string>();
foreach (var reportUri in reportUris)
{
Uri uri;
if (!Uri.TryCreate(reportUri, UriKind.RelativeOrAbsolute, out uri))
{
throw new ArgumentException("Could not parse reportUri: " + reportUri);
}
reportUriList.Add(CspUriSource.EncodeUri(uri));
}
ReportUris = reportUriList.ToArray();
}
}
}

49
external/NWebsec.Middleware/FluentCspPluginTypesDirective.cs

@ -0,0 +1,49 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Linq;
using NWebsec.Core.HttpHeaders.Configuration;
using NWebsec.Core.HttpHeaders.Configuration.Validation;
namespace NWebsec.Middleware
{
public class FluentCspPluginTypesDirective : CspPluginTypesDirectiveConfiguration, IFluentCspPluginTypesDirective
{
public FluentCspPluginTypesDirective()
{
Enabled = true;
}
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
public void MediaTypes(params string[] mediaTypes)
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
{
if (mediaTypes == null)
{
throw new ArgumentNullException(nameof(mediaTypes));
}
if (mediaTypes.Length == 0)
{
throw new ArgumentException("One or more parameter values expected.", nameof(mediaTypes));
}
var validator = new Rfc2045MediaTypeValidator();
var types = mediaTypes.Distinct().ToArray();
foreach (var mediaType in types)
{
try
{
validator.Validate(mediaType);
}
catch (Exception e)
{
throw new ArgumentException("Invalid argument. Details: " + e.Message, e);
}
}
base.MediaTypes = types;
}
}
}

45
external/NWebsec.Middleware/FluentCspSandboxDirective.cs

@ -0,0 +1,45 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware
{
class FluentCspSandboxDirective : CspSandboxDirectiveConfiguration, IFluentCspSandboxDirective
{
public new IFluentCspSandboxDirective AllowForms()
{
base.AllowForms = true;
return this;
}
public new IFluentCspSandboxDirective AllowPointerLock()
{
base.AllowPointerLock = true;
return this;
}
public new IFluentCspSandboxDirective AllowPopups()
{
base.AllowPopups = true;
return this;
}
public new IFluentCspSandboxDirective AllowSameOrigin()
{
base.AllowSameOrigin = true;
return this;
}
public new IFluentCspSandboxDirective AllowScripts()
{
base.AllowScripts = true;
return this;
}
public new IFluentCspSandboxDirective AllowTopNavigation()
{
base.AllowTopNavigation = true;
return this;
}
}
}

21
external/NWebsec.Middleware/Helpers/CspUpgradeHelper.cs

@ -0,0 +1,21 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNet.Http;
namespace NWebsec.Middleware.Helpers
{
//Tested indirectly by CSP Middleware
internal class CspUpgradeHelper
{
internal static bool UaSupportsUpgradeInsecureRequests(HttpContext env)
{
var upgradeHeader = env.Request.Headers["Upgrade-Insecure-Requests"];
return upgradeHeader.Any(h => h.Equals("1", StringComparison.Ordinal));
}
}
}

124
external/NWebsec.Middleware/HpkpOptions.cs

@ -0,0 +1,124 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using NWebsec.Core.Helpers.X509;
using NWebsec.Core.HttpHeaders.Configuration.Validation;
namespace NWebsec.Middleware
{
public class HpkpOptions : IFluentHpkpOptions
{
private readonly List<string> _pins;
private readonly HpkpConfigurationValidator _validator;
internal HpkpOptionsConfiguration Config { get; set; }
public HpkpOptions()
{
_pins = new List<string>();
Config = new HpkpOptionsConfiguration { Pins = _pins };
_validator = new HpkpConfigurationValidator();
}
// ReSharper disable once CSharpWarnings::CS0109
public IFluentHpkpOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0)
{
if (days < 0) throw new ArgumentOutOfRangeException("days", "Value must be equal to or larger than 0.");
if (hours < 0) throw new ArgumentOutOfRangeException("hours", "Value must be equal to or larger than 0.");
if (minutes < 0) throw new ArgumentOutOfRangeException("minutes", "Value must be equal to or larger than 0.");
if (seconds < 0) throw new ArgumentOutOfRangeException("seconds", "Value must be equal to or larger than 0.");
Config.MaxAge = new TimeSpan(days, hours, minutes, seconds);
return this;
}
public IFluentHpkpOptions IncludeSubdomains()
{
Config.IncludeSubdomains = true;
return this;
}
public IFluentHpkpOptions ReportUri(string reportUri)
{
try
{
_validator.ValidateReportUri(reportUri);
}
catch (Exception e)
{
throw new ArgumentException(e.Message, "reportUri");
}
Config.ReportUri = reportUri;
return this;
}
public IFluentHpkpOptions AllResponses()
{
Config.HttpsOnly = false;
return this;
}
public IFluentHpkpOptions Sha256Pins(params string[] pins)
{
foreach (var pin in pins)
{
try
{
_validator.ValidateRawPin(pin);
}
catch (Exception e)
{
throw new ArgumentException(e.Message, "pins");
}
var formattedPin = "sha256=\"" + pin + "\"";
if (!_pins.Contains(formattedPin))
{
_pins.Add(formattedPin);
}
}
return this;
}
public IFluentHpkpOptions PinCertificate(string thumbprint, StoreLocation storeLocation = StoreLocation.LocalMachine,
StoreName storeName = StoreName.My)
{
try
{
_validator.ValidateThumbprint(thumbprint);
}
catch (Exception e)
{
throw new ArgumentException(e.Message, thumbprint);
}
var helper = new X509Helper();
var cert = helper.GetCertByThumbprint(thumbprint, storeLocation, storeName);
var pin = helper.GetSubjectPublicKeyInfoPinValue(cert);
#if DNX451
cert.Reset();
#elif NET451
cert.Reset();
#else
cert.Dispose();
#endif
if (!_pins.Contains(pin))
{
_pins.Add(pin);
}
return this;
}
public IFluentHpkpOptions HttpsOnly()
{
Config.HttpsOnly = true;
return this;
}
}
}

33
external/NWebsec.Middleware/HpkpOptionsConfiguration.cs

@ -0,0 +1,33 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware
{
public class HpkpOptionsConfiguration : IHpkpConfiguration
{
internal HpkpOptionsConfiguration()
{
MaxAge = TimeSpan.Zero;
HttpsOnly = true;
Pins = new string[0];
}
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<string> Pins { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public TimeSpan MaxAge { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IncludeSubdomains { get; set; }
public string ReportUri { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool HttpsOnly { get; set; }
}
}

52
external/NWebsec.Middleware/HstsOptions.cs

@ -0,0 +1,52 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
namespace NWebsec.Middleware
{
public class HstsOptions : HstsOptionsConfiguration, IFluentHstsOptions
{
// ReSharper disable once CSharpWarnings::CS0109
public new IFluentHstsOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0)
{
if (days < 0) throw new ArgumentOutOfRangeException(nameof(days), "Value must be equal to or larger than 0.");
if (hours < 0) throw new ArgumentOutOfRangeException(nameof(hours), "Value must be equal to or larger than 0.");
if (minutes < 0) throw new ArgumentOutOfRangeException(nameof(minutes), "Value must be equal to or larger than 0.");
if (seconds < 0) throw new ArgumentOutOfRangeException(nameof(seconds), "Value must be equal to or larger than 0.");
base.MaxAge = new TimeSpan(days, hours, minutes, seconds);
return this;
}
public new IFluentHstsOptions IncludeSubdomains()
{
base.IncludeSubdomains = true;
return this;
}
public new IFluentHstsOptions Preload()
{
base.Preload = true;
return this;
}
public new IFluentHstsOptions UpgradeInsecureRequests()
{
base.UpgradeInsecureRequests = true;
return this;
}
public IFluentHstsOptions AllResponses()
{
base.HttpsOnly = false;
return this;
}
public new IFluentHstsOptions HttpsOnly()
{
base.HttpsOnly = true;
return this;
}
}
}

32
external/NWebsec.Middleware/HstsOptionsConfiguration.cs

@ -0,0 +1,32 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.ComponentModel;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware
{
public class HstsOptionsConfiguration : IHstsConfiguration
{
internal HstsOptionsConfiguration()
{
MaxAge = TimeSpan.Zero;
HttpsOnly = true;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public TimeSpan MaxAge { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IncludeSubdomains { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool Preload { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool HttpsOnly { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
public bool UpgradeInsecureRequests { get; set; }
}
}

142
external/NWebsec.Middleware/IFluentCspOptions.cs

@ -0,0 +1,142 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using NWebsec.Core.Fluent;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for Content-Security-Options.
/// </summary>
public interface IFluentCspOptions : IFluentInterface
{
/// <summary>
/// Configures the default-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions DefaultSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the script-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions ScriptSources(Action<ICspDirectiveConfiguration> configurer);
/// <summary>
/// Configures the object-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions ObjectSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the style-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions StyleSources(Action<ICspDirectiveUnsafeInlineConfiguration> configurer);
/// <summary>
/// Configures the image-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions ImageSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the media-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions MediaSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the frame-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions FrameSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the font-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions FontSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the connect-src directive (CSP 1.0).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions ConnectSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the base-uri directive (CSP 2).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions BaseUris(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the child-src directive (CSP 2).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions ChildSources(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the form-action directive (CSP 2).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions FormActions(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the fram-ancestors directive (CSP 2).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions FrameAncestors(Action<ICspDirectiveBasicConfiguration> configurer);
/// <summary>
/// Configures the plugin-types directive (CSP 2).
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the media types for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions PluginTypes(Action<IFluentCspPluginTypesDirective> configurer);
/// <summary>
/// Enables the sandbox directive (CSP 2) without further ado.
/// </summary>
/// <remarks>Support for this directive was optional in CSP 1.0, but is mandatory as of CSP 2.</remarks>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions Sandbox();
/// <summary>
/// Configures the sandbox directive (CSP 2) with one or more sources.
/// </summary>
/// <remarks>Support for this directive was optional in CSP 1.0, but is mandatory as of CSP 2.</remarks>
/// <param name="configurer">An <see cref="Action"/> that configures the sources for the directive.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions Sandbox(Action<IFluentCspSandboxDirective> configurer);
/// <summary>
/// Enables the upgrade-insecure-requests directive and redirects conformant UAs to HTTPS.
/// </summary>
/// <remarks>This directive is not part of CSP 1.0 or CSP 2, but is described in a separate specification.</remarks>
/// <param name="httpsPort">The HTTPS port. Defaults to 443.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions UpgradeInsecureRequests(int httpsPort = 443);
/// <summary>
/// Configures the report-uri directive (CSP 1.0). Support for absolute URIs was introduced in CSP 2.
/// </summary>
/// <param name="configurer">An <see cref="Action"/> that configures the report URIs.</param>
/// <returns>The current <see cref="CspOptions" /> instance.</returns>
IFluentCspOptions ReportUris(Action<IFluentCspReportUriDirective> configurer);
}
}

14
external/NWebsec.Middleware/IFluentCspPluginTypesDirective.cs

@ -0,0 +1,14 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
public interface IFluentCspPluginTypesDirective : IFluentInterface
{
/// <summary>
/// Sets the media types for the CSP plugin-types directive.
/// </summary>
void MediaTypes(params string[] pluginType);
}
}

18
external/NWebsec.Middleware/IFluentCspReportUriDirective.cs

@ -0,0 +1,18 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure report URIs.
/// </summary>
public interface IFluentCspReportUriDirective : IFluentInterface
{
/// <summary>
/// Sets report URIs for the CSP directive.
/// </summary>
/// <param name="reportUris">One or more report URIs.</param>
void Uris(params string[] reportUris);
}
}

39
external/NWebsec.Middleware/IFluentCspSandboxDirective.cs

@ -0,0 +1,39 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
public interface IFluentCspSandboxDirective : IFluentInterface
{
/// <summary>
/// Sets the 'allow-forms' source for the CSP sandbox directive.
/// </summary>
IFluentCspSandboxDirective AllowForms();
/// <summary>
/// Sets the 'allow-pointer-lock' source for the CSP sandbox directive.
/// </summary>
IFluentCspSandboxDirective AllowPointerLock();
/// <summary>
/// Sets the 'allow-popups' source for the CSP sandbox directive.
/// </summary>
IFluentCspSandboxDirective AllowPopups();
/// <summary>
/// Sets the 'allow-same-origin' source for the CSP sandbox directive.
/// </summary>
IFluentCspSandboxDirective AllowSameOrigin();
/// <summary>
/// Sets the 'allow-scripts' source for the CSP sandbox directive.
/// </summary>
IFluentCspSandboxDirective AllowScripts();
/// <summary>
/// Sets the 'allow-top-navigation' source for the CSP sandbox directive.
/// </summary>
IFluentCspSandboxDirective AllowTopNavigation();
}
}

62
external/NWebsec.Middleware/IFluentHpkpOptions.cs

@ -0,0 +1,62 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Security.Cryptography.X509Certificates;
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for Http Strict Transport Security.
/// </summary>
public interface IFluentHpkpOptions : IFluentInterface
{
/// <summary>
/// Specifies the max age for the HPKP header.
/// </summary>
/// <param name="days">The number of days added to max age.</param>
/// <param name="hours">The number of hours added to max age.</param>
/// <param name="minutes">The number of minutes added to max age.</param>
/// <param name="seconds">The number of seconds added to max age.</param>
/// <returns>The current instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value was supplied in any of the parameters.</exception>
IFluentHpkpOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0);
/// <summary>
/// Enables the IncludeSubdomains directive in the HPKP header.
/// </summary>
/// <returns>The current instance.</returns>
IFluentHpkpOptions IncludeSubdomains();
/// <summary>
/// Specifies a report URI where the browser can send HPKP violations.
/// </summary>
/// <param name="reportUri">The report URI, which is an absolute URI with scheme http or https.</param>
/// <returns>The current instance.</returns>
IFluentHpkpOptions ReportUri(string reportUri);
/// <summary>
/// Specifies that the HPKP header should also be set for HTTP responses. The header is always set for HTTPS responses.
/// </summary>
/// <remarks>The HPKP standard specifies that the header should only be set over secure connections, which is the default behavior.
/// This configuration option exists to accomodate websites running behind an SSL terminator.</remarks>
/// <returns>The current instance.</returns>
IFluentHpkpOptions AllResponses();
/// <summary>
/// Specifies one or more certificate pins to include in the HPKP header. A certificate pin is the Base64 encoded SHA-256 hash value of a certficate's SPKI.
/// </summary>
/// <param name="pins">One or more certficate pin values.</param>
/// <returns>The current instance.</returns>
IFluentHpkpOptions Sha256Pins(params string[] pins);
/// <summary>
/// Specifies a certificate that should be pinned in the HPKP header.
/// </summary>
/// <param name="thumbprint">The certificate thumbprint.</param>
/// <param name="storeLocation">The <see cref="StoreLocation"/> for the certificate. The default is <see cref="StoreLocation.LocalMachine"/>.</param>
/// <param name="storeName">The <see cref="StoreName"/> for the certificate. The default is <see cref="StoreName.My"/>.</param>
/// <returns>The current instance.</returns>
IFluentHpkpOptions PinCertificate(string thumbprint, StoreLocation storeLocation = StoreLocation.LocalMachine, StoreName storeName = StoreName.My);
}
}

59
external/NWebsec.Middleware/IFluentHstsOptions.cs

@ -0,0 +1,59 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for Http Strict Transport Security.
/// </summary>
public interface IFluentHstsOptions : IFluentInterface
{
/// <summary>
/// Specifies the max age for the HSTS header.
/// </summary>
/// <param name="days">The number of days added to max age.</param>
/// <param name="hours">The number of hours added to max age.</param>
/// <param name="minutes">The number of minutes added to max age.</param>
/// <param name="seconds">The number of seconds added to max age.</param>
/// <returns>The current instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown if a negative value was supplied in any of the parameters.</exception>
IFluentHstsOptions MaxAge(int days = 0, int hours = 0, int minutes = 0, int seconds = 0);
/// <summary>
/// Enables the IncludeSubdomains directive in the Hsts header.
/// </summary>
/// <returns>The current instance.</returns>
IFluentHstsOptions IncludeSubdomains();
/// <summary>
/// Enables the Preload directive in the HSTS header. MaxAge must be at least 18 weeks, and IncludeSubdomains must be enabled.
/// </summary>
/// <remarks>Read more about preloaded HSTS sites at <a href="https://www.chromium.org/hsts">www.chromium.org/sts</a></remarks>
/// <returns>The current instance.</returns>
IFluentHstsOptions Preload();
/// <summary>
/// Sets the HSTS header only when the user agent signals that it supports the upgrade-insecure-requests CSP directive.
/// </summary>
/// <remarks>This setting is intended to be used in combination with the upgrade-insecure-requests CSP directive.</remarks>
/// <returns>The current instance.</returns>
IFluentHstsOptions UpgradeInsecureRequests();
/// <summary>
/// Specifies that the HSTS header should also be set for HTTP responses. The header is always set for HTTPS responses.
/// </summary>
/// <remarks>The HSTS standard specifies that the header should only be set over secure connections, which is the default behavior.
/// This configuration option exists to accomodate websites running behind an SSL terminator.</remarks>
/// <returns>The current instance.</returns>
IFluentHstsOptions AllResponses();
/// <summary>
/// Specifies that the HSTS header should be set for HTTPS responses only.
/// </summary>
/// <returns>The current instance.</returns>
[Obsolete("This method is deprecated as the default has been changed to HTTPS only.", false)]
IFluentHstsOptions HttpsOnly();
}
}

26
external/NWebsec.Middleware/IFluentRedirectValidationOptions.cs

@ -0,0 +1,26 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for redirect validation.
/// </summary>
public interface IFluentRedirectValidationOptions : IFluentInterface
{
/// <summary>
/// Configures the allowed redirect destinations. These must be well formed absolute URIs.
/// </summary>
/// <param name="uris">Allowed redirect destinations.</param>
/// <returns>The current instance.</returns>
IFluentRedirectValidationOptions AllowedDestinations(params string[] uris);
/// <summary>
/// Allows same host redirects to HTTPS.
/// </summary>
/// <param name="httpsPorts">Allowed destination port(s) for redirects to HTTPS. The default HTTPS port (443) is assumed if no values are configured.</param>
/// <returns>The current instance.</returns>
IFluentRedirectValidationOptions AllowSameHostRedirectsToHttps(params int[] httpsPorts);
}
}

22
external/NWebsec.Middleware/IFluentXFrameOptions.cs

@ -0,0 +1,22 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for X-Frame-Options.
/// </summary>
public interface IFluentXFrameOptions : IFluentInterface
{
/// <summary>
/// Enables the Deny directive.
/// </summary>
void Deny();
/// <summary>
/// Enables the SameOrigin directive.
/// </summary>
void SameOrigin();
}
}

54
external/NWebsec.Middleware/IFluentXRobotsTagOptions.cs

@ -0,0 +1,54 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for X-Robots-Tag.
/// </summary>
public interface IFluentXRobotsTagOptions : IFluentInterface
{
/// <summary>
/// Enables the noindex directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoIndex();
/// <summary>
/// Enables the nofollow directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoFollow();
/// <summary>
/// Enables the nosnippet directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoSnippet();
/// <summary>
/// Enables the noarchive directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoArchive();
/// <summary>
/// Enables the noodp directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoOdp();
/// <summary>
/// Enables the notranslate directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoTranslate();
/// <summary>
/// Enables the noimageindex directive.
/// </summary>
/// <returns>The current instance.</returns>
IFluentXRobotsTagOptions NoImageIndex();
}
}

27
external/NWebsec.Middleware/IFluentXXssProtectionOptions.cs

@ -0,0 +1,27 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using NWebsec.Core.Fluent;
namespace NWebsec.Middleware
{
/// <summary>
/// Fluent interface to configure options for X-Xss-Protection.
/// </summary>
public interface IFluentXXssProtectionOptions : IFluentInterface
{
/// <summary>
/// Configures the header to explicitly disable protection.
/// </summary>
void Disabled();
/// <summary>
/// Configures the header to explicitly enable protection.
/// </summary>
void Enabled();
/// <summary>
/// Configures the header to explicitly enable protection with block mode.
/// </summary>
void EnabledWithBlockMode();
}
}

88
external/NWebsec.Middleware/Middleware/CspMiddleware.cs

@ -0,0 +1,88 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.Extensions;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
using NWebsec.Middleware.Helpers;
namespace NWebsec.Middleware.Middleware
{
public class CspMiddleware
{
private readonly ICspConfiguration _config;
private readonly HeaderResult _headerResult;
private readonly bool _reportOnly;
private readonly RequestDelegate _next;
public CspMiddleware(RequestDelegate next, ICspConfiguration options, bool reportOnly)
{
_next = next;
_config = options;
_reportOnly = reportOnly;
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateCspResult(_config, reportOnly);
}
public async Task Invoke(HttpContext context)
{
if (HandleUpgradeInsecureRequest(context))
{
return;
}
SetCspHeaders(context);
if (_next != null)
{
await _next(context);
}
}
internal bool HandleUpgradeInsecureRequest(HttpContext context)
{
//Already on https.
if (context.Request.IsHttps) return false;
//CSP upgrade-insecure-requests is disabled
if (!_config.Enabled || !_config.UpgradeInsecureRequestsDirective.Enabled) return false;
if (!CspUpgradeHelper.UaSupportsUpgradeInsecureRequests(context)) return false;
var upgradeUri = new UriBuilder($"https://{context.Request.Host}")
{
Port = _config.UpgradeInsecureRequestsDirective.HttpsPort,
Path = context.Request.PathBase + context.Request.Path
};
//Redirect
context.Response.Headers["Vary"] = "Upgrade-Insecure-Requests";
context.Response.Headers["Location"] = upgradeUri.Uri.AbsoluteUri;
context.Response.StatusCode = 307;
return true;
}
internal void SetCspHeaders(HttpContext context)
{
if (_reportOnly)
{
context.GetNWebsecContext().CspReportOnly = _config;
}
else
{
context.GetNWebsecContext().Csp = _config;
}
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
context.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

39
external/NWebsec.Middleware/Middleware/HpkpMiddleware.cs

@ -0,0 +1,39 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware.Middleware
{
public class HpkpMiddleware : MiddlewareBase
{
private readonly IHpkpConfiguration _config;
private readonly HeaderResult _headerResult;
public HpkpMiddleware(RequestDelegate next, HpkpOptions options, bool reportOnly)
: base(next)
{
_config = options.Config;
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateHpkpResult(_config, reportOnly);
}
internal override void PreInvokeNext(HttpContext context)
{
if (_config.HttpsOnly && !context.Request.IsHttps)
{
return;
}
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
context.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

46
external/NWebsec.Middleware/Middleware/HstsMiddleware.cs

@ -0,0 +1,46 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
using NWebsec.Middleware.Helpers;
namespace NWebsec.Middleware.Middleware
{
public class HstsMiddleware : MiddlewareBase
{
private readonly IHstsConfiguration _config;
private readonly HeaderResult _headerResult;
private const string Https = "https";
public HstsMiddleware(RequestDelegate next, HstsOptions options)
: base(next)
{
_config = options;
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateHstsResult(_config);
}
internal override void PreInvokeNext(HttpContext context)
{
if (_config.HttpsOnly && !context.Request.IsHttps)
{
return;
}
if (_config.UpgradeInsecureRequests && !CspUpgradeHelper.UaSupportsUpgradeInsecureRequests(context))
{
return;
}
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
context.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

39
external/NWebsec.Middleware/Middleware/MiddleWareBase.cs

@ -0,0 +1,39 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
namespace NWebsec.Middleware.Middleware
{
public class MiddlewareBase
{
private readonly RequestDelegate _next;
public MiddlewareBase(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
PreInvokeNext(context);
if (_next != null)
{
await _next(context);
}
PostInvokeNext(context);
}
internal virtual void PreInvokeNext(HttpContext context)
{
}
internal virtual void PostInvokeNext(HttpContext context)
{
}
}
}

39
external/NWebsec.Middleware/Middleware/RedirectValidationMiddleware.cs

@ -0,0 +1,39 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core;
namespace NWebsec.Middleware.Middleware
{
public class RedirectValidationMiddleware : MiddlewareBase
{
private readonly RedirectValidationOptions _config;
private readonly RedirectValidator _redirectValidator;
public RedirectValidationMiddleware(RequestDelegate next, RedirectValidationOptions options)
: base(next)
{
_config = options;
_redirectValidator = new RedirectValidator();
}
internal override void PostInvokeNext(HttpContext context)
{
var statusCode = context.Response.StatusCode;
if (!_redirectValidator.IsRedirectStatusCode(statusCode))
{
return;
}
var scheme = context.Request.Scheme;
var hostandport = context.Request.Host;
var requestUri = new Uri(scheme + "://" + hostandport);
_redirectValidator.ValidateRedirect(statusCode, context.Response.Headers["Location"], requestUri, _config);
}
}
}

34
external/NWebsec.Middleware/Middleware/XContentTypeOptionsMiddleware.cs

@ -0,0 +1,34 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.Extensions;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware.Middleware
{
public class XContentTypeOptionsMiddleware : MiddlewareBase
{
private readonly ISimpleBooleanConfiguration _config;
private readonly HeaderResult _headerResult;
public XContentTypeOptionsMiddleware(RequestDelegate next)
: base(next)
{
_config = new SimpleBooleanConfiguration { Enabled = true };
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateXContentTypeOptionsResult(_config);
}
internal override void PreInvokeNext(HttpContext owinEnvironment)
{
owinEnvironment.GetNWebsecContext().XContentTypeOptions = _config;
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

34
external/NWebsec.Middleware/Middleware/XDownloadOptionsMiddleware.cs

@ -0,0 +1,34 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.Extensions;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware.Middleware
{
public class XDownloadOptionsMiddleware : MiddlewareBase
{
private readonly ISimpleBooleanConfiguration _config;
private readonly HeaderResult _headerResult;
public XDownloadOptionsMiddleware(RequestDelegate next)
: base(next)
{
_config = new SimpleBooleanConfiguration { Enabled = true };
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateXDownloadOptionsResult(_config);
}
internal override void PreInvokeNext(HttpContext owinEnvironment)
{
owinEnvironment.GetNWebsecContext().XDownloadOptions = _config;
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
owinEnvironment.Response.Headers[_headerResult.Name]= _headerResult.Value;
}
}
}
}

36
external/NWebsec.Middleware/Middleware/XRobotsTagMiddleware.cs

@ -0,0 +1,36 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.Extensions;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware.Middleware
{
public class XRobotsTagMiddleware : MiddlewareBase
{
private readonly IXRobotsTagConfiguration _config;
private readonly HeaderResult _headerResult;
public XRobotsTagMiddleware(RequestDelegate next, XRobotsTagOptions options)
: base(next)
{
_config = options.Config;
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateXRobotsTagResult(_config);
}
internal override void PreInvokeNext(HttpContext owinEnvironment)
{
owinEnvironment.GetNWebsecContext().XRobotsTag = _config;
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

34
external/NWebsec.Middleware/Middleware/XXssMiddleware.cs

@ -0,0 +1,34 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.Extensions;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware.Middleware
{
public class XXssMiddleware : MiddlewareBase
{
private readonly IXXssProtectionConfiguration _config;
private readonly HeaderResult _headerResult;
public XXssMiddleware(RequestDelegate next, XXssProtectionOptions options)
: base(next)
{
_config = options;
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateXXssProtectionResult(_config);
}
internal override void PreInvokeNext(HttpContext owinEnvironment)
{
owinEnvironment.GetNWebsecContext().XXssProtection = _config;
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

33
external/NWebsec.Middleware/Middleware/XfoMiddleware.cs

@ -0,0 +1,33 @@
// Copyright (c) André N. Klingsheim. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using NWebsec.Core.Extensions;
using NWebsec.Core.HttpHeaders;
using NWebsec.Core.HttpHeaders.Configuration;
namespace NWebsec.Middleware.Middleware
{
public class XfoMiddleware : MiddlewareBase
{
private readonly IXFrameOptionsConfiguration _config;
private readonly HeaderResult _headerResult;
public XfoMiddleware(RequestDelegate next, XFrameOptions options)
: base(next)
{
_config = options;
var headerGenerator = new HeaderGenerator();
_headerResult = headerGenerator.CreateXfoResult(_config);
}
internal override void PreInvokeNext(HttpContext owinEnvironment)
{
owinEnvironment.GetNWebsecContext().XFrameOptions = _config;
if (_headerResult.Action == HeaderResult.ResponseAction.Set)
{
owinEnvironment.Response.Headers[_headerResult.Name] = _headerResult.Value;
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save