
Creación de un Panel de administración de operaciones en MQL5 (Parte VII): Usuario de confianza, recuperación y criptografía
Contenido:
- Introducción
- Implementación de usuarios de confianza en el Panel de administración
- Criptografía y ejemplos de aplicación en el Panel de administración
- Pruebas
- Conclusión
Introducción
El Market de MQL5 ejemplifica la importancia de la identidad de los dispositivos para garantizar la seguridad de los productos distribuidos. Nuestra exploración actual está motivada por los desafíos que hemos encontrado al trabajar con nuestro Panel de administración seguro. Desde que se implementaron medidas de seguridad mejoradas, el proceso de desarrollo y prueba ha enfrentado demoras debido a frecuentes solicitudes de inicio de sesión y solicitudes de autenticación generadas por cada compilación de archivo o prueba de funciones en la terminal. Esta capa adicional de seguridad, aunque necesaria, ha introducido fricción en nuestro flujo de trabajo.
La advertencia que se muestra a continuación aparece en la primera página del Market al publicar un nuevo producto. Destaca cómo MQL5 prioriza y aplica medidas de seguridad sólidas para proteger tanto a los desarrolladores como a los usuarios.Cada Programa vendido en el Mercado está encriptado con una clave especial para proteger el Producto contra uso ilegal. La clave de cifrado es única para cada Comprador y está vinculada a su ordenador, de modo que todos los Productos del Mercado tienen protección automática contra copia.
El producto adquirido se puede activar al menos cinco veces. Esto garantiza el equilibrio de intereses entre compradores y vendedores. El número de activaciones disponibles lo establece el Vendedor.
Muchas aplicaciones y sitios web implementan protección de segunda capa de forma selectiva, activándola solo cuando se detecta actividad sospechosa, como uso de IP anónima, intentos de inicio de sesión desde nuevos dispositivos o múltiples intentos fallidos de inicio de sesión. Este enfoque minimiza las interrupciones manteniendo la seguridad.
En nuestro caso, las pruebas retrasadas durante el desarrollo se deben a la introducción repetida de contraseñas y a la verificación de la aplicación Telegram en busca de códigos de 6 dígitos generados. Las indicaciones frecuentes pueden volverse tediosas, en particular cuando son provocadas por cambios en la actividad del terminal. A continuación se presentan algunas actividades notables que conducen a la reinicialización del dispositivo y las posteriores solicitudes de contraseña:
- Cambio de par.
- Cambio de marco temporal.
- Reinicio de terminal, etc.
En determinados casos, nuestros programas se reinician repetidamente debido a diversas actividades, un proceso que es inevitable por motivos técnicos u operativos. El algoritmo de validación del usuario está integrado al inicio de la función de inicialización, lo que hace imposible que el programa continúe sin pasar este paso. Sin embargo, podemos introducir un mecanismo de derivación dentro de la función de inicialización para optimizar el proceso. Este algoritmo de derivación monitorea la cantidad de intentos de inicio de sesión, lo que permite una experiencia más fluida durante sesiones válidas.
Si el número de intentos fallidos de contraseña excede el límite establecido, esto indica un comportamiento sospechoso o que el usuario original puede haber perdido u olvidado su contraseña. Por otro lado, si se ingresa la contraseña correcta dentro de tres intentos, el sistema omite de manera inteligente la 2FA, ahorrando un tiempo significativo al evitar la autenticación repetida basada en Telegram. Este enfoque mejora tanto la seguridad como la eficiencia, especialmente durante las fases intensivas de desarrollo y prueba.
La imagen a continuación resalta el problema de los múltiples mensajes de verificación enviados a Telegram durante el desarrollo de la aplicación, lo que muestra la necesidad práctica de este algoritmo refinado.
Panel de administración: Bot personal de entrega de código 2FA de Telegram.
Anteriormente, las solicitudes repetidas de contraseña y autenticación 2FA durante cada inicialización del programa hacían que el proceso fuera extremadamente lento. Para ahorrar tiempo durante el desarrollo, agregué una línea de código para imprimir el código de verificación de 6 dígitos enviado a Telegram directamente en el registro experto de la terminal. Esto me permitió recuperar rápidamente el código del registro durante la prueba de la aplicación sin necesidad de cambiar a la aplicación Telegram, agilizando significativamente el proceso.
El fragmento de código en cuestión es parte de la función responsable de enviar mensajes de Telegram. Dentro de él, incluí una función para registrar el mensaje en el diario para mayor comodidad. Sin embargo, para esta fase de desarrollo, planeo eliminar esta función para mejorar la seguridad en futuras versiones. Dejar información confidencial, como códigos de verificación, expuesta en el diario podría representar un riesgo de seguridad si se produce acceso no autorizado. Este paso refleja un equilibrio entre la optimización del proceso de desarrollo y la adhesión a las mejores prácticas para proteger datos confidenciales.
El fragmento de código a continuación muestra la línea que imprime el mensaje enviado.
//+------------------------------------------------------------------+ //| Send the message to Telegram | //+------------------------------------------------------------------+ bool SendMessageToTelegram(string message, string chatId, string botToken) { string url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}"; char postData[]; ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1); int timeout = 5000; char result[]; string responseHeaders; int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders); if (responseCode == 200) { Print("Message sent successfully: ", message); //This line prints the message in journal,thus a security leak that I used to bypass telegram during app tests. return true; } else { Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); return false; } }
A continuación se resalta un mensaje enviado a Telegram, incluido el código de verificación. Este enfoque fue una forma conveniente de evitar la verificación del código en Telegram durante el desarrollo. Sin embargo, dejarlo puesto supone un riesgo de seguridad significativo, ya que personas no autorizadas podrían obtener el código directamente del terminal sin necesidad de acceder a Telegram. El texto suele aparecer impreso en la pestaña "Expertos" del terminal:
Objetivo de la discusión:2024.11.22 08:44:47.980 Admin Panel V1.22 (Volatility 75 (1s) Index,M1) Message sent successfully: A login attempt was made on the Admin Panel. Please use this code to verify your identity: 028901 2024.11.22 08:44:48.040 Admin Panel V1.22 (Volatility 75 (1s) Index,M1) Password authentication successful. A 2FA code has been sent to your Telegram.
El objetivo de esta discusión es integrar un sistema de reconocimiento de usuarios confiable que agilice los procesos de inicio de sesión y 2FA. Los usuarios que ingresen exitosamente la contraseña correcta dentro de tres intentos pueden omitir la 2FA, mientras que exceder el límite activa la 2FA, requiriendo un código enviado a través de Telegram y la contraseña codificada para la recuperación. Este sistema equilibra conveniencia y seguridad al limitar los intentos de reintento para mitigar ataques de fuerza bruta y al mismo tiempo garantizar un proceso de autenticación fluido. La confianza es específica de cada sesión y requiere revalidación con cada nuevo inicio de sesión. La siguiente sección proporcionará más información sobre las medidas para abordar los ataques de fuerza bruta.
¿Qué es un ataque de fuerza bruta?
Un ataque de fuerza bruta es un método utilizado por los atacantes para obtener acceso no autorizado a cuentas, sistemas o datos cifrados probando sistemáticamente todas las combinaciones posibles de contraseñas, claves de cifrado o credenciales hasta encontrar la correcta. Este enfoque se basa en la potencia computacional y la persistencia en lugar de explotar vulnerabilidades del propio sistema.
Características principales de un ataque de fuerza bruta:
- Prueba y error: el atacante intenta repetidamente diferentes combinaciones de caracteres hasta obtener acceso.
- Automatización: A menudo se utilizan herramientas o scripts especializados para automatizar el proceso, lo que permite realizar miles o incluso millones de intentos en un período corto.
- Requiere mucho tiempo: el tiempo requerido depende de la complejidad de la contraseña o clave de destino. Las contraseñas más seguras, con más caracteres y combinaciones variadas, tardan mucho más en descifrarse.
Tipos de ataques de fuerza bruta:
- Fuerza bruta simple: probar todas las combinaciones posibles de caracteres.
- Ataque de diccionario: utilizar una lista de contraseñas de uso común (por ejemplo, «123456», «contraseña» o «qwerty») para intentar acceder.
- Relleno de credenciales: uso de pares de nombres de usuario y contraseñas filtrados de otras violaciones para obtener acceso a cuentas en diferentes plataformas.
Prevención:
- Limitar los intentos de inicio de sesión.
- Aplicar contraseñas seguras (largas, con una combinación de mayúsculas, minúsculas, números y símbolos).
- Implementar la autenticación de dos factores (2FA) para agregar otra capa de seguridad.
La seguridad actual del acceso al panel es vulnerable al ataque mencionado y, si bien limitar los intentos de inicio de sesión es un paso crucial, también buscamos evitar la autenticación frecuente a través de Telegram para ahorrar tiempo. El envío del código de verificación sirve para proteger contra el acceso no autorizado y alerta al administrador real sobre cualquier posible actividad maliciosa en el Panel, proporcionando una protección si no está al tanto de alguna violación de seguridad.
Implementación de usuarios de confianza en el Panel de administración
Para implementar la nueva función en el Panel de administración, modificamos solo algunas secciones de nuestro código fuente relacionadas con la seguridad y la inicialización del programa EA. Este enfoque garantiza que el resto del programa permanezca sin cambios, minimizando el riesgo de errores al gestionar un código grande. Aquí nos centramos en la función del controlador de inicio de sesión y hemos introducido nuevas variables, que se declaran en el siguiente fragmento de código.
int failedAttempts = 0; // Counter for failed login attempts bool isTrustedUser = false; // Flag for trusted users
Las variables globales failedAttempts e isTrustedUser son fundamentales para implementar las nuevas funciones. failedAttempts supervisa el número de entradas de contraseña incorrectas y ayuda a determinar cuándo se debe aplicar la autenticación de dos factores (2FA). El indicador isTrustedUser reconoce a los usuarios que inician sesión correctamente dentro del número de intentos permitidos, omitiendo el proceso de autenticación de dos factores (2FA) para estos usuarios. Al restablecer estas variables adecuadamente en la función OnLoginButtonClick, el sistema mantiene un control dinámico sobre el flujo de autenticación, lo que garantiza flexibilidad y seguridad.
Mejora de la función de inicio de sesión para las nuevas funciones
La función OnLoginButtonClick integra las nuevas características mediante el seguimiento de los intentos de inicio de sesión con el contador failedAttempts y la identificación de usuarios de confianza con el indicador isTrustedUser. Cuando se introduce una contraseña correcta, se comprueba si el usuario cumple los requisitos para ser considerado de confianza al haber realizado menos de tres intentos fallidos. Los usuarios confiables omiten el paso 2FA y pasan directamente al Panel de inicio de administración. Si los intentos fallidos superan el límite, la función genera un código 2FA, lo envía al usuario a través de Telegram junto con la contraseña codificada para su recuperación y muestra el mensaje de autenticación 2FA. Esto garantiza un equilibrio entre la comodidad del usuario para los usuarios confiables y la seguridad reforzada después de intentos fallidos.
//+------------------------------------------------------------------+ //| Handle login button click | //+------------------------------------------------------------------+ void OnLoginButtonClick() { string enteredPassword = passwordInputBox.Text(); if (enteredPassword == Password) { failedAttempts = 0; // Reset attempts on successful login isTrustedUser = true; if (failedAttempts <= 3) // Skip 2FA for trusted users { authentication.Destroy(); adminHomePanel.Show(); Print("Login successful. 2FA skipped for trusted user."); } else { } } else { failedAttempts++; feedbackLabel.Text("Wrong password. Try again."); passwordInputBox.Text(""); if (failedAttempts >= 3) { Print("Too many failed attempts. 2FA will be required."); twoFACode = GenerateRandom6DigitCode(); SendMessageToTelegram( "A login attempt was made on the Admin Panel.\n" + "Use this code to verify your identity: " + twoFACode + "\n" + "Reminder: Your admin password is: " + Password, Hardcoded2FAChatId, Hardcoded2FABotToken ); authentication.Destroy(); ShowTwoFactorAuthPrompt(); Print("Password authentication successful. A 2FA code has been sent to your Telegram."); failedAttempts = 0; // Reset attempts after requiring 2FA } } }
Cinco instancias durante la inicialización del programa
El proceso de autenticación, incluidas las nuevas funciones para omitir la autenticación de dos factores (2FA), se activa durante la inicialización porque la función ShowAuthenticationPrompt() muestra la interfaz de inicio de sesión, donde el controlador OnLoginButtonClick está vinculado al botón «Login». Así es como funciona paso a paso:
- Activación del mensaje de autenticación: En la función OnInit(), ShowAuthenticationPrompt() se llama primero. Esta función crea y muestra el cuadro de diálogo de autenticación que contiene el campo de entrada de contraseña y el botón «Login». El programa detiene la ejecución hasta que el usuario interactúe con el cuadro de diálogo.
- Gestión de intentos de inicio de sesión: Cuando el usuario hace clic en el botón «Login», se ejecuta la función OnLoginButtonClick. Esta función comprueba la contraseña introducida, actualiza el contador failedAttempts y determina si se concede acceso directamente o se aplica la autenticación de dos factores (2FA) en función del número de intentos fallidos.
- Procedimiento tras una autenticación correcta: si el inicio de sesión se realiza correctamente y el usuario cumple los requisitos para ser considerado un usuario de confianza (menos de tres intentos fallidos), el cuadro de diálogo de autenticación se cierra y se muestra inmediatamente el panel de inicio del administrador. Esto omite la autenticación de dos factores (2FA) para los usuarios de confianza.
- Requerir 2FA cuando sea necesario: Si el usuario supera el número permitido de intentos fallidos, el programa aplica la 2FA. Se genera un código de 6 dígitos y se envía a través de Telegram, y el cuadro de diálogo de autenticación 2FA se muestra mediante la función ShowTwoFactorAuthPrompt(). Recuerda la función para generar nuestro código de dígitos resaltada en rojo.
//+------------------------------------------------------------------+ //| Generate a random 6-digit code for 2FA | //+------------------------------------------------------------------+ string GenerateRandom6DigitCode() { int code = MathRand() % 1000000; // Generate a 6-digit number return StringFormat("%06d", code); // Ensure leading zeros }
5. Inicialización adicional: Una vez que el usuario ha sido autenticado (ya sea mediante un inicio de sesión de confianza o una autenticación de dos factores satisfactoria), el resto de paneles (panel de inicio de administración, panel de comunicaciones, panel de gestión de operaciones) se inicializan en segundo plano, pero permanecen ocultos hasta que se muestran explícitamente durante la navegación.
Aquí está el fragmento de código de la función de inicialización:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if (!ShowAuthenticationPrompt()) { Print("Authorization failed. Exiting..."); return INIT_FAILED; } if (!adminHomePanel.Create(ChartID(), "Admin Home Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Admin Home Panel"); return INIT_FAILED; } if (!CreateAdminHomeControls()) { Print("Home panel control creation failed"); return INIT_FAILED; } if (!communicationsPanel.Create(ChartID(), "Communications Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Communications panel dialog"); return INIT_FAILED; } if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0,260, 30, 1040, 170)) { Print("Failed to create Trade Management panel dialog"); return INIT_FAILED; } if (!CreateControls()) { Print("Control creation failed"); return INIT_FAILED; } if (!CreateTradeManagementControls()) { Print("Trade management control creation failed"); return INIT_FAILED; } adminHomePanel.Hide(); // Hide home panel by default on initialization communicationsPanel.Hide(); // Hide the Communications Panel tradeManagementPanel.Hide(); // Hide the Trade Management Panel return INIT_SUCCEEDED; }
Criptografía y ejemplos de aplicación en el Panel de administración
La criptografía en MQL5 consiste en el uso de algoritmos y métodos para proteger datos confidenciales, como contraseñas, mensajes o códigos de autenticación.
A continuación se describen cuatro métodos para aplicar el concepto en el Panel de administración.
1. Hashing de contraseñas
- La función HashPassword utiliza la función CryptEncode con el algoritmo CRYPT_HASH_SHA256 para realizar el hash de la contraseña introducida. El resultado hash se convierte en una cadena hexadecimal utilizando CharArrayToHex para su almacenamiento o comparación. En la función VerifyPassword, la contraseña introducida se somete a un proceso de hash y se compara con el hash almacenado, lo que garantiza que no se almacenen ni procesen contraseñas en texto plano.
- El almacenamiento de contraseñas en hash garantiza que, incluso si se accede a los datos almacenados, las contraseñas reales permanecerán seguras. El hash es unidireccional, lo que significa que la contraseña original no se puede derivar del hash, lo que agrega una capa esencial de seguridad a la autenticación.
// Example for storing our hard-coded password string HashPassword(string password) { uchar hash[]; CryptEncode(CRYPT_HASH_SHA256, password, hash); return CharArrayToHex(hash); } // Usage string PasswordHash = HashPassword("2024"); // Store this instead of plaintext bool VerifyPassword(string enteredPassword) { return HashPassword(enteredPassword) == PasswordHash; }
2. Cifrado de datos confidenciales
- La función EncryptData utiliza el algoritmo AES-256 (CRYPT_AES256) para cifrar información confidencial, como códigos 2FA, utilizando una clave de cifrado segura. La función DecryptData invierte este proceso, descifrando los datos y devolviéndolos a su forma original. La clave es fundamental en este proceso, ya que debe coincidir durante el cifrado y el descifrado.
- El cifrado protege los datos confidenciales durante el almacenamiento o la transmisión. Por ejemplo, si el código 2FA se intercepta durante la comunicación, la versión cifrada garantiza que usuarios no autorizados no puedan interpretarlo sin la clave correcta.
//Example for encryption of our 2FA verification code string EncryptData(string data, string key) { uchar encrypted[]; CryptEncode(CRYPT_AES256, data, encrypted, StringToCharArray(key)); return CharArrayToHex(encrypted); } string DecryptData(string encryptedData, string key) { uchar decrypted[]; uchar input[]; StringToCharArray(encryptedData, input); CryptDecode(CRYPT_AES256, input, decrypted, StringToCharArray(key)); return CharArrayToString(decrypted); } // Usage string key = "StrongEncryptionKey123"; // Use a secure key string encrypted2FA = EncryptData(twoFACode, key); Print("Encrypted 2FA code: ", encrypted2FA);
3. Generación segura de números aleatorios para 2FA
- La función GenerateSecureRandom6DigitCode utiliza la función CryptEncode con el algoritmo CRYPT_HASH_SHA256 para generar una secuencia aleatoria criptográficamente segura. Luego, el resultado se convierte en un número de 6 dígitos utilizando aritmética modular y formato.
- Las funciones aleatorias estándar como MathRand son pseudoaleatorias y predecibles. El uso de aleatoriedad criptográfica garantiza que los códigos 2FA sean seguros y resistentes a ataques de predicción o fuerza bruta, lo que mejora la seguridad del proceso de autenticación.
// Example for generating a secure verification code string GenerateSecureRandom6DigitCode() { uchar randomBytes[3]; CryptEncode(CRYPT_HASH_SHA256, MathRand(), randomBytes); // Use CryptEncode for randomness int randomValue = (randomBytes[0] << 16) | (randomBytes[1] << 8) | randomBytes[2]; randomValue = MathAbs(randomValue % 1000000); // Ensure 6 digits return StringFormat("%06d", randomValue); }
4. Comunicaciones seguras con Telegram
- La función SendEncryptedMessageToTelegram cifra el mensaje utilizando la función EncryptData antes de transmitirlo al servidor de Telegram a través de la función SendMessageToTelegram. El mensaje cifrado sólo puede ser descifrado por alguien con la clave de descifrado correcta.
- El cifrado de la comunicación garantiza la confidencialidad de la información sensible, como los códigos 2FA, incluso si la transmisión es interceptada. Esto es especialmente importante cuando se utilizan plataformas de comunicación de terceros, donde los datos podrían no estar completamente seguros.
//Example of the securely sending to Telegram bool SendEncryptedMessageToTelegram(string message, string chatId, string botToken, string key) { string encryptedMessage = EncryptData(message, key); return SendMessageToTelegram(encryptedMessage, chatId, botToken); } // Usage string key = "StrongEncryptionKey123"; SendEncryptedMessageToTelegram("Your 2FA code is: " + twoFACode, Hardcoded2FAChatId, Hardcoded2FABotToken, key);
Pruebas
En esta etapa, presentaremos los resultados de nuestras mejoras de seguridad al Panel de administración. La actualización simplifica el proceso de inicio de sesión para usuarios confiables, permitiéndoles acceder rápidamente al panel, mientras que los usuarios no confiables deben someterse a una verificación secundaria para confirmar su autenticidad. Si un usuario olvida su contraseña, puede recuperarla a través del proceso de autenticación.
En el registro de expertos a continuación, mostramos los intentos fallidos de inicio de sesión, seguidos de una imagen que ilustra el flujo de inicio de sesión.
2024.11.22 03:53:59.675 Admin Panel V1.23 (Volatility 75 (1s) Index,M2) Too many failed attempts. 2FA will be required. 2024.11.22 03:54:00.643 Admin Panel V1.23 (Volatility 75 (1s) Index,M2) Message sent successfully: Check your telegram for verification code and Password 2024.11.22 03:54:00.646 Admin Panel V1.23 (Volatility 75 (1s) Index,M2) Password authentication successful. A 2FA code has been sent to your Telegram. 2024.11.22 03:54:22.946 Admin Panel V1.23 (Volatility 75 (1s) Index,M2) 2FA authentication successful. Access granted to Admin Home Panel.
Mensaje de Telegram enviado para verificación.
Panel de administración: Intentos de inicio de sesión fallidos.
Para los usuarios confiables, el proceso de inicio de sesión es sencillo. Pueden omitir el paso de verificación secundaria, agilizando su acceso al Panel de administración. A continuación se muestra el registro de expertos que muestra el inicio de sesión exitoso para usuarios confiables, seguido de una imagen que ilustra el proceso simple para aquellos que ingresan la contraseña correcta.2024.11.22 03:57:41.563 Admin Panel V1.23 (Volatility 75 (1s) Index,M2) Login successful. 2FA skipped for trusted user.
Panel de administración: Usuario de confianza. Inicio de sesión exitoso con contraseña.
Conclusión
En este debate sobre cómo mejorar el Panel de administración, logramos avances significativos tanto en funcionalidad como en seguridad. La introducción de una función de usuario confiable permite una experiencia de inicio de sesión más fluida y eficiente para usuarios conocidos al limitar los intentos de inicio de sesión a tres y omitiendo la 2FA para una autenticación exitosa dentro de este umbral. Este enfoque equilibra la seguridad y la facilidad de uso, reduciendo la fricción para los usuarios legítimos y manteniendo al mismo tiempo medidas estrictas de control de acceso para intentos no confiables.
También exploramos el potencial de la criptografía para fortalecer el marco de seguridad del panel. Al emplear hash de contraseñas, cifrado para datos confidenciales y generación segura de números aleatorios para códigos 2FA, garantizamos la integridad y confidencialidad de la información crítica. El hashing protege las contraseñas almacenadas contra accesos no autorizados, mientras que el cifrado salvaguarda los datos confidenciales durante la transmisión o el almacenamiento. Además, la aleatoriedad criptográficamente segura garantiza la imprevisibilidad de los códigos generados, agregando una capa adicional de defensa contra ataques de fuerza bruta.
Estos avances nos ayudan a garantizar una herramienta administrativa segura y fácil de usar para gestionar las comunicaciones y operaciones comerciales. Al abordar tanto la funcionalidad como la seguridad, preparamos el escenario para futuras mejoras que pueden agilizar aún más los flujos de trabajo y al mismo tiempo cumplir con los más altos estándares de protección de datos.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16339
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso