¿Cuál es la forma correcta de desconectar de forma segura un socket ASIO SSL?

Un socket TCP SSL / TLS boost-asio se implementa como una ssl::stream en un tcp::socket

 boost::asio::ssl::stream ssl_socket; 

En el protocolo TLS , un cierre criptográficamente seguro implica que las partes intercambien mensajes close_notify . Simplemente cerrar la capa más baja puede hacer que la sesión sea vulnerable a un ataque de truncamiento .

In boost asio ssl async_shutdown siempre termina con un error? @Tanner Sansbury describe el proceso de cierre SSL en detalle con una serie de escenarios y propone utilizar async_shutdown seguido de async_write para desconectar un flujo SSL antes de cerrar el socket:

 ssl_socket.async_shutdown(...); const char buffer[] = ""; async_write(ssl_socket, buffer, [](...) { ssl_socket.close(); }) 

La ejecución de un async_shutdown en un ssl::stream envía un mensaje SSL close_notify y espera una respuesta del otro extremo. El propósito de escribir en la transmisión después de async_shutdown es recibir una notificación cuando async_shutdown ha enviado close_notify para que el socket se pueda cerrar sin esperar la respuesta. Sin embargo, en la versión actual (1.59) de boost, la llamada a async_write falla …

En Cómo cerrar con gracia un cliente asio ssl? @maxschlepzig propone cerrar el receptor del socket TCP subyacente:

 ssl_socket.lowest_layer()::shutdown(tcp::socket::shutdown_receive); 

Esto produce un error de short read , y se llama a async_shutdown cuando se detecta en el controlador de errores:

 // const boost::system::error_code &ec if (ec.category() == asio::error::get_ssl_category() && ec.value() == ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) { // -> not a real error: do_ssl_async_shutdown(); } 

O cancelar las operaciones de lectura / escritura en el socket y luego llamar a SSL async shutdown, es decir:

 boost::system::error_code ec; ssl_socket.cancel(ec); ssl_socket.async_shutdown([](...) { ssl_socket.close(); }; 

Actualmente estoy usando este último método ya que funciona con la versión actual de boost .

¿Cuál es la mejor / la mejor forma de desconectar de forma segura un socket SSL de boost-asio ?

Para desconectar de forma segura, realice una operación de apagado y luego cierre el transporte subyacente una vez que se haya completado el apagado. Por lo tanto, el método que esté utilizando realizará una desconexión segura:

 boost::system::error_code ec; ssl_socket.cancel(ec); ssl_socket.async_shutdown([](...) { ssl_socket.close(); }; 

Tenga en cuenta que la operación actual async_shutdown se considerará completa cuando:

  • Un close_notify ha recibido un close_notify .
  • El par remoto cierra el socket.
  • La operación ha sido cancelada.

Por lo tanto, si los recursos están ligados a la duración del socket o la conexión, estos recursos permanecerán vivos esperando que el par remoto actúe o hasta que la operación se cancele localmente. Sin embargo, no es necesario esperar una respuesta close_notify para un cierre seguro. Si los recursos están vinculados a la conexión, y localmente la conexión se considera muerta al enviar un cierre, entonces puede valer la pena no esperar a que el par remoto tome medidas:

 ssl_socket.async_shutdown(...); const char buffer[] = ""; async_write(ssl_socket, boost::asio::buffer(buffer), [](...) { ssl_socket.close(); }) 

Cuando un cliente envía un mensaje close_notify , el cliente garantiza que el cliente no enviará datos adicionales a través de la conexión segura. En esencia, el async_write() se usa para detectar cuándo el cliente ha enviado un close_notify , y dentro del controlador de finalización, cerrará el transporte subyacente, haciendo que el async_shutdown() complete con boost::asio::error::operation_aborted . Como se señala en la respuesta vinculada , se espera que falle la operación async_write() .

… ya que el lado de escritura de la transmisión SSL de PartyA se ha cerrado, la operación async_write() fallará con un error de SSL que indica que el protocolo se ha apagado.

 if ((error.category() == boost::asio::error::get_ssl_category()) && (SSL_R_PROTOCOL_IS_SHUTDOWN == ERR_GET_REASON(error.value()))) { ssl_stream.lowest_layer().close(); } 

La async_write() fallida async_write() cerrará explícitamente el transporte subyacente, provocando que la operación async_shutdown() que está esperando que close_notify de close_notify se cancele.