Do not allow control characters in response headers.

This commit is contained in:
Chris R 2016-05-10 10:59:42 -07:00
parent 5bcf381604
commit 3061a48a38
2 changed files with 93 additions and 0 deletions

View File

@ -41,6 +41,8 @@ namespace Microsoft.Net.Http.Server
}
else
{
ValidateHeaderCharacters(key);
ValidateHeaderCharacters(value);
Store[key] = value;
}
}
@ -52,6 +54,8 @@ namespace Microsoft.Net.Http.Server
set
{
ThrowIfReadOnly();
ValidateHeaderCharacters(key);
ValidateHeaderCharacters(value);
Store[key] = value;
}
}
@ -74,18 +78,24 @@ namespace Microsoft.Net.Http.Server
public void Add(KeyValuePair<string, StringValues> item)
{
ThrowIfReadOnly();
ValidateHeaderCharacters(item.Key);
ValidateHeaderCharacters(item.Value);
Store.Add(item);
}
public void Add(string key, StringValues value)
{
ThrowIfReadOnly();
ValidateHeaderCharacters(key);
ValidateHeaderCharacters(value);
Store.Add(key, value);
}
public void Append(string key, string value)
{
ThrowIfReadOnly();
ValidateHeaderCharacters(key);
ValidateHeaderCharacters(value);
StringValues values;
Store.TryGetValue(key, out values);
Store[key] = StringValues.Concat(values, value);
@ -156,5 +166,27 @@ namespace Microsoft.Net.Http.Server
throw new InvalidOperationException("The response headers cannot be modified because the response has already started.");
}
}
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));
}
}
}
}
}
}

View File

@ -1,11 +1,13 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Primitives;
using Xunit;
namespace Microsoft.Net.Http.Server
@ -415,6 +417,65 @@ namespace Microsoft.Net.Http.Server
}
}
[Theory]
[InlineData("Server", "\r\nData")]
[InlineData("Server", "\0Data")]
[InlineData("Server", "Data\r")]
[InlineData("Server", "Da\0ta")]
[InlineData("Server", "Da\u001Fta")]
[InlineData("Unknown-Header", "\r\nData")]
[InlineData("Unknown-Header", "\0Data")]
[InlineData("Unknown-Header", "Data\0")]
[InlineData("Unknown-Header", "Da\nta")]
[InlineData("\r\nServer", "Data")]
[InlineData("Server\r", "Data")]
[InlineData("Ser\0ver", "Data")]
[InlineData("Server\r\n", "Data")]
[InlineData("\u001FServer", "Data")]
[InlineData("Unknown-Header\r\n", "Data")]
[InlineData("\0Unknown-Header", "Data")]
[InlineData("Unknown\r-Header", "Data")]
[InlineData("Unk\nown-Header", "Data")]
public async Task AddingControlCharactersToHeadersThrows(string key, string value)
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
Assert.Throws<InvalidOperationException>(() => {
responseHeaders[key] = value;
});
Assert.Throws<InvalidOperationException>(() => {
responseHeaders[key] = new StringValues(new[] { "valid", value });
});
Assert.Throws<InvalidOperationException>(() => {
((IDictionary<string, StringValues>)responseHeaders)[key] = value;
});
Assert.Throws<InvalidOperationException>(() => {
var kvp = new KeyValuePair<string, StringValues>(key, value);
((ICollection<KeyValuePair<string, StringValues>>)responseHeaders).Add(kvp);
});
Assert.Throws<InvalidOperationException>(() => {
var kvp = new KeyValuePair<string, StringValues>(key, value);
((IDictionary<string, StringValues>)responseHeaders).Add(key, value);
});
context.Dispose();
HttpResponseMessage response = await responseTask;
response.EnsureSuccessStatusCode();
}
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool usehttp11 = true, bool sendKeepAlive = false)
{
using (HttpClient client = new HttpClient())