python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python密码强度检测器

基于Python实现一个专业的密码强度检测器

作者:零日失眠者

这篇文章主要为大家详细介绍了基于Python实现一个专业的密码强度检测器,可以用于评估密码的安全性和强度,感兴趣的小伙伴可以跟随小编一起学习一下

功能介绍

这是一个专业的密码强度检测工具,用于评估密码的安全性和强度。该工具具备以下核心功能:

多维度强度评估

详细评分系统

自定义策略支持

批量检测功能

安全防护机制

场景应用

1. 系统管理员使用

2. 开发者集成

3. 安全审计

4. 个人用户

报错处理

1. 输入验证异常

try:
    password = input("请输入密码: ")
    if not password:
        raise PasswordValidationError("密码不能为空")
    if len(password) > MAX_PASSWORD_LENGTH:
        raise PasswordValidationError(f"密码长度不能超过 {MAX_PASSWORD_LENGTH} 个字符")
except PasswordValidationError as e:
    logger.error(f"密码输入验证失败: {str(e)}")
    raise PasswordSecurityError(f"输入错误: {str(e)}")

2. 文件操作异常

try:
    with open(password_file, 'r', encoding='utf-8') as f:
        passwords = f.readlines()
except FileNotFoundError:
    logger.error(f"密码文件不存在: {password_file}")
    raise PasswordSecurityError(f"文件未找到: {password_file}")
except PermissionError:
    logger.error(f"无权限访问密码文件: {password_file}")
    raise PasswordSecurityError(f"文件访问权限不足: {password_file}")

3. 内存安全异常

try:
    # 安全清除密码变量
    password = None
    del password
except Exception as e:
    logger.warning(f"内存清理失败: {str(e)}")

4. 配置文件异常

try:
    with open(config_file, 'r', encoding='utf-8') as f:
        config = json.load(f)
except json.JSONDecodeError as e:
    logger.error(f"配置文件格式错误: {str(e)}")
    raise PasswordSecurityError(f"配置文件无效: {str(e)}")
except FileNotFoundError:
    logger.warning("配置文件不存在,使用默认配置")
    config = DEFAULT_CONFIG

