Prefácio: por que segurança não é uma opção, mas uma higiene básica
\n\nImagine: você está escrevendo um serviço web incrível em C#. Tudo funciona perfeitamente — usuários se registram, deixam comentários, pesquisam produtos. E então aparece o "hacker Zé" (que na verdade só copiou um script da internet) — e vaza todo o banco de dados. Ou, pior ainda, insere na sua página um script que rouba os cookies de todos os visitantes.
\n\nParece roteiro de filme de terror? Mas isso acontece todos os dias. Segundo as estatísticas da OWASP, injeções (SQL, XSS, LDAP) estão entre as 3 vulnerabilidades mais perigosas de aplicações web. E o C#, apesar de toda sua confiabilidade, não oferece um escudo de imunidade por padrão.
\n\nNeste artigo, vamos analisar os dois ataques mais críticos — injeções SQL e XSS (Cross-Site Scripting). Você não terá apenas teoria, mas sim técnicas práticas de defesa em C# com exemplos reais de código. Vamos lá!
\n\nInjeções SQL: quando o usuário se torna hacker
\n\nComo isso funciona na prática
\n\nSuponha que você tenha um sistema de login simples. O usuário insere login e senha, e você monta a consulta assim:
\n\nstring login = txtLogin.Text;\nstring password = txtPassword.Text;\nstring query = "SELECT * FROM Users WHERE Login = '" + login + "' AND Password = '" + password + "'";\nSqlCommand cmd = new SqlCommand(query, connection);\n\nParece lógico? Erro! Se um invasor inserir no campo de login: ' OR 1=1 --, a consulta se transforma em:
SELECT * FROM Users WHERE Login = '' OR 1=1 --' AND Password = '...'\n\nTudo após -- é um comentário. A condição 1=1 é sempre verdadeira. A consulta retorna todos os usuários, e a autenticação é aprovada. E se, em vez de um comentário, for inserido DROP TABLE Users? Adeus, banco de dados.
Defesa nº1: consultas parametrizadas (seu principal escudo)
\n\nA melhor defesa contra injeções SQL é nunca concatenar strings manualmente. Use parâmetros. O servidor SQL escapa automaticamente os caracteres perigosos, e seu banco de dados recebe os dados como valores, não como código.
\n\nstring login = txtLogin.Text;\nstring password = txtPassword.Text;\n\nstring query = "SELECT * FROM Users WHERE Login = @Login AND Password = @Password";\n\nusing (SqlConnection conn = new SqlConnection(connectionString))\n{\n SqlCommand cmd = new SqlCommand(query, conn);\n cmd.Parameters.AddWithValue("@Login", login);\n cmd.Parameters.AddWithValue("@Password", password);\n \n conn.Open();\n SqlDataReader reader = cmd.ExecuteReader();\n // processamento do resultado\n}\n\nObserve: @Login e @Password são parâmetros. Mesmo que o usuário insira '; DROP TABLE Users; --, o SGBD interpretará isso como uma string comum, não como código. Seguro e elegante.
Defesa nº2: procedimentos armazenados
\n\nOutra ferramenta poderosa são os procedimentos armazenados. Eles não apenas protegem contra injeções, mas também melhoram o desempenho (planos de consulta são armazenados em cache).
\n\n-- SQL Server\nCREATE PROCEDURE GetUserByLogin\n @Login NVARCHAR(50)\nAS\nBEGIN\n SELECT * FROM Users WHERE Login = @Login\nEND\n\n// Código C#\nusing (SqlConnection conn = new SqlConnection(connectionString))\n{\n SqlCommand cmd = new SqlCommand("GetUserByLogin", conn);\n cmd.CommandType = CommandType.StoredProcedure;\n cmd.Parameters.AddWithValue("@Login", inputLogin);\n \n conn.Open();\n // ...\n}\n\nDefesa nº3: Entity Framework e ORM
\n\nSe você usa Entity Framework (ou qualquer outro ORM), já está parcialmente protegido. O EF gera consultas parametrizadas automaticamente. Mas há um detalhe: consultas SQL brutas via EF (método FromSqlRaw) — é a mesma concatenação. Fique atento!
// RUIM:\nvar users = context.Users.FromSqlRaw("SELECT * F