andito HF Staff commited on
Commit
a21aca9
·
verified ·
1 Parent(s): bd5462b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -12
app.py CHANGED
@@ -190,8 +190,10 @@ def _flush_request_log():
190
  except Exception as e:
191
  print(f"Warning: Failed to flush request log: {e}")
192
 
193
- def _get_recent_requests_from_log(ip_address: str) -> list:
194
- """Get recent requests for an IP from the log file (with caching)"""
 
 
195
  now_time = time.time()
196
  now_dt = datetime.now(timezone.utc)
197
 
@@ -210,11 +212,14 @@ def _get_recent_requests_from_log(ip_address: str) -> list:
210
  _request_log_cache["data"] = log_data
211
  _request_log_cache["ts"] = now_time
212
  except Exception:
213
- # If log doesn't exist or there's an error, return empty list
214
- return []
215
 
216
  # Filter for this IP and recent requests
217
  recent_timestamps = []
 
 
 
218
  for row in log_data:
219
  if row.get('ip_address') == ip_address:
220
  try:
@@ -224,18 +229,22 @@ def _get_recent_requests_from_log(ip_address: str) -> list:
224
  # Only consider requests from the last hour
225
  if age_seconds < 3600:
226
  recent_timestamps.append(age_seconds)
 
 
 
 
227
  except (ValueError, KeyError):
228
  continue
229
 
230
- return recent_timestamps
231
 
232
- def _check_suspicious_activity(ip_address: str, order_number: str) -> list:
233
  """Check for suspicious patterns and return list of flags"""
234
  flags = []
235
  now_dt = datetime.now(timezone.utc)
236
 
237
  # Get recent requests from the persistent log
238
- recent_request_ages = _get_recent_requests_from_log(ip_address)
239
 
240
  # Also check the buffer for requests not yet written to the log
241
  for entry in _request_log_buffer:
@@ -245,6 +254,9 @@ def _check_suspicious_activity(ip_address: str, order_number: str) -> list:
245
  age_seconds = (now_dt - timestamp).total_seconds()
246
  if age_seconds < 3600:
247
  recent_request_ages.append(age_seconds)
 
 
 
248
  except (ValueError, KeyError):
249
  continue
250
 
@@ -255,16 +267,35 @@ def _check_suspicious_activity(ip_address: str, order_number: str) -> list:
255
  if total_requests > 20:
256
  flags.append("HIGH_FREQUENCY")
257
 
258
- # Flag: More than 5 requests in the last minute
259
  last_minute = sum(1 for age in recent_request_ages if age < 60) + 1
260
- if last_minute > 5:
261
  flags.append("RAPID_REQUESTS")
262
 
263
- # Flag: More than 10 requests in the last 5 minutes
264
  last_5_min = sum(1 for age in recent_request_ages if age < 300) + 1
265
- if last_5_min > 10:
266
  flags.append("BURST_PATTERN")
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  return flags
269
 
270
  # -------------------------
