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; BadHttpRequestException ex;
switch (reason) switch (reason)
{ {
case RequestRejectionReason.TlsOverHttpError:
ex = new BadHttpRequestException(CoreStrings.HttpParserTlsOverHttpError, StatusCodes.Status400BadRequest, reason);
break;
case RequestRejectionReason.InvalidRequestLine: case RequestRejectionReason.InvalidRequestLine:
ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestLine_Detail(detail), StatusCodes.Status400BadRequest, reason); ex = new BadHttpRequestException(CoreStrings.FormatBadRequest_InvalidRequestLine_Detail(detail), StatusCodes.Status400BadRequest, reason);
break; 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"> <data name="Http2StreamResetByApplication" xml:space="preserve">
<value>The HTTP/2 stream was reset by the application with error code {errorCode}.</value> <value>The HTTP/2 stream was reset by the application with error code {errorCode}.</value>
</data> </data>
<data name="HttpParserTlsOverHttpError" xml:space="preserve">
<value>Detected a TLS handshake to an endpoint that does not have TLS enabled.</value>
</data>
</root> </root>

View File

@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private const byte ByteTab = (byte)'\t'; private const byte ByteTab = (byte)'\t';
private const byte ByteQuestionMark = (byte)'?'; private const byte ByteQuestionMark = (byte)'?';
private const byte BytePercentage = (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) 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); 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] [StackTraceHidden]
private unsafe void RejectRequestLine(byte* requestLine, int length) 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] [StackTraceHidden]
private unsafe void RejectRequestHeader(byte* headerLine, int length) 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. // 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 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
internal enum RequestRejectionReason internal enum RequestRejectionReason
{ {
TlsOverHttpError,
UnrecognizedHTTPVersion, UnrecognizedHTTPVersion,
InvalidRequestLine, InvalidRequestLine,
InvalidRequestHeader, 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -394,6 +394,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(buffer.End, examined); 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] [Fact]
public void ParseHeadersWithGratuitouslySplitBuffers() public void ParseHeadersWithGratuitouslySplitBuffers()
{ {