代码实现

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
密码强度检测器
功能:评估密码的安全性和强度
作者:Cline
版本:1.0
"""

import argparse
import sys
import json
import logging
import os
import re
import math
from datetime import datetime
from typing import Dict, List, Tuple, Optional
import hashlib
import secrets

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('password_checker.log'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)

class PasswordSecurityError(Exception):
    """密码安全异常类"""
    pass

class PasswordValidationError(Exception):
    """密码验证异常类"""
    pass

class PasswordStrengthChecker:
    def __init__(self, config: Dict[str, any] = None):
        # 默认配置
        self.default_config = {
            "min_length": 8,
            "max_length": 128,
            "require_uppercase": True,
            "require_lowercase": True,
            "require_digits": True,
            "require_special_chars": True,
            "min_special_chars": 1,
            "blacklist_words": [
                "password", "123456", "qwerty", "admin", "welcome",
                "login", "root", "guest", "user", "test"
            ],
            "keyboard_patterns": [
                "qwerty", "asdf", "zxcv", "1234", "abcd"
            ],
            "scoring_weights": {
                "length": 0.3,
                "complexity": 0.4,
                "patterns": 0.2,
                "entropy": 0.1
            }
        }
        
        # 合并配置
        self.config = {**self.default_config, **(config or {})}
        
        # 特殊字符集合
        self.special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
        
        # 检测结果统计
        self.stats = {
            "total_checked": 0,
            "strong_passwords": 0,
            "medium_passwords": 0,
            "weak_passwords": 0,
            "very_weak_passwords": 0
        }
        
    def calculate_entropy(self, password: str) -> float:
        """计算密码熵值"""
        if not password:
            return 0.0
            
        # 计算字符集大小
        charset_size = 0
        if re.search(r'[a-z]', password):
            charset_size += 26
        if re.search(r'[A-Z]', password):
            charset_size += 26
        if re.search(r'[0-9]', password):
            charset_size += 10
        if re.search(r'[^a-zA-Z0-9]', password):
            charset_size += len(self.special_chars)
            
        # 计算熵值
        if charset_size > 0:
            entropy = len(password) * math.log2(charset_size)
            return min(entropy, 100.0)  # 限制最大值
        return 0.0
        
    def check_length(self, password: str) -> Tuple[int, str]:
        """检查密码长度"""
        length = len(password)
        min_len = self.config["min_length"]
        max_len = self.config["max_length"]
        
        if length < min_len:
            return 0, f"密码长度不足,至少需要 {min_len} 个字符"
        elif length > max_len:
            return 0, f"密码长度过长,最多允许 {max_len} 个字符"
        else:
            # 长度评分(线性增长)
            score = min(100, (length / max_len) * 100)
            return int(score), f"密码长度合适 ({length} 个字符)"
            
    def check_complexity(self, password: str) -> Tuple[int, str]:
        """检查密码复杂度"""
        score = 0
        feedback = []
        
        # 检查小写字母
        if re.search(r'[a-z]', password):
            score += 25
        elif self.config["require_lowercase"]:
            feedback.append("缺少小写字母")
            
        # 检查大写字母
        if re.search(r'[A-Z]', password):
            score += 25
        elif self.config["require_uppercase"]:
            feedback.append("缺少大写字母")
            
        # 检查数字
        if re.search(r'[0-9]', password):
            score += 25
        elif self.config["require_digits"]:
            feedback.append("缺少数字")
            
        # 检查特殊字符
        special_count = len(re.findall(r'[^a-zA-Z0-9]', password))
        if special_count >= self.config["min_special_chars"]:
            score += 25
        elif self.config["require_special_chars"]:
            feedback.append(f"特殊字符不足,至少需要 {self.config['min_special_chars']} 个")
            
        if not feedback:
            feedback_msg = "字符类型齐全"
        else:
            feedback_msg = "、".join(feedback)
            
        return score, feedback_msg
        
    def check_patterns(self, password: str) -> Tuple[int, str]:
        """检查密码模式"""
        score = 100
        feedback = []
        
        # 检查黑名单词汇
        password_lower = password.lower()
        for word in self.config["blacklist_words"]:
            if word in password_lower:
                score -= 30
                feedback.append(f"包含常见弱密码词汇: {word}")
                
        # 检查键盘序列
        for pattern in self.config["keyboard_patterns"]:
            if pattern in password_lower or pattern[::-1] in password_lower:
                score -= 20
                feedback.append(f"包含键盘序列: {pattern}")
                
        # 检查重复字符
        if re.search(r'(.)\1{2,}', password):
            score -= 15
            feedback.append("包含过多重复字符")
            
        # 检查连续字符
        consecutive_count = 0
        for i in range(len(password) - 2):
            if ord(password[i+1]) == ord(password[i]) + 1 and ord(password[i+2]) == ord(password[i]) + 2:
                consecutive_count += 1
        if consecutive_count > 0:
            score -= 10 * consecutive_count
            feedback.append(f"包含 {consecutive_count} 组连续字符")
            
        # 确保分数不低于0
        score = max(0, score)
        
        if not feedback:
            feedback_msg = "未发现明显模式问题"
        else:
            feedback_msg = "、".join(feedback)
            
        return score, feedback_msg
        
    def evaluate_password_strength(self, password: str) -> Dict[str, any]:
        """评估密码强度"""
        if not password:
            raise PasswordValidationError("密码不能为空")
            
        # 基本验证
        if len(password) > self.config["max_length"]:
            raise PasswordValidationError(f"密码长度不能超过 {self.config['max_length']} 个字符")
            
        # 各项检测
        length_score, length_feedback = self.check_length(password)
        complexity_score, complexity_feedback = self.check_complexity(password)
        patterns_score, patterns_feedback = self.check_patterns(password)
        entropy_score = self.calculate_entropy(password)
        
        # 计算综合得分
        weights = self.config["scoring_weights"]
        total_score = (
            length_score * weights["length"] +
            complexity_score * weights["complexity"] +
            patterns_score * weights["patterns"] +
            entropy_score * weights["entropy"]
        )
        
        # 确定强度等级
        if total_score >= 80:
            strength_level = "very_strong"
            strength_desc = "非常强"
        elif total_score >= 60:
            strength_level = "strong"
            strength_desc = "强"
        elif total_score >= 40:
            strength_level = "medium"
            strength_desc = "中等"
        else:
            strength_level = "weak"
            strength_desc = "弱"
            
        # 更新统计
        self.stats["total_checked"] += 1
        if strength_level == "very_strong":
            self.stats["strong_passwords"] += 1
        elif strength_level == "strong":
            self.stats["strong_passwords"] += 1
        elif strength_level == "medium":
            self.stats["medium_passwords"] += 1
        else:
            self.stats["weak_passwords"] += 1
            
        result = {
            "password": self.mask_password(password),
            "score": round(total_score, 2),
            "strength_level": strength_level,
            "strength_description": strength_desc,
            "details": {
                "length": {
                    "score": length_score,
                    "feedback": length_feedback
                },
                "complexity": {
                    "score": complexity_score,
                    "feedback": complexity_feedback
                },
                "patterns": {
                    "score": patterns_score,
                    "feedback": patterns_feedback
                },
                "entropy": {
                    "score": round(entropy_score, 2),
                    "bits": round(math.log2(len(password) or 1), 2) if password else 0
                }
            },
            "recommendations": self.generate_recommendations(
                length_score, complexity_score, patterns_score
            ),
            "timestamp": datetime.now().isoformat()
        }
        
        return result
        
    def mask_password(self, password: str) -> str:
        """掩码密码显示"""
        if len(password) <= 4:
            return "*" * len(password)
        else:
            return password[:2] + "*" * (len(password) - 4) + password[-2:]
            
    def generate_recommendations(self, length_score: int, complexity_score: int, patterns_score: int) -> List[str]:
        """生成改进建议"""
        recommendations = []
        
        if length_score < 80:
            recommendations.append("增加密码长度,建议至少12个字符")
            
        if complexity_score < 80:
            recommendations.append("使用更多类型的字符(大小写字母、数字、特殊符号)")
            
        if patterns_score < 80:
            recommendations.append("避免使用常见词汇、键盘序列和重复字符")
            
        if not recommendations:
            recommendations.append("密码强度良好,继续保持")
            
        return recommendations
        
    def check_password_list(self, passwords: List[str]) -> List[Dict[str, any]]:
        """批量检查密码列表"""
        results = []
        
        for i, password in enumerate(passwords):
            try:
                result = self.evaluate_password_strength(password.strip())
                results.append(result)
                logger.info(f"已检查第 {i+1} 个密码")
            except Exception as e:
                logger.error(f"检查第 {i+1} 个密码时出错: {str(e)}")
                results.append({
                    "password": self.mask_password(password.strip()),
                    "error": str(e),
                    "timestamp": datetime.now().isoformat()
                })
                
        return results
        
    def load_passwords_from_file(self, file_path: str) -> List[str]:
        """从文件加载密码列表"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                passwords = [line.strip() for line in f if line.strip()]
            logger.info(f"从文件加载了 {len(passwords)} 个密码")
            return passwords
        except FileNotFoundError:
            logger.error(f"密码文件不存在: {file_path}")
            raise PasswordSecurityError(f"文件未找到: {file_path}")
        except Exception as e:
            logger.error(f"加载密码文件时出错: {str(e)}")
            raise PasswordSecurityError(f"文件加载失败: {str(e)}")
            
    def save_results(self, results: List[Dict[str, any]], output_file: str):
        """保存检测结果"""
        try:
            # 确保输出目录存在
            output_dir = os.path.dirname(output_file) if os.path.dirname(output_file) else '.'
            os.makedirs(output_dir, exist_ok=True)
            
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(results, f, indent=2, ensure_ascii=False)
                
            logger.info(f"检测结果已保存到 {output_file}")
        except Exception as e:
            logger.error(f"保存检测结果时出错: {str(e)}")
            raise PasswordSecurityError(f"结果保存失败: {str(e)}")
            
    def generate_report(self, results: List[Dict[str, any]]) -> Dict[str, any]:
        """生成检测报告"""
        weak_passwords = [r for r in results if r.get("strength_level") in ["weak", "very_weak"]]
        medium_passwords = [r for r in results if r.get("strength_level") == "medium"]
        strong_passwords = [r for r in results if r.get("strength_level") in ["strong", "very_strong"]]
        
        report = {
            "summary": {
                "total_passwords": len(results),
                "very_strong": len([r for r in results if r.get("strength_level") == "very_strong"]),
                "strong": len([r for r in results if r.get("strength_level") == "strong"]),
                "medium": len(medium_passwords),
                "weak": len([r for r in results if r.get("strength_level") == "weak"]),
                "very_weak": len([r for r in results if r.get("strength_level") == "very_weak"])
            },
            "statistics": self.stats,
            "weak_passwords": weak_passwords[:10],  # 只显示前10个弱密码
            "recommendations": [
                "定期更换密码",
                "使用密码管理器生成和存储复杂密码",
                "启用多因素认证",
                "避免在多个账户使用相同密码"
            ],
            "timestamp": datetime.now().isoformat()
        }
        
        return report
        
    def print_result(self, result: Dict[str, any]):
        """打印检测结果"""
        print("\n" + "="*60)
        print("密码强度检测结果")
        print("="*60)
        print(f"密码: {result['password']}")
        print(f"综合得分: {result['score']}/100")
        print(f"强度等级: {result['strength_description']}")
        print("-"*60)
        print("详细分析:")
        print(f"  长度检测: {result['details']['length']['score']}/100 - {result['details']['length']['feedback']}")
        print(f"  复杂度检测: {result['details']['complexity']['score']}/100 - {result['details']['complexity']['feedback']}")
        print(f"  模式检测: {result['details']['patterns']['score']}/100 - {result['details']['patterns']['feedback']}")
        print(f"  熵值检测: {result['details']['entropy']['score']}/100 ({result['details']['entropy']['bits']} bits)")
        print("-"*60)
        print("改进建议:")
        for i, rec in enumerate(result['recommendations'], 1):
            print(f"  {i}. {rec}")
        print("="*60)
        
    def print_report(self, report: Dict[str, any]):
        """打印检测报告"""
        print("\n" + "="*60)
        print("密码强度检测报告")
        print("="*60)
        print(f"检测时间: {report['timestamp']}")
        print("-"*60)
        print("统计摘要:")
        print(f"  总密码数: {report['summary']['total_passwords']}")
        print(f"  非常强: {report['summary']['very_strong']}")
        print(f"  强: {report['summary']['strong']}")
        print(f"  中等: {report['summary']['medium']}")
        print(f"  弱: {report['summary']['weak']}")
        print(f"  非常弱: {report['summary']['very_weak']}")
        print("-"*60)
        print("安全建议:")
        for i, rec in enumerate(report['recommendations'], 1):
            print(f"  {i}. {rec}")
        print("="*60)

