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;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -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>
|
||||||
</root>
|
<data name="HttpParserTlsOverHttpError" xml:space="preserve">
|
||||||
|
<value>Detected a TLS handshake to an endpoint that does not have TLS enabled.</value>
|
||||||
|
</data>
|
||||||
|
</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