mdbq 4.2.23__tar.gz → 4.2.24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mdbq might be problematic. Click here for more details.

Files changed (46) hide show
  1. {mdbq-4.2.23 → mdbq-4.2.24}/PKG-INFO +1 -1
  2. mdbq-4.2.24/mdbq/__version__.py +1 -0
  3. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/route/monitor.py +178 -5
  4. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq.egg-info/PKG-INFO +1 -1
  5. mdbq-4.2.23/mdbq/__version__.py +0 -1
  6. {mdbq-4.2.23 → mdbq-4.2.24}/README.txt +0 -0
  7. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/__init__.py +0 -0
  8. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/auth/__init__.py +0 -0
  9. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/auth/auth_backend.py +0 -0
  10. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/auth/crypto.py +0 -0
  11. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/auth/rate_limiter.py +0 -0
  12. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/js/__init__.py +0 -0
  13. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/js/jc.py +0 -0
  14. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/log/__init__.py +0 -0
  15. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/log/mylogger.py +0 -0
  16. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/myconf/__init__.py +0 -0
  17. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/myconf/myconf.py +0 -0
  18. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/mysql/__init__.py +0 -0
  19. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/mysql/deduplicator.py +0 -0
  20. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/mysql/mysql.py +0 -0
  21. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/mysql/s_query.py +0 -0
  22. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/mysql/unique_.py +0 -0
  23. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/mysql/uploader.py +0 -0
  24. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/other/__init__.py +0 -0
  25. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/other/download_sku_picture.py +0 -0
  26. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/other/error_handler.py +0 -0
  27. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/other/otk.py +0 -0
  28. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/other/pov_city.py +0 -0
  29. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/other/ua_sj.py +0 -0
  30. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/pbix/__init__.py +0 -0
  31. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/pbix/pbix_refresh.py +0 -0
  32. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/pbix/refresh_all.py +0 -0
  33. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/redis/__init__.py +0 -0
  34. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/redis/getredis.py +0 -0
  35. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/redis/redis_cache.py +0 -0
  36. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/route/__init__.py +0 -0
  37. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/route/analytics.py +0 -0
  38. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/route/routes.py +0 -0
  39. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/selenium/__init__.py +0 -0
  40. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/selenium/get_driver.py +0 -0
  41. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq/spider/__init__.py +0 -0
  42. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq.egg-info/SOURCES.txt +0 -0
  43. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq.egg-info/dependency_links.txt +0 -0
  44. {mdbq-4.2.23 → mdbq-4.2.24}/mdbq.egg-info/top_level.txt +0 -0
  45. {mdbq-4.2.23 → mdbq-4.2.24}/setup.cfg +0 -0
  46. {mdbq-4.2.23 → mdbq-4.2.24}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mdbq
3
- Version: 4.2.23
3
+ Version: 4.2.24
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -0,0 +1 @@
1
+ VERSION = '4.2.24'
@@ -21,7 +21,7 @@ import functools
21
21
  import hashlib
22
22
  import socket
23
23
  from datetime import datetime, timedelta
24
- from typing import Dict, Any, Optional
24
+ from typing import Dict, Any, Optional, List
25
25
  from dbutils.pooled_db import PooledDB # type: ignore
26
26
  from flask import request, g
27
27
 
@@ -437,6 +437,138 @@ class RouteMonitor:
437
437
  except Exception:
438
438
  return None
439
439
 