def create_sample_config():
    """创建示例配置文件"""
    sample_config = {
        "min_length": 8,
        "max_length": 128,
        "require_uppercase": True,
        "require_lowercase": True,
        "require_digits": True,
        "require_special_chars": True,
        "min_special_chars": 1,
        "blacklist_words": [
            "password", "123456", "qwerty", "admin", "welcome",
            "login", "root", "guest", "user", "test", "abc123"
        ],
        "keyboard_patterns": [
            "qwerty", "asdf", "zxcv", "1234", "abcd", "qwer"
        ],
        "scoring_weights": {
            "length": 0.3,
            "complexity": 0.4,
            "patterns": 0.2,
            "entropy": 0.1
        }
    }
    
    with open('password_checker_config.json', 'w', encoding='utf-8') as f:
        json.dump(sample_config, f, indent=2, ensure_ascii=False)
    logger.info("示例配置文件已创建: password_checker_config.json")

def generate_strong_password(length: int = 16) -> str:
    """生成强密码"""
    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()"
    password = ''.join(secrets.choice(alphabet) for _ in range(length))
    return password

def main():
    parser = argparse.ArgumentParser(description='密码强度检测器')
    parser.add_argument('password', nargs='?', help='要检测的密码')
    parser.add_argument('-f', '--file', help='包含密码列表的文件路径')
    parser.add_argument('-o', '--output', help='输出结果文件路径')
    parser.add_argument('-c', '--config', help='配置文件路径')
    parser.add_argument('--generate', type=int, nargs='?', const=16, help='生成指定长度的强密码')
    parser.add_argument('--sample-config', action='store_true', help='创建示例配置文件')
    parser.add_argument('--interactive', action='store_true', help='交互式检测模式')
    
    args = parser.parse_args()
    
    if args.sample_config:
        create_sample_config()
        return
        
    if args.generate:
        password = generate_strong_password(args.generate)
        print(f"生成的强密码: {password}")
        return
        
    # 加载配置
    config = {}
    if args.config and os.path.exists(args.config):
        try:
            with open(args.config, 'r', encoding='utf-8') as f:
                config = json.load(f)
        except Exception as e:
            logger.error(f"加载配置文件失败: {str(e)}")
            
    # 创建检测器实例
    checker = PasswordStrengthChecker(config)
    
    try:
        if args.interactive:
            # 交互式模式
            while True:
                try:
                    password = input("\n请输入要检测的密码 (输入 'quit' 退出): ")
                    if password.lower() == 'quit':
                        break
                    if not password:
                        continue
                        
                    result = checker.evaluate_password_strength(password)
                    checker.print_result(result)
                except KeyboardInterrupt:
                    print("\n退出交互模式")
                    break
                except Exception as e:
                    logger.error(f"密码检测出错: {str(e)}")
                    
        elif args.file:
            # 文件批量检测模式
            passwords = checker.load_passwords_from_file(args.file)
            results = checker.check_password_list(passwords)
            
            # 保存结果
            output_file = args.output or f"password_check_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
            checker.save_results(results, output_file)
            
            # 生成报告
            report = checker.generate_report(results)
            report_file = f"{output_file}.report.json"
            with open(report_file, 'w', encoding='utf-8') as f:
                json.dump(report, f, indent=2, ensure_ascii=False)
            logger.info(f"检测报告已保存到 {report_file}")
            
            # 打印报告
            checker.print_report(report)
            
        elif args.password:
            # 单密码检测模式
            result = checker.evaluate_password_strength(args.password)
            checker.print_result(result)
            
            # 保存结果
            if args.output:
                checker.save_results([result], args.output)
                
        else:
            # 显示帮助信息
            parser.print_help()
            
    except KeyboardInterrupt:
        logger.info("密码检测被用户中断")
        sys.exit(1)
    except PasswordSecurityError as e:
        logger.error(f"密码安全错误: {str(e)}")
        sys.exit(1)
    except Exception as e:
        logger.error(f"密码检测过程中发生未知错误: {str(e)}")
        sys.exit(1)