@@ -289,7 +320,16 @@ def claim_c_key(
289
  order_number = order_number.strip()
290
 
291
  # Check for suspicious activity patterns
292
- suspicious_flags = _check_suspicious_activity(ip_address, order_number)
 
 
 
 
 
 
 
 
 
293
 
294
  if not order_number:
295
  _log_request(order_number, ip_address, user_agent, False, "Empty order number", suspicious_flags)
@@ -417,3 +457,4 @@ with gr.Blocks(title="API") as demo:
417
  demo.queue()
418
  demo.launch()
419
 
 
 
190
  except Exception as e:
191
  print(f"Warning: Failed to flush request log: {e}")
192
 
193
+ def _get_recent_requests_from_log(ip_address: str) -> tuple:
194
+ """Get recent requests for an IP from the log file (with caching)
195
+ Returns: (list of ages in seconds, set of user_agents, set of order_numbers)
196
+ """
197
  now_time = time.time()
198
  now_dt = datetime.now(timezone.utc)
199
 
 
212
  _request_log_cache["data"] = log_data
213
  _request_log_cache["ts"] = now_time
214
  except Exception:
215
+ # If log doesn't exist or there's an error, return empty data
216
+ return [], set(), set()
217
 
218
  # Filter for this IP and recent requests
219
  recent_timestamps = []
220
+ user_agents = set()
221
+ order_numbers = set()
222
+
223
  for row in log_data:
224
  if row.get('ip_address') == ip_address:
225
  try:
 
229
  # Only consider requests from the last hour
230
  if age_seconds < 3600:
231
  recent_timestamps.append(age_seconds)
232
+ if row.get('user_agent'):
233
+ user_agents.add(row['user_agent'])
234
+ if row.get('order_number'):
235
+ order_numbers.add(row['order_number'])
236
  except (ValueError, KeyError):
237
  continue
238
 
239
+ return recent_timestamps, user_agents, order_numbers
240
 
241
+ def _check_suspicious_activity(ip_address: str, order_number: str, user_agent: str = "unknown") -> list:
242
  """Check for suspicious patterns and return list of flags"""
243
  flags = []
244
  now_dt = datetime.now(timezone.utc)
245
 
246
  # Get recent requests from the persistent log
247
+ recent_request_ages, user_agents_from_ip, order_numbers_from_ip = _get_recent_requests_from_log(ip_address)
248
 
249
  # Also check the buffer for requests not yet written to the log
250
  for entry in _request_log_buffer:
 
254
  age_seconds = (now_dt - timestamp).total_seconds()
255
  if age_seconds < 3600:
256
  recent_request_ages.append(age_seconds)
257
+ user_agents_from_ip.add(entry.get('user_agent', 'unknown'))
258
+ if entry.get('order_number'):
259
+ order_numbers_from_ip.add(entry['order_number'])
260
  except (ValueError, KeyError):
261
  continue
262
 
 
267
  if total_requests > 20:
268
  flags.append("HIGH_FREQUENCY")
269
 
270
+ # Flag: More than 3 requests in the last minute
271
  last_minute = sum(1 for age in recent_request_ages if age < 60) + 1
272
+ if last_minute > 3:
273
  flags.append("RAPID_REQUESTS")
274
 
275
+ # Flag: More than 5 requests in the last 5 minutes
276
  last_5_min = sum(1 for age in recent_request_ages if age < 300) + 1
277
+ if last_5_min > 5:
278
  flags.append("BURST_PATTERN")
279
 
280
+ # Flag: Suspicious user agent patterns (low false positive risk)
281
+ ua_lower = user_agent.lower()
282
+ suspicious_ua_patterns = [
283
+ 'bot', 'crawler', 'spider', 'scraper', 'curl', 'wget',
284
+ 'python-requests', 'python-urllib', 'go-http-client',
285
+ 'java/', 'okhttp', 'axios', 'node-fetch'
286
+ ]
287
+ if any(pattern in ua_lower for pattern in suspicious_ua_patterns):
288
+ flags.append("SUSPICIOUS_USER_AGENT")
289
+
290
+ # Flag: Multiple different user agents from same IP (enumeration attempt)
291
+ # Only flag if we have enough data to be confident
292
+ if len(user_agents_from_ip) >= 3 and user_agent not in user_agents_from_ip:
293
+ flags.append("MULTIPLE_USER_AGENTS")
294
+
295
+ # Flag: Trying many different order numbers from same IP
296
+ if order_number and len(order_numbers_from_ip) >= 5 and order_number not in order_numbers_from_ip:
297
+ flags.append("ORDER_ENUMERATION")
298
+
299
  return flags
300
 
301
  # -------------------------
 
320
  order_number = order_number.strip()
321
 
322
  # Check for suspicious activity patterns
323
+ suspicious_flags = _check_suspicious_activity(ip_address, order_number, user_agent)
324
+
325
+ # Apply exponential delay for suspicious requests (anti-abuse measure)
326
+ # This significantly slows down automated attacks while having minimal impact on legitimate users
327
+ # Delay formula: 2^(number_of_flags) seconds, capped at 30 seconds
328
+ # Examples: 1 flag = 2s, 2 flags = 4s, 3 flags = 8s, 4 flags = 16s, 5+ flags = 30s
329
+ if suspicious_flags:
330
+ delay_seconds = 2 ** len(suspicious_flags)
331
+ delay_seconds = min(delay_seconds, 30) # Cap at 30 seconds
332
+ time.sleep(delay_seconds)
333
 
334
  if not order_number:
335
  _log_request(order_number, ip_address, user_agent, False, "Empty order number", suspicious_flags)
 
457
  demo.queue()
458
  demo.launch()
459
 
460
+