feat:removed manual timezone management, delegate timezone configuration to execution environment
removed code that manages timezone setup from config file, krawl now obeys to the environment configuration
This commit is contained in:
@@ -43,12 +43,12 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if forwarded_for:
|
||||
# X-Forwarded-For can contain multiple IPs, get the first (original client)
|
||||
return forwarded_for.split(',')[0].strip()
|
||||
|
||||
|
||||
# Check X-Real-IP header (set by nginx and other proxies)
|
||||
real_ip = self.headers.get('X-Real-IP')
|
||||
if real_ip:
|
||||
return real_ip.strip()
|
||||
|
||||
|
||||
# Fallback to direct connection IP
|
||||
return self.client_address[0]
|
||||
|
||||
@@ -73,12 +73,12 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if not error_codes:
|
||||
error_codes = [400, 401, 403, 404, 500, 502, 503]
|
||||
return random.choice(error_codes)
|
||||
|
||||
|
||||
def _parse_query_string(self) -> str:
|
||||
"""Extract query string from the request path"""
|
||||
parsed = urlparse(self.path)
|
||||
return parsed.query
|
||||
|
||||
|
||||
def _handle_sql_endpoint(self, path: str) -> bool:
|
||||
"""
|
||||
Handle SQL injection honeypot endpoints.
|
||||
@@ -86,22 +86,22 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"""
|
||||
# SQL-vulnerable endpoints
|
||||
sql_endpoints = ['/api/search', '/api/sql', '/api/database']
|
||||
|
||||
|
||||
base_path = urlparse(path).path
|
||||
if base_path not in sql_endpoints:
|
||||
return False
|
||||
|
||||
|
||||
try:
|
||||
# Get query parameters
|
||||
query_string = self._parse_query_string()
|
||||
|
||||
|
||||
# Log SQL injection attempt
|
||||
client_ip = self._get_client_ip()
|
||||
user_agent = self._get_user_agent()
|
||||
|
||||
|
||||
# Always check for SQL injection patterns
|
||||
error_msg, content_type, status_code = generate_sql_error_response(query_string or "")
|
||||
|
||||
|
||||
if error_msg:
|
||||
# SQL injection detected - log and return error
|
||||
self.access_logger.warning(f"[SQL INJECTION DETECTED] {client_ip} - {base_path} - Query: {query_string[:100] if query_string else 'empty'}")
|
||||
@@ -117,9 +117,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
response_data = get_sql_response_with_data(base_path, query_string or "")
|
||||
self.wfile.write(response_data.encode())
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
except BrokenPipeError:
|
||||
# Client disconnected
|
||||
return True
|
||||
@@ -142,7 +142,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
# Build the content HTML
|
||||
content = ""
|
||||
|
||||
|
||||
# Add canary token if needed
|
||||
if Handler.counter <= 0 and self.config.canary_token_url:
|
||||
content += f"""
|
||||
@@ -189,16 +189,16 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
from urllib.parse import urlparse
|
||||
base_path = urlparse(self.path).path
|
||||
|
||||
|
||||
if base_path in ['/api/search', '/api/sql', '/api/database']:
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
if content_length > 0:
|
||||
post_data = self.rfile.read(content_length).decode('utf-8', errors="replace")
|
||||
|
||||
|
||||
self.access_logger.info(f"[SQL ENDPOINT POST] {client_ip} - {base_path} - Data: {post_data[:100] if post_data else 'empty'}")
|
||||
|
||||
|
||||
error_msg, content_type, status_code = generate_sql_error_response(post_data)
|
||||
|
||||
|
||||
try:
|
||||
if error_msg:
|
||||
self.access_logger.warning(f"[SQL INJECTION DETECTED POST] {client_ip} - {base_path}")
|
||||
@@ -217,26 +217,26 @@ class Handler(BaseHTTPRequestHandler):
|
||||
except Exception as e:
|
||||
self.app_logger.error(f"Error in SQL POST handler: {str(e)}")
|
||||
return
|
||||
|
||||
|
||||
if base_path == '/api/contact':
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
if content_length > 0:
|
||||
post_data = self.rfile.read(content_length).decode('utf-8', errors="replace")
|
||||
|
||||
|
||||
parsed_data = {}
|
||||
for pair in post_data.split('&'):
|
||||
if '=' in pair:
|
||||
key, value = pair.split('=', 1)
|
||||
from urllib.parse import unquote_plus
|
||||
parsed_data[unquote_plus(key)] = unquote_plus(value)
|
||||
|
||||
|
||||
xss_detected = any(detect_xss_pattern(v) for v in parsed_data.values())
|
||||
|
||||
|
||||
if xss_detected:
|
||||
self.access_logger.warning(f"[XSS ATTEMPT DETECTED] {client_ip} - {base_path} - Data: {post_data[:200]}")
|
||||
else:
|
||||
self.access_logger.info(f"[XSS ENDPOINT POST] {client_ip} - {base_path}")
|
||||
|
||||
|
||||
try:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
@@ -264,17 +264,17 @@ class Handler(BaseHTTPRequestHandler):
|
||||
timestamp = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
credential_line = f"{timestamp}|{client_ip}|{username or 'N/A'}|{password or 'N/A'}|{self.path}"
|
||||
self.credential_logger.info(credential_line)
|
||||
|
||||
|
||||
# Also record in tracker for dashboard
|
||||
self.tracker.record_credential_attempt(client_ip, self.path, username or 'N/A', password or 'N/A')
|
||||
|
||||
|
||||
self.access_logger.warning(f"[CREDENTIALS CAPTURED] {client_ip} - Username: {username or 'N/A'} - Path: {self.path}")
|
||||
|
||||
# send the post data (body) to the record_access function so the post data can be used to detect suspicious things.
|
||||
self.tracker.record_access(client_ip, self.path, user_agent, post_data, method='POST')
|
||||
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
try:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
@@ -289,11 +289,11 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
def serve_special_path(self, path: str) -> bool:
|
||||
"""Serve special paths like robots.txt, API endpoints, etc."""
|
||||
|
||||
|
||||
# Check SQL injection honeypot endpoints first
|
||||
if self._handle_sql_endpoint(path):
|
||||
return True
|
||||
|
||||
|
||||
try:
|
||||
if path == '/robots.txt':
|
||||
self.send_response(200)
|
||||
@@ -301,7 +301,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.robots_txt().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/credentials.txt', '/passwords.txt', '/admin_notes.txt']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/plain')
|
||||
@@ -311,7 +311,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
else:
|
||||
self.wfile.write(passwords_txt().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/users.json', '/api_keys.json', '/config.json']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
@@ -323,28 +323,28 @@ class Handler(BaseHTTPRequestHandler):
|
||||
else:
|
||||
self.wfile.write(api_response('/api/config').encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/admin', '/admin/', '/admin/login', '/login']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.login_form().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/users', '/user', '/database', '/db', '/search']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.product_search().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/info', '/input', '/contact', '/feedback', '/comment']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.input_form().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path == '/server':
|
||||
error_html, content_type = generate_server_error()
|
||||
self.send_response(500)
|
||||
@@ -352,35 +352,35 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(error_html.encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/wp-login.php', '/wp-login', '/wp-admin', '/wp-admin/']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.wp_login().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/wp-content/', '/wp-includes/'] or 'wordpress' in path.lower():
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.wordpress().encode())
|
||||
return True
|
||||
|
||||
|
||||
if 'phpmyadmin' in path.lower() or path in ['/pma/', '/phpMyAdmin/']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html_templates.phpmyadmin().encode())
|
||||
return True
|
||||
|
||||
|
||||
if path.startswith('/api/') or path.startswith('/api') or path in ['/.env']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(api_response(path).encode())
|
||||
return True
|
||||
|
||||
|
||||
if path in ['/backup/', '/uploads/', '/private/', '/admin/', '/config/', '/database/']:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
@@ -400,22 +400,21 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"""Responds to webpage requests"""
|
||||
client_ip = self._get_client_ip()
|
||||
user_agent = self._get_user_agent()
|
||||
|
||||
|
||||
if self.config.dashboard_secret_path and self.path == self.config.dashboard_secret_path:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
try:
|
||||
stats = self.tracker.get_stats()
|
||||
timezone = str(self.config.timezone) if self.config.timezone else 'UTC'
|
||||
dashboard_path = self.config.dashboard_secret_path
|
||||
self.wfile.write(generate_dashboard(stats, timezone, dashboard_path).encode())
|
||||
self.wfile.write(generate_dashboard(stats, dashboard_path).encode())
|
||||
except BrokenPipeError:
|
||||
pass
|
||||
except Exception as e:
|
||||
self.app_logger.error(f"Error generating dashboard: {e}")
|
||||
return
|
||||
|
||||
|
||||
# API endpoint for fetching IP stats
|
||||
if self.config.dashboard_secret_path and self.path.startswith(f"{self.config.dashboard_secret_path}/api/ip-stats/"):
|
||||
ip_address = self.path.replace(f"{self.config.dashboard_secret_path}/api/ip-stats/", "")
|
||||
@@ -473,7 +472,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return
|
||||
|
||||
self.tracker.record_access(client_ip, self.path, user_agent, method='GET')
|
||||
|
||||
|
||||
# self.analyzer.infer_user_category(client_ip)
|
||||
# self.analyzer.update_ip_rep_infos(client_ip)
|
||||
|
||||
@@ -497,9 +496,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
try:
|
||||
self.wfile.write(self.generate_page(self.path).encode())
|
||||
|
||||
|
||||
Handler.counter -= 1
|
||||
|
||||
|
||||
if Handler.counter < 0:
|
||||
Handler.counter = self.config.canary_token_tries
|
||||
except BrokenPipeError:
|
||||
|
||||
Reference in New Issue
Block a user