if __name__ == '__main__':
    main()

使用说明

1. 基本使用

# 检测单个密码
python password_checker.py "MyPassword123!"

# 交互式检测模式
python password_checker.py --interactive

# 生成强密码
python password_checker.py --generate
python password_checker.py --generate 20  # 指定长度

2. 批量检测

# 从文件批量检测密码
python password_checker.py -f passwords.txt

# 指定输出文件
python password_checker.py -f passwords.txt -o results.json

3. 配置文件使用

# 使用自定义配置文件
python password_checker.py "MyPassword123!" -c config.json

# 创建示例配置文件
python password_checker.py --sample-config

配置文件示例

创建一个名为 config.json 的配置文件:

{
  "min_length": 12,
  "max_length": 128,
  "require_uppercase": true,
  "require_lowercase": true,
  "require_digits": true,
  "require_special_chars": true,
  "min_special_chars": 2,
  "blacklist_words": [
    "password", "123456", "qwerty", "admin", "welcome",
    "login", "root", "guest", "user", "test", "abc123",
    "password123", "admin123"
  ],
  "keyboard_patterns": [
    "qwerty", "asdf", "zxcv", "1234", "abcd", "qwer",
    "qazwsx", "plmokn"
  ],
  "scoring_weights": {
    "length": 0.25,
    "complexity": 0.35,
    "patterns": 0.25,
    "entropy": 0.15
  }
}

