mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +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 6_files.qbk]
|
||||
[include 7_composed.qbk]
|
||||
[include 9_ssl-tls-shutdown.qbk]
|
||||
[endsect]
|
||||
|
||||
[section:config Configuration]
|
||||
|
@ -17,6 +17,8 @@ namespace boost { namespace asio { namespace ssl { } } }
|
||||
//[snippet_core_1a
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#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
|
||||
|
Reference in New Issue
Block a user