This commit is contained in:
parent
ef99d81d68
commit
1bff37bec1
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue