Construindo um scanner simples de segurança de aplicativos da Web com Python: um guia para iniciantes
Neste artigo, você aprenderá a criar uma ferramenta básica de segurança que pode ser útil na identificação de vulnerabilidades comuns em aplicativos da web.
Eu tenho dois objetivos aqui. O primeiro é capacitá -lo com as habilidades para desenvolver ferramentas que possam ajudar a aprimorar a postura geral de segurança de seus sites. O segundo é ajudá -lo a praticar alguma programação em Python.
Neste guia, você estará construindo um scanner de segurança baseado em Python que pode detectar XSS, injeção de SQL e PII sensível (informações pessoalmente identificáveis).
Tipos de vulnerabilidades
Geralmente, podemos categorizar as vulnerabilidades de segurança da web nos seguintes grupos (para ainda mais grupos, verifique o OWASP Top 10):
-
Injeção de SQL: Uma técnica em que invasores conseguem inserir código SQL malicioso em consultas SQL por meio de entradas não validadas, permitindo-lhes modificar/ler o conteúdo do banco de dados.
Scripts cross-sites (XSS) : Uma técnica em que os invasores injetam javascript malicioso em sites confiáveis. Isso lhes permite executar o código JavaScript no contexto do navegador e roubar informações confidenciais ou executar operações não autorizadas.
Exposição de informações confidenciais: um problema de segurança em que um aplicativo revela involuntariamente dados confidenciais, como senhas, chaves de API e assim por diante, por meio de logs, armazenamento inseguro e outras vulnerabilidades.
Configurações incorretas de segurança comuns: problemas de segurança que ocorrem devido à configuração inadequada de servidores web, como credenciais padrão para contas de administrador, modo de depuração ativado, painéis de administrador disponíveis publicamente com credenciais fracas e assim por diante.
Fraquezas básicas de autenticação: problemas de segurança que ocorrem devido a falhas nas políticas de senha, processos de autenticação de usuários, gerenciamento inadequado de sessões e assim por diante.
Índice
Pré -requisitos
Configurando Nosso Ambiente de Desenvolvimento
Construindo nossa classe de scanner central
Implementando o rastreador
Projetando e implementando as verificações de segurança
-
Verificação de detecção de injeção de SQL
-
Verificação de XSS (Cross-Site Scripting)
Verificação de exposição à informação sensível
Implementando a lógica principal de varredura
Estendendo o scanner de segurança
Embrulhando
Pré-requisitos
Para acompanhar este tutorial, você precisará:
Python 3.x
Compreensão básica dos protocolos HTTP
Entendimento básico de aplicativos da Web
-
Compreensão básica de como funcionam XSS, injeção de SQL e ataques básicos de segurança
Configurando nosso ambiente de desenvolvimento
Vamos instalar nossas dependências necessárias com o seguinte comando:
pip install requests beautifulsoup4 urllib3 colorama
Usaremos essas dependências em nosso arquivo de código:
# Required packages
import requests
from bs4 import BeautifulSoup
import urllib.parse
import colorama
import re
from concurrent.futures import ThreadPoolExecutor
import sys
from typing import List, Dict, Set
Construindo nossa classe de scanner central
Depois de ter as dependências, é hora de escrever a classe principal do scanner.
Esta classe servirá como nossa classe principal que lidará com a funcionalidade de digitalização de segurança da Web. Ele rastreará nossas páginas visitadas e também armazenará nossas descobertas.
Temos a função normalize_url
que usaremos para garantir que você não rescane URLs que já foram vistos antes. Esta função removerá essencialmente os parâmetros HTTP do URL. Por exemplo, https://example.com/page?id=1
se tornará https://example.com/page
depois de normalizá -lo.
class WebSecurityScanner:
def __init__(self, target_url: str, max_depth: int = 3):
"""
Initialize the security scanner with a target URL and maximum crawl depth.
Args:
target_url: The base URL to scan
max_depth: Maximum depth for crawling links (default: 3)
"""
self.target_url = target_url
self.max_depth = max_depth
self.visited_urls: Set[str] = set()
self.vulnerabilities: List[Dict] = []
self.session = requests.Session()
# Initialize colorama for cross-platform colored output
colorama.init()
def normalize_url(self, url: str) -> str:
"""Normalize the URL to prevent duplicate checks"""
parsed = urllib.parse.urlparse(url)
return f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
Implementando o rastreador
O primeiro passo em nosso scanner é implementar um rastreador da Web que descobrirá páginas e URLs em um determinado aplicativo de destino. Verifique se você está escrevendo essas funções em nossa classe WebSecurityScanner
.
def crawl(self, url: str, depth: int = 0) -> None:
"""
Crawl the website to discover pages and endpoints.
Args:
url: Current URL to crawl
depth: Current depth in the crawl tree
"""
if depth > self.max_depth or url in self.visited_urls:
return
try:
self.visited_urls.add(url)
response = self.session.get(url, verify=False)
soup = BeautifulSoup(response.text, 'html.parser')
# Find all links in the page
links = soup.find_all('a', href=True)
for link in links:
next_url = urllib.parse.urljoin(url, link['href'])
if next_url.startswith(self.target_url):
self.crawl(next_url, depth + 1)
except Exception as e:
print(f"Error crawling {url}: {str(e)}")
Esta função de rastreamento
nos ajuda a realizar um rastreamento profundo de um site. Ele explorará todas as páginas de um site enquanto permanece no domínio especificado.
Por exemplo, se você planeja usar esse scanner em https://google.com
, a função primeiro receberá todos os URLs e depois uma verificação de um por um se eles pertencem ao domínio especificado ( isto é, google.com
). Nesse caso, ele continuará a digitalizar o URL visto até uma profundidade especificada que é fornecida com o parâmetro de profundidade como um argumento para a função. Também temos algum manuseio de exceção para garantir que lidamos com erros sem problemas e relatamos erros durante o rastreamento.
Projetando e implementando as verificações de segurança
Agora vamos finalmente chegar à parte suculenta e implementar nossas verificações de segurança. Começaremos primeiro com a injeção de SQL.
Verificação de detecção de injeção SQL
def check_sql_injection(self, url: str) -> None:
"""Test for potential SQL injection vulnerabilities"""
sql_payloads = ["'", "1' OR '1'='1", "' OR 1=1--", "' UNION SELECT NULL--"]
for payload in sql_payloads:
try:
# Test GET parameters
parsed = urllib.parse.urlparse(url)
params = urllib.parse.parse_qs(parsed.query)
for param in params:
test_url = url.replace(f"{param}={params[param][0]}",
f"{param}={payload}")
response = self.session.get(test_url)
# Look for SQL error messages
if any(error in response.text.lower() for error in
['sql', 'mysql', 'sqlite', 'postgresql', 'oracle']):
self.report_vulnerability({
'type': 'SQL Injection',
'url': url,
'parameter': param,
'payload': payload
})
except Exception as e:
print(f"Error testing SQL injection on {url}: {str(e)}")
Essa função executa essencialmente as verificações básicas de injeção de SQL testando o URL em relação às cargas úteis comuns de injeção de SQL e procurando mensagens de erro que possam sugerir uma vulnerabilidade de segurança.
Com base na mensagem de erro recebida após executar uma solicitação GET simples na URL, verificamos se essa mensagem é um erro de banco de dados ou não. Se for, usamos a função relation_vulnerability
para relatar isso como um problema de segurança em nosso relatório final, esse script gerará. Para o bem deste exemplo, estamos selecionando algumas cargas úteis de injeção de SQL comumente testadas, mas você pode estendê -lo para testar ainda mais.
Verificação de XSS (Cross-Site Scripting)
Agora vamos implementar a segunda verificação de segurança para cargas úteis do XSS.
def check_xss(self, url: str) -> None:
"""Test for potential Cross-Site Scripting vulnerabilities"""
xss_payloads = [
"<script>alert('XSS')</script>",
"<img src=x onerror=alert('XSS')>",
"javascript:alert('XSS')"
]
for payload in xss_payloads:
try:
# Test GET parameters
parsed = urllib.parse.urlparse(url)
params = urllib.parse.parse_qs(parsed.query)
for param in params:
test_url = url.replace(f"{param}={params[param][0]}",
f"{param}={urllib.parse.quote(payload)}")
response = self.session.get(test_url)
if payload in response.text:
self.report_vulnerability({
'type': 'Cross-Site Scripting (XSS)',
'url': url,
'parameter': param,
'payload': payload
})
except Exception as e:
print(f"Error testing XSS on {url}: {str(e)}")
Esta função, assim como o testador de injeção SQL, usa um conjunto de cargas XSS comuns e aplica a mesma ideia. Mas a principal diferença aqui é que estamos procurando que nossa carga injetada apareça inalterada em nossa resposta, em vez de procurar uma mensagem de erro.
Se você conseguir ver nossa carga injetada, provavelmente ela será executada no contexto do navegador da vítima como um ataque XSS refletido.
Verificação de exposição à informação sensível
Agora vamos implementar nossa verificação final para PII sensível.
def check_sensitive_info(self, url: str) -> None:
"""Check for exposed sensitive information"""
sensitive_patterns = {
'email': r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
'phone': r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b',
'ssn': r'\b\d{3}-\d{2}-\d{4}\b',
'api_key': r'api[_-]?key[_-]?([\'"|`])([a-zA-Z0-9]{32,45})\1'
}
try:
response = self.session.get(url)
for info_type, pattern in sensitive_patterns.items():
matches = re.finditer(pattern, response.text)
for match in matches:
self.report_vulnerability({
'type': 'Sensitive Information Exposure',
'url': url,
'info_type': info_type,
'pattern': pattern
})
except Exception as e:
print(f"Error checking sensitive information on {url}: {str(e)}")
Esta função usa um conjunto de padrões regex predefinidos para procurar PII, como e-mails, números de telefone, SSNs e chaves de API (que são prefixadas com API-KEY-
Assim como as duas funções anteriores, usamos o texto de resposta para o URL e nossos padrões regex para encontrar esses PIIs no texto da resposta. Se encontrarmos algum, nós os relatamos com a função relation_vulnerability
. Certifique -se de ter todas essas funções definidas na classe webcurityScanner
.
Implementando a Lógica de Varredura Principal
Vamos finalmente juntar tudo definindo as funções scan
e report_vulnerability
na classe WebSecurityScanner
:
def scan(self) -> List[Dict]:
"""
Main scanning method that coordinates the security checks
Returns:
List of discovered vulnerabilities
"""
print(f"\n{colorama.Fore.BLUE}Starting security scan of {self.target_url}{colorama.Style.RESET_ALL}\n")
# First, crawl the website
self.crawl(self.target_url)
# Then run security checks on all discovered URLs
with ThreadPoolExecutor(max_workers=5) as executor:
for url in self.visited_urls:
executor.submit(self.check_sql_injection, url)
executor.submit(self.check_xss, url)
executor.submit(self.check_sensitive_info, url)
return self.vulnerabilities
def report_vulnerability(self, vulnerability: Dict) -> None:
"""Record and display found vulnerabilities"""
self.vulnerabilities.append(vulnerability)
print(f"{colorama.Fore.RED}[VULNERABILITY FOUND]{colorama.Style.RESET_ALL}")
for key, value in vulnerability.items():
print(f"{key}: {value}")
print()
Este código define nossa função scan
que essencialmente invocará a função crawl
e começará a rastrear recursivamente o site. Com multithreading, aplicaremos todas as três verificações de segurança nas URLs visitadas.
Também definimos a função relation_vulnerability
, que imprimirá efetivamente nossa vulnerabilidade ao console e também os armazenará em nossa matriz vulnerabilidades .
Agora vamos finalmente usar nosso scanner salvando-o como scanner.py
:
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python scanner.py <target_url>")
sys.exit(1)
target_url = sys.argv[1]
scanner = WebSecurityScanner(target_url)
vulnerabilities = scanner.scan()
# Print summary
print(f"\n{colorama.Fore.GREEN}Scan Complete!{colorama.Style.RESET_ALL}")
print(f"Total URLs scanned: {len(scanner.visited_urls)}")
print(f"Vulnerabilities found: {len(vulnerabilities)}")
A URL de destino será fornecida como um argumento do sistema e obteremos o resumo das URLs verificadas e das vulnerabilidades encontradas no final da nossa verificação. Agora vamos discutir como você pode estender o scanner e adicionar mais recursos.
Estendendo o scanner de segurança
Aqui estão algumas idéias para estender esse scanner de segurança básico em algo ainda mais avançado:
Adicione mais verificações de vulnerabilidade, como detecção de CSRF, passagem de diretório e assim por diante.
Melhore os relatórios com uma saída HTML ou PDF.
Adicione opções de configuração para a intensidade e o escopo da verificação da pesquisa (especificando a profundidade das varreduras por meio de um argumento da CLI).
Implementando limitação de taxa adequada.
Adicionando suporte de autenticação para testar URLs que requerem autenticação baseada em sessão.
Embrulhando
Agora você sabe como construir um scanner de segurança básico! Este scanner demonstra alguns conceitos básicos de segurança na Web.
Lembre -se de que este tutorial deve ser usado apenas para fins educacionais. Existem vários aplicativos de nível corporativo projetados profissionalmente, como o Burp Suite e o OWASP ZAP, que podem verificar se há centenas de vulnerabilidades de segurança em uma escala muito maior.
Espero que você tenha aprendido o básico de segurança na web e também um pouco de programação Python.