Cuando C++ se encuentra con la web: por qué la seguridad es su mayor dolor de cabeza
\n\n¿Alguna vez se ha preguntado por qué los hackers aman tanto las aplicaciones C++? No, no porque el código sea complejo. Sino porque en la búsqueda del rendimiento, los desarrolladores a menudo se olvidan de la seguridad en C++. Y lo pagan con los datos de los usuarios, su reputación y su dinero.
\n\nImagínese: escribió un servidor increíble en C++ que procesa solicitudes más rápido que un rayo. Pero una sola entrada sin escapar — y un atacante extrae toda la base de datos. O, peor aún, inserta un script que roba las sesiones de todos los visitantes del sitio. ¿Le suena familiar? Si no — perfecto, significa que está leyendo este artículo a tiempo.
\n\nEn este material, analizaremos los dos ataques más peligrosos a aplicaciones web — inyecciones SQL y Cross-Site Scripting (XSS) — y mostraremos cómo protegerse de ellos específicamente en C++. Sin magia, con ejemplos de código reales. Comencemos.
\n\nInyecciones SQL: cómo los hackers convierten su consulta en un arma
\n\nUna inyección SQL ocurre cuando un atacante inserta en un campo de entrada (por ejemplo, un nombre de usuario o una consulta de búsqueda) un fragmento de código SQL. Si su aplicación inserta ciegamente esta entrada en la consulta, en lugar de buscar un usuario por nombre, la base de datos ejecutará algo como DROP TABLE users. O, peor aún, devolverá todas las contraseñas.
El error típico de un principiante (y cómo evitarlo)
\n\nMire este código. Parece inofensivo, pero es una bomba de tiempo:
\n\n// ¡PELIGROSO! Nunca haga esto\nstd::string username = getUserInput(); // el usuario ingresó: ' OR '1'='1\nstd::string query = "SELECT * FROM users WHERE username = '" + username + "'";\n// Resultado: SELECT * FROM users WHERE username = '' OR '1'='1'\n// ¡Devolverá todos los usuarios!\n\n¿Ve el problema? Si el usuario ingresa ' OR '1'='1, la consulta devolverá toda la tabla. Y si ingresa '; DROP TABLE users; --, entonces adiós, base de datos.
El enfoque correcto: consultas parametrizadas
\n\nLa única forma confiable de protegerse contra inyecciones SQL en C++ es usar consultas parametrizadas. La base de datos misma distingue el código de los datos. Así es como se ve con la biblioteca libpqxx (para PostgreSQL) o MySQL Connector/C++:
\n\n#include <iostream>\n#include <pqxx/pqxx> // libpqxx para PostgreSQL\n\nvoid safeQuery(const std::string& username) {\n try {\n pqxx::connection conn("dbname=mydb user=myuser password=mypass");\n pqxx::work txn(conn);\n \n // Consulta parametrizada: $1 es el parámetro\n std::string query = "SELECT * FROM users WHERE username = $1";\n pqxx::result result = txn.exec_params(query, username);\n \n for (auto row : result) {\n std::cout << "User: " << row["username"].c_str() << std::endl;\n }\n txn.commit();\n } catch (const std::exception &e) {\n std::cerr << "Error: " << e.what() << std::endl;\n }\n}\n\nint main() {\n std::string input;\n std::cout << "Enter username: ";\n std::getline(std::cin, input);\n safeQuery(input);\n return 0;\n}\n\nObserve: usamos txn.exec_params en lugar de concatenación de cadenas. La base de datos misma escapa los caracteres especiales. Incluso si el usuario ingresa ' OR '1'='1, esto se interpretará como una cadena, no como parte de un comando SQL.
¿Qué hacer si usa SQLite puro?
\n\nIncluso con SQLite se puede trabajar de forma segura. Use sqlite3_prepare_v2 y sqlite3_bind_text:
#include <sqlite3.h>\n#include <iostream>\n\nvoid safeSQLiteQuery(sqlite3* db, const std::string& username) {\n sqlite3_stmt* stmt;\n const char* sql = "SELECT * FROM users WHERE username = ?";\n \n if (sqlite3_prepare_v2