Issue #11559: Show meaningful error message for TLS over HTTP endpoint. (#12697)

This commit is contained in:
bashdx 2019-07-30 19:26:54 +02:00 committed by Chris Ross
parent ef99d81d68
commit 1bff37bec1
5 changed files with 76 additions and 31 deletions

View File

@ -139,6 +139,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
BadHttpRequestException ex;
switch (reason)
{
case RequestRejectionReason.TlsOverHttpError:
ex = new BadHttpRequestException(CoreStrings.HttpParserTlsOverHttpError, StatusCodes.Status400BadRequest, reason);
break;
case RequestRejectionReason.InvalidRequestLine:
ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestLine_Detail(detail), StatusCodes.Status400BadRequest, reason);
break;

View File

@ -614,4 +614,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
<data name="Http2StreamResetByApplication" xml:space="preserve">
<value>The HTTP/2 stream was reset by the application with error code {errorCode}.</value>
</data>
<data name="HttpParserTlsOverHttpError" xml:space="preserve">
<value>Detected a TLS handshake to an endpoint that does not have TLS enabled.</value>
</data>
</root>

View File

@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private const byte ByteTab = (byte)'\t';
private const byte ByteQuestionMark = (byte)'?';
private const byte BytePercentage = (byte)'%';
private const int MinTlsRequestSize = 1; // We need at least 1 byte to check for a proper TLS request line
public unsafe bool ParseRequestLine(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{
@ -415,9 +416,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return new Span<byte>(data, methodLength);
}
private unsafe bool IsTlsHandshake(byte* data, int length)
{
const byte SslRecordTypeHandshake = (byte)0x16;
// Make sure we can check at least for the existence of a TLS handshake - we check the first byte
// See https://serializethoughts.com/2014/07/27/dissecting-tls-client-hello-message/
return (length >= MinTlsRequestSize && data[0] == SslRecordTypeHandshake);
}
[StackTraceHidden]
private unsafe void RejectRequestLine(byte* requestLine, int length)
=> throw GetInvalidRequestException(RequestRejectionReason.InvalidRequestLine, requestLine, length);
{
// Check for incoming TLS handshake over HTTP
if (IsTlsHandshake(requestLine, length))
{
throw GetInvalidRequestException(RequestRejectionReason.TlsOverHttpError, requestLine, length);
}
else
{
throw GetInvalidRequestException(RequestRejectionReason.InvalidRequestLine, requestLine, length);
}
}
[StackTraceHidden]
private unsafe void RejectRequestHeader(byte* headerLine, int length)

View File

@ -1,10 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
internal enum RequestRejectionReason
{
TlsOverHttpError,
UnrecognizedHTTPVersion,
InvalidRequestLine,
InvalidRequestHeader,

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@ -394,6 +394,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(buffer.End, examined);
}
[Fact]
public void ParseRequestLineTlsOverHttp()
{
var parser = CreateParser(_nullTrace);
var buffer = ReadOnlySequenceFactory.CreateSegments(new byte[] { 0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0xfc, 0x03, 0x03, 0x03, 0xca, 0xe0, 0xfd, 0x0a });
var requestHandler = new RequestHandler();
var badHttpRequestException = Assert.Throws<BadHttpRequestException>(() =>
{
parser.ParseRequestLine(requestHandler, buffer, out var consumed, out var examined);
});
Assert.Equal(badHttpRequestException.StatusCode, StatusCodes.Status400BadRequest);
Assert.Equal(RequestRejectionReason.TlsOverHttpError, badHttpRequestException.Reason);
}
[Fact]
public void ParseHeadersWithGratuitouslySplitBuffers()
{