440
+ def calculate_risk_score(self, client_ip: str, date: datetime.date) -> int:
441
+ """
442
+ 计算 IP 风险评分(0-100)
443
+
444
+ 风险评分基于以下维度:
445
+ 1. 请求频率异常(30分):单日请求数超过阈值
446
+ 2. 失败率异常(25分):失败请求比例过高
447
+ 3. 访问模式异常(20分):短时间访问大量不同接口
448
+ 4. 时间分布异常(15分):非正常时间段高频访问
449
+ 5. 响应时间异常(10分):平均响应时间过长(可能是攻击)
450
+
451
+ Args:
452
+ client_ip: 客户端 IP 地址
453
+ date: 统计日期
454
+
455
+ Returns:
456
+ int: 风险评分(0-100),分数越高风险越大
457
+ """
458
+ risk_score = 0
459
+
460
+ try:
461
+ connection = self.pool.connection()
462
+ try:
463
+ with connection.cursor() as cursor:
464
+ self.ensure_database_context(cursor)
465
+
466
+ # 获取该 IP 当日的统计数据
467
+ cursor.execute("""
468
+ SELECT
469
+ `请求总数`, `成功次数`, `失败次数`,
470
+ `平均耗时`, `首次访问`, `最后访问`, `访问接口数`
471
+ FROM `api_ip记录`
472
+ WHERE `客户端ip` = %s AND `统计日期` = %s
473
+ """, (client_ip, date))
474
+
475
+ ip_stats = cursor.fetchone()
476
+ if not ip_stats:
477
+ return 0 # 无数据,无风险
478
+
479
+ total_requests = ip_stats['请求总数']
480
+ success_count = ip_stats['成功次数']
481
+ failure_count = ip_stats['失败次数']
482
+ avg_time = float(ip_stats['平均耗时']) if ip_stats['平均耗时'] else 0
483
+ first_access = ip_stats['首次访问']
484
+ last_access = ip_stats['最后访问']
485
+ endpoint_count = ip_stats['访问接口数'] or 0
486
+
487
+ # 1. 请求频率异常检测(30分)
488
+ # 阈值:正常用户单日请求 < 1000,可疑 1000-5000,高风险 > 5000
489
+ if total_requests > 10000:
490
+ risk_score += 30 # 极高频率
491
+ elif total_requests > 5000:
492
+ risk_score += 25 # 高频率
493
+ elif total_requests > 2000:
494
+ risk_score += 18 # 中等频率
495
+ elif total_requests > 1000:
496
+ risk_score += 10 # 略高
497
+
498
+ # 2. 失败率异常检测(25分)
499
+ # 高失败率可能是暴力破解、SQL注入等攻击
500
+ if total_requests > 0:
501
+ failure_rate = failure_count / total_requests
502
+ if failure_rate > 0.8:
503
+ risk_score += 25 # 80%以上失败
504
+ elif failure_rate > 0.5:
505
+ risk_score += 20 # 50%-80%失败
506
+ elif failure_rate > 0.3:
507
+ risk_score += 12 # 30%-50%失败
508
+ elif failure_rate > 0.15:
509
+ risk_score += 5 # 15%-30%失败
510
+
511
+ # 3. 访问模式异常检测(20分)
512
+ # 短时间访问大量不同接口可能是扫描行为
513
+ if endpoint_count > 0:
514
+ # 计算平均每个接口的访问次数
515
+ avg_per_endpoint = total_requests / endpoint_count
516
+
517
+ if endpoint_count > 50 and avg_per_endpoint < 5:
518
+ risk_score += 20 # 访问50+接口,每个接口少于5次(扫描特征)
519
+ elif endpoint_count > 30 and avg_per_endpoint < 10:
520
+ risk_score += 15 # 访问30+接口
521
+ elif endpoint_count > 20 and avg_per_endpoint < 15:
522
+ risk_score += 10 # 访问20+接口
523
+ elif endpoint_count > 10 and avg_per_endpoint < 20:
524
+ risk_score += 5 # 访问10+接口
525
+
526
+ # 4. 时间分布异常检测(15分)
527
+ # 查询该 IP 的小时分布,检测是否有非正常时间段高频访问
528
+ cursor.execute("""
529
+ SELECT
530
+ HOUR(`请求时间`) as hour,
531
+ COUNT(*) as count
532
+ FROM `api_访问日志`
533
+ WHERE `客户端ip` = %s
534
+ AND DATE(`请求时间`) = %s
535
+ GROUP BY HOUR(`请求时间`)
536
+ HAVING count > 100
537
+ ORDER BY count DESC
538
+ """, (client_ip, date))
539
+
540
+ hourly_distribution = cursor.fetchall()
541
+
542
+ # 检测是否有凌晨时段(0-5点)的异常高频访问
543
+ night_requests = sum(h['count'] for h in hourly_distribution if 0 <= h['hour'] <= 5)
544
+ if night_requests > 500:
545
+ risk_score += 15 # 凌晨大量请求(可能是爬虫/攻击)
546
+ elif night_requests > 200:
547
+ risk_score += 10
548
+ elif night_requests > 100:
549
+ risk_score += 5
550
+
551
+ # 5. 响应时间异常检测(10分)
552
+ # 过长的响应时间可能是 DDoS 攻击或资源耗尽攻击
553
+ if avg_time > 5000: # > 5秒
554
+ risk_score += 10
555
+ elif avg_time > 3000: # > 3秒
556
+ risk_score += 7
557
+ elif avg_time > 2000: # > 2秒
558
+ risk_score += 4
559
+
560
+ # 限制评分范围在 0-100
561
+ risk_score = min(100, max(0, risk_score))
562
+
563
+ finally:
564
+ connection.close()
565
+
566
+ except Exception as e:
567
+ # 计算失败时返回 0(保守策略)
568
+ return 0
569
+
570
+ return risk_score
571
+
440
572
  # ==================== 核心数据收集 ====================
