Don't allow response headers to contain control characters
- For the purposes of this commit, control characters are everything < 0x20. - Immediately throw an InvalidOperationException when the header is added to the IHttpResponseFeature.Headers dictionary.
This commit is contained in:
parent
296ca0f948
commit
c1e5640a65
|
|
@ -2038,6 +2038,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
protected override void SetValueFast(string key, StringValues value)
|
protected override void SetValueFast(string key, StringValues value)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (key.Length)
|
switch (key.Length)
|
||||||
{
|
{
|
||||||
case 13:
|
case 13:
|
||||||
|
|
@ -2424,10 +2425,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unknown[key] = value;
|
Unknown[key] = value;
|
||||||
}
|
}
|
||||||
protected override void AddValueFast(string key, StringValues value)
|
protected override void AddValueFast(string key, StringValues value)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (key.Length)
|
switch (key.Length)
|
||||||
{
|
{
|
||||||
case 13:
|
case 13:
|
||||||
|
|
@ -2990,6 +2993,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unknown.Add(key, value);
|
Unknown.Add(key, value);
|
||||||
}
|
}
|
||||||
protected override bool RemoveFast(string key)
|
protected override bool RemoveFast(string key)
|
||||||
|
|
@ -7181,6 +7185,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
protected override void SetValueFast(string key, StringValues value)
|
protected override void SetValueFast(string key, StringValues value)
|
||||||
{
|
{
|
||||||
|
ValidateHeaderCharacters(value);
|
||||||
switch (key.Length)
|
switch (key.Length)
|
||||||
{
|
{
|
||||||
case 13:
|
case 13:
|
||||||
|
|
@ -7512,10 +7517,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ValidateHeaderCharacters(key);
|
||||||
Unknown[key] = value;
|
Unknown[key] = value;
|
||||||
}
|
}
|
||||||
protected override void AddValueFast(string key, StringValues value)
|
protected override void AddValueFast(string key, StringValues value)
|
||||||
{
|
{
|
||||||
|
ValidateHeaderCharacters(value);
|
||||||
switch (key.Length)
|
switch (key.Length)
|
||||||
{
|
{
|
||||||
case 13:
|
case 13:
|
||||||
|
|
@ -7991,6 +7998,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ValidateHeaderCharacters(key);
|
||||||
Unknown.Add(key, value);
|
Unknown.Add(key, value);
|
||||||
}
|
}
|
||||||
protected override bool RemoveFast(string key)
|
protected override bool RemoveFast(string key)
|
||||||
|
|
|
||||||
|
|
@ -39,21 +39,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
// Unlike the IHeaderDictionary version, this getter will throw a KeyNotFoundException.
|
||||||
return GetValueFast(key);
|
return GetValueFast(key);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_isReadOnly)
|
((IHeaderDictionary)this)[key] = value;
|
||||||
{
|
|
||||||
ThrowReadOnlyException();
|
|
||||||
}
|
|
||||||
SetValueFast(key, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ThrowReadOnlyException()
|
protected void ThrowReadOnlyException()
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Headers are readonly, reponse has already started.");
|
throw new InvalidOperationException("Headers are read-only, response has already started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ThrowArgumentException()
|
protected void ThrowArgumentException()
|
||||||
|
|
@ -140,11 +137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
|
|
||||||
void ICollection<KeyValuePair<string, StringValues>>.Add(KeyValuePair<string, StringValues> item)
|
void ICollection<KeyValuePair<string, StringValues>>.Add(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
if (_isReadOnly)
|
((IDictionary<string, StringValues>)this).Add(item.Key, item.Value);
|
||||||
{
|
|
||||||
ThrowReadOnlyException();
|
|
||||||
}
|
|
||||||
AddValueFast(item.Key, item.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDictionary<string, StringValues>.Add(string key, StringValues value)
|
void IDictionary<string, StringValues>.Add(string key, StringValues value)
|
||||||
|
|
@ -216,5 +209,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
return TryGetValueFast(key, out value);
|
return TryGetValueFast(key, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ValidateHeaderCharacters(StringValues headerValues)
|
||||||
|
{
|
||||||
|
foreach (var value in headerValues)
|
||||||
|
{
|
||||||
|
ValidateHeaderCharacters(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ValidateHeaderCharacters(string headerCharacters)
|
||||||
|
{
|
||||||
|
if (headerCharacters != null)
|
||||||
|
{
|
||||||
|
foreach (var ch in headerCharacters)
|
||||||
|
{
|
||||||
|
if (ch < 0x20)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format("Invalid control character in header: 0x{0:X2}", (byte)ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}}
|
}}
|
||||||
protected override void SetValueFast(string key, StringValues value)
|
protected override void SetValueFast(string key, StringValues value)
|
||||||
{{
|
{{
|
||||||
|
{(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(value);" : "")}
|
||||||
switch (key.Length)
|
switch (key.Length)
|
||||||
{{{Each(loop.HeadersByLength, byLength => $@"
|
{{{Each(loop.HeadersByLength, byLength => $@"
|
||||||
case {byLength.Key}:
|
case {byLength.Key}:
|
||||||
|
|
@ -313,10 +314,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
")}}}
|
")}}}
|
||||||
break;
|
break;
|
||||||
")}}}
|
")}}}
|
||||||
|
{(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(key);" : "")}
|
||||||
Unknown[key] = value;
|
Unknown[key] = value;
|
||||||
}}
|
}}
|
||||||
protected override void AddValueFast(string key, StringValues value)
|
protected override void AddValueFast(string key, StringValues value)
|
||||||
{{
|
{{
|
||||||
|
{(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(value);" : "")}
|
||||||
switch (key.Length)
|
switch (key.Length)
|
||||||
{{{Each(loop.HeadersByLength, byLength => $@"
|
{{{Each(loop.HeadersByLength, byLength => $@"
|
||||||
case {byLength.Key}:
|
case {byLength.Key}:
|
||||||
|
|
@ -335,6 +338,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
")}}}
|
")}}}
|
||||||
break;
|
break;
|
||||||
")}}}
|
")}}}
|
||||||
|
{(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(key);" : "")}
|
||||||
Unknown.Add(key, value);
|
Unknown.Add(key, value);
|
||||||
}}
|
}}
|
||||||
protected override bool RemoveFast(string key)
|
protected override bool RemoveFast(string key)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue