mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 21:07:26 +02:00
committed by
Mohammad Nejati
parent
9bf2184b33
commit
cf72589ae9
69
doc/qbk/03_core/9_ssl-tls-shutdown.qbk
Normal file
69
doc/qbk/03_core/9_ssl-tls-shutdown.qbk
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
[/
|
||||||
|
Copyright (c) 2024 Mohammad Nejati
|
||||||
|
|
||||||
|
Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
Official repository: https://github.com/boostorg/beast
|
||||||
|
]
|
||||||
|
|
||||||
|
[section SSL/TLS Shutdown]
|
||||||
|
[block'''<?dbhtml stop-chunking?>''']
|
||||||
|
|
||||||
|
A secure SSL/TLS connection requires a proper shutdown process to securely
|
||||||
|
indicate the [@https://en.wikipedia.org/wiki/End-of-file ['EOF]] condition.
|
||||||
|
This process prevents a type of attack known as a
|
||||||
|
[@https://en.wikipedia.org/wiki/Transport_Layer_Security#Truncation_attack ['truncation attack]]
|
||||||
|
in which an attacker can close the underlying transport layer and control
|
||||||
|
the length of the last message in the SSL/TLS connection. A shutdown process
|
||||||
|
consists of exchanging `close_notify` message between two parties. In __Asio__
|
||||||
|
these steps happen by calling `shutdown()` or `async_shutdown()` on
|
||||||
|
`ssl::stream` object.
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[section error::stream_truncated]
|
||||||
|
|
||||||
|
There are SSL/TLS implementations that don't perform a proper shutdown process
|
||||||
|
and simply close the underlying transport layer instead. As a result, the EOF
|
||||||
|
condition in these applications is not cryptographically secure and should not
|
||||||
|
be relied upon. However, there are scenarios where an HTTPS client or server
|
||||||
|
doesn't need EOF for determining the end of the last message:
|
||||||
|
|
||||||
|
* The HTTP message has a `Content-Length` header, and the body is fully
|
||||||
|
received (a known body length).
|
||||||
|
|
||||||
|
* The HTTP message uses chunked transfer encoding, and the final chunk is
|
||||||
|
received.
|
||||||
|
|
||||||
|
* The HTTP message doesn't contain a body, such as any response with a 1xx
|
||||||
|
(Informational), 204 (No Content), or 304 (Not Modified) status code.
|
||||||
|
|
||||||
|
In such scenarios, `http::read` or `http::async_read` operations succeed as
|
||||||
|
they don't need EOF to complete. However, the next operation on the stream
|
||||||
|
would fail with an
|
||||||
|
[@boost:/doc/html/boost_asio/reference/ssl__error__stream_errors.html `net::ssl::error::stream_truncated`]
|
||||||
|
error.
|
||||||
|
|
||||||
|
For example, let's assume we are using Beast for communicating with an HTTPS
|
||||||
|
server that doesn't perform a proper SSL/TLS shutdown:
|
||||||
|
|
||||||
|
[snippet_core_4]
|
||||||
|
|
||||||
|
[/-----------------------------------------------------------------------------]
|
||||||
|
|
||||||
|
[heading Non-Compliant Peers and Unknown Body Length]
|
||||||
|
|
||||||
|
This is a rare case and indeed a security issue when HTTPS servers don't
|
||||||
|
perform a proper SSL/TLS shutdown procedure and send an HTTP response message
|
||||||
|
that relies on EOF to determine the end of the body. This is a security concern
|
||||||
|
because without an SSL/TLS shutdown procedure, the EOF is not cryptographically
|
||||||
|
secure, leaving the message body vulnerable to truncation attacks.
|
||||||
|
|
||||||
|
The following is an example that can read an HTTP response from such a server:
|
||||||
|
|
||||||
|
[snippet_core_5]
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
|
[endsect]
|
@ -96,6 +96,7 @@ effect:
|
|||||||
[include 5_buffers.qbk]
|
[include 5_buffers.qbk]
|
||||||
[include 6_files.qbk]
|
[include 6_files.qbk]
|
||||||
[include 7_composed.qbk]
|
[include 7_composed.qbk]
|
||||||
|
[include 9_ssl-tls-shutdown.qbk]
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
[section:config Configuration]
|
[section:config Configuration]
|
||||||
|
@ -17,6 +17,8 @@ namespace boost { namespace asio { namespace ssl { } } }
|
|||||||
//[snippet_core_1a
|
//[snippet_core_1a
|
||||||
|
|
||||||
#include <boost/beast/core.hpp>
|
#include <boost/beast/core.hpp>
|
||||||
|
#include <boost/beast/http.hpp>
|
||||||
|
#include <boost/beast/ssl.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -83,4 +85,63 @@ void write_string(SyncWriteStream& stream, string_view s)
|
|||||||
|
|
||||||
//]
|
//]
|
||||||
|
|
||||||
|
void ssl_tls_shutdown()
|
||||||
|
{
|
||||||
|
net::io_context ioc;
|
||||||
|
net::ssl::context ctx(net::ssl::context::tlsv12);
|
||||||
|
ssl_stream<tcp_stream> stream(ioc, ctx);
|
||||||
|
flat_buffer buffer;
|
||||||
|
http::response<http::dynamic_body> res;
|
||||||
|
auto log = [](error_code){};
|
||||||
|
|
||||||
|
{
|
||||||
|
//[snippet_core_4
|
||||||
|
// Receive the HTTP response
|
||||||
|
http::read(stream, buffer, res);
|
||||||
|
|
||||||
|
// Gracefully shutdown the SSL/TLS connection
|
||||||
|
error_code ec;
|
||||||
|
stream.shutdown(ec);
|
||||||
|
// Non-compliant servers don't participate in the SSL/TLS shutdown process and
|
||||||
|
// close the underlying transport layer. This causes the shutdown operation to
|
||||||
|
// complete with a `stream_truncated` error. One might decide not to log such
|
||||||
|
// errors as there are many non-compliant servers in the wild.
|
||||||
|
if(ec != net::ssl::error::stream_truncated)
|
||||||
|
log(ec);
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//[snippet_core_5
|
||||||
|
// Use an HTTP response parser to have more control
|
||||||
|
http::response_parser<http::dynamic_body> parser;
|
||||||
|
|
||||||
|
error_code ec;
|
||||||
|
// Receive the HTTP response until the end or until an error occurs
|
||||||
|
http::read(stream, buffer, parser, ec);
|
||||||
|
|
||||||
|
// Try to manually commit the EOF, the resulting message body would be
|
||||||
|
// vulnerable to truncation attacks.
|
||||||
|
if(parser.need_eof() && ec == net::ssl::error::stream_truncated)
|
||||||
|
parser.put_eof(ec); // Override the error_code
|
||||||
|
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
|
||||||
|
// Access the HTTP response inside the parser
|
||||||
|
std::cout << parser.get() << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
// Gracefully shutdown the SSL/TLS connection
|
||||||
|
stream.shutdown(ec); // Override the error_code
|
||||||
|
// Non-compliant servers don't participate in the SSL/TLS shutdown process and
|
||||||
|
// close the underlying transport layer. This causes the shutdown operation to
|
||||||
|
// complete with a `stream_truncated` error. One might decide not to log such
|
||||||
|
// errors as there are many non-compliant servers in the wild.
|
||||||
|
if(ec != net::ssl::error::stream_truncated)
|
||||||
|
log(ec);
|
||||||
|
//]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // doc_core_snippets
|
} // doc_core_snippets
|
||||||
|
Reference in New Issue
Block a user