密码文件格式

创建一个名为 passwords.txt 的密码文件:

MyPassword123!
weakpassword
Admin123
qwerty123
StrongPass!@#456
testuser
SecurePassword2023$
password
12345678
MySecurePass99!

高级特性

1. 集成到应用程序

from password_checker import PasswordStrengthChecker

# 创建检测器实例
checker = PasswordStrengthChecker()

# 在用户注册时检查密码强度
def register_user(username, password):
    try:
        result = checker.evaluate_password_strength(password)
        if result['strength_level'] in ['weak', 'very_weak']:
            return False, "密码强度不足,请使用更强的密码"
        return True, "密码强度合格"
    except Exception as e:
        return False, str(e)

2. 定期安全审计

# 每周日凌晨2点执行密码安全审计
0 2 * * 0 /usr/bin/python3 /path/to/password_checker.py -f /path/to/user_passwords.txt -o /var/log/password_audit.json

3. 企业级部署

# 企业级密码策略配置
enterprise_config = {
    "min_length": 16,
    "require_uppercase": True,
    "require_lowercase": True,
    "require_digits": True,
    "require_special_chars": True,
    "min_special_chars": 3,
    "blacklist_words": ["companyname", "productname"],  # 企业特定词汇
    "scoring_weights": {
        "length": 0.2,
        "complexity": 0.4,
        "patterns": 0.3,
        "entropy": 0.1
    }
}

checker = PasswordStrengthChecker(enterprise_config)

性能优化

1. 内存管理

2. 检测速度优化

3. 错误处理优化

安全考虑

1. 数据安全

2. 系统安全

3. 隐私保护

这个密码强度检测器是一个功能强大、安全可靠的安全工具,能够帮助用户和企业评估密码的安全性,提升整体网络安全水平。

以上就是基于Python实现一个专业的密码强度检测器的详细内容,更多关于Python密码强度检测器的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文