441
573
 
442
574
  def collect_request_data(self, request) -> Dict[str, Any]:
@@ -685,6 +817,9 @@ class RouteMonitor:
685
817
  ))
686
818
 
687
819
  # 2. 更新 IP 统计表
820
+ client_ip = request_data.get('客户端ip', '')
821
+
822
+ # 首先更新基础统计
688
823
  cursor.execute("""
689
824
  INSERT INTO `api_ip记录` (
690
825
  `统计日期`, `客户端ip`, `请求总数`, `成功次数`, `失败次数`,
@@ -695,13 +830,13 @@ class RouteMonitor:
695
830
  `请求总数` = `请求总数` + 1,
696
831
  `成功次数` = `成功次数` + %s,
697
832
  `失败次数` = `失败次数` + %s,
698
- `平均耗时` = (
699
- (`平均耗时` * `请求总数` + %s) / (`请求总数` + 1)
700
- ),
833
+ `平均耗时` = (
834
+ (`平均耗时` * `请求总数` + %s) / (`请求总数` + 1)
835
+ ),
701
836
  `最后访问` = %s
702
837
  """, (
703
838
  date,
704
- request_data.get('客户端ip', ''),
839
+ client_ip,
705
840
  is_success, is_error,
706
841
  response_time, now, now,
707
842
  is_success, is_error,
@@ -709,8 +844,46 @@ class RouteMonitor:
709
844
  now
710
845
  ))
711
846
 
847
+ # 计算该 IP 访问的不同接口数量
848
+ cursor.execute("""
849
+ SELECT COUNT(DISTINCT `路由地址`) as endpoint_count
850
+ FROM `api_访问日志`
851
+ WHERE `客户端ip` = %s AND DATE(`请求时间`) = %s
852
+ """, (client_ip, date))
853
+
854
+ endpoint_result = cursor.fetchone()
855
+ endpoint_count = endpoint_result['endpoint_count'] if endpoint_result else 0
856
+
857
+ # 更新访问接口数
858
+ cursor.execute("""
859
+ UPDATE `api_ip记录`
860
+ SET `访问接口数` = %s
861
+ WHERE `客户端ip` = %s AND `统计日期` = %s
862
+ """, (endpoint_count, client_ip, date))
863
+
712
864
  connection.commit()
713
865
 
866
+ # 3. 计算并更新风险评分(在事务外执行,避免影响主流程性能)
867
+ try:
868
+ risk_score = self.calculate_risk_score(client_ip, date)
869
+ if risk_score > 0:
870
+ # 重新获取连接更新风险评分
871
+ connection2 = self.pool.connection()
872
+ try:
873
+ with connection2.cursor() as cursor2:
874
+ self.ensure_database_context(cursor2)
875
+ cursor2.execute("""
876
+ UPDATE `api_ip记录`
877
+ SET `风险评分` = %s
878
+ WHERE `客户端ip` = %s AND `统计日期` = %s
879
+ """, (risk_score, client_ip, date))
880
+ connection2.commit()
881
+ finally:
882
+ connection2.close()
883
+ except Exception:
884
+ # 风险评分计算失败不影响主流程
885
+ pass
886
+
714
887
  finally:
715
888
  connection.close()
716
889
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mdbq
3
- Version: 4.2.23
3
+ Version: 4.2.24
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -1 +0,0 @@
1
- VERSION = '4.2.23'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes