JSRadar (JavaScript敏感信息辅助脚本)

admin 2025年3月26日14:19:30评论18 views字数 18732阅读62分26秒阅读模式

作用原理

通过 _compile_rules 加载YAML规则文件,使用多线程 ThreadPoolExecutor 扫描JS/HTML文件中的敏感模式,最终生成可视化风险报告。

主要优势

✅ 高效扫描 - 多线程处理400+文件/秒

✅ 精准检测 - 支持邮箱/密钥/身份证等20+敏感模式

✅ 风险可视化 - 带星标分级和代码片段展示的 generate_report

✅ 智能缓存 - 基于 _calculate_md5 的增量扫描

当前局限

❌ 误报率依赖正则精度

❌ 仅支持静态代码分析

❌ 未集成动态调试能力

❌ 规则需手动维护更新

渗透测试应用场景

  • 项目源码 敏感信息泄露 审计
  • 前端代码 硬编码凭证 提取
  • 第三方库 供应链风险 检测
  • 自动化报告生成

js_sensitive_scanner.py代码

import os
import re
import json
import yaml
import chardet
from datetime import datetime
import argparse
import hashlib
from concurrent.futures import ThreadPoolExecutor
import regex  # 替换标准re库
import html  # 新增html模块导入
class SecurityScanner:
    def __init__(self, rule_file="security_rules.yaml"):
        self.file_hashes = {}
        self._load_cache()
        self.rules = self._load_rules(rule_file)
        self.compiled_rules = self._compile_rules()
        
    def _load_rules(self, rule_file):
        with open(rule_file, 'r', encoding='utf-8') as f:
            full_config = yaml.safe_load(f)  # 整个配置文件
            return full_config['rules']  # 明确提取rules字段
    
    # 问题1:Java Deserialization规则缺少color字段(在规则文件中补充)
    def _compile_rules(self):
        """修复后的规则编译方法"""
        compiled = []
        for group in self.rules:
            if isinstance(group, dict) and 'rule' in group:
                for rule in group['rule']:
                    color = rule.get('color', 'yellow')
                    try:
                        # 直接使用原始正则表达式字符串(YAML中应已正确转义)
                        compiled.append((
                            re.compile(rule['f_regex']),  # 移除多余的字符串格式化
                            rule['name'],
                            self._map_color_to_severity(color)
                        ))
                    except Exception as e:
                        print(f"规则编译失败: {rule.get('name')} - {str(e)}")
        return compiled
    def _load_cache(self):
        """加载文件哈希缓存"""
        try:
            with open('scan_cache.json', 'r') as f:
                self.file_hashes = json.load(f)
        except FileNotFoundError:
            self.file_hashes = {}
    def _save_cache(self):
        """保存文件哈希缓存"""
        with open('scan_cache.json', 'w') as f:
            json.dump(self.file_hashes, f)
    def _map_color_to_severity(self, color):
        """添加缺失的风险等级映射方法"""
        color_map = {'red':5, 'orange':4, 'yellow':3, 'cyan':2, 'green':1}
        return color_map.get(color.lower(), 2)
    # 修复点1:删除重复的 _compile_rules 方法(25行附近)
    # 原25行的旧方法定义已被下方的新实现替代
    # 删除旧的 _compile_rules 方法(原26行附近)
    # def _compile_rules(self):
    #    """修复后的规则编译方法""" 
    #    ... 旧实现 ...
    
    # 保留下方合并后的新实现(约70行附近)
    def _compile_rules(self):
        """合并后的正则编译方法"""
        compiled = []
        for group in self.rules:
            if isinstance(group, dict) and 'rule' in group:
                for rule in group.get('rule', []):
                    try:
                        compiled.append((
                            regex.compile(rule['f_regex'], flags=regex.VERSION1),
                            rule['name'],
                            self._map_color_to_severity(rule.get('color', 'yellow'))
                        ))
                    except Exception as e:
                        print(f"规则编译失败: {rule.get('name')} - {str(e)}")
        return compiled
    def scan_file(self, file_path):
        """修复未定义的seen变量"""
        if self._is_file_unchanged(file_path):
            return []
            
        findings = []
        try:
            with open(file_path, 'rb') as f:
                raw_data = f.read(4096)
            encoding_result = chardet.detect(raw_data)
            encoding = encoding_result.get('encoding') if encoding_result else 'utf-8'
            seen = set()  # 添加变量初始化
            
            with open(file_path, 'r', encoding=encoding, errors='replace') as f:
                for line_num, line in enumerate(f, 1):  # 流式逐行读取
                    for pattern, label, severity in self.compiled_rules:
                        matches = pattern.finditer(line)
                        for match in matches:
                            raw_value = match.group()
                            unique_key = f"{file_path}|{line_num}|{label}|{raw_value}"
                            if unique_key in seen:
                                continue
                            seen.add(unique_key)
                            # 修复点1:优化敏感标记检测逻辑
                            is_sensitive = next((
                                rule.get('sensitive', False)
                                for group in self.rules
                                for rule in group.get('rule', [])
                                if rule.get('name') == label
                            ), False)
                            
                            sanitized = self._sanitize(raw_value, is_sensitive)
                            
                            if is_sensitive and sanitized == raw_value:
                                print(f"警告:未脱敏的敏感数据 @ {file_path}:{line_num}")
                                
                            findings.append({
                                "file": os.path.basename(file_path),
                                "line": line_num,
                                "type": label,
                                "raw": raw_value,
                                "sanitized": sanitized,
                                "severity": severity
                            })
        except Exception as e:
            print(f"扫描失败: {file_path} - {str(e)}")
        return findings
    def _is_file_unchanged(self, file_path):
        """检查文件是否修改"""
        current_hash = self._calculate_md5(file_path)
        return self.file_hashes.get(file_path) == current_hash
    def _calculate_md5(self, file_path):
        """计算文件哈希"""
        hasher = hashlib.md5()
        with open(file_path, 'rb') as f:
            buf = f.read(4096)
            hasher.update(buf)
        return hasher.hexdigest()
    def _update_file_hash(self, file_path):
        """更新文件哈希"""
        self.file_hashes[file_path] = self._calculate_md5(file_path)
    def _sanitize(self, text, is_sensitive):
        """增强版脱敏逻辑"""
        if not is_sensitive: 
            return text
            
        # 新增邮箱脱敏:t***@example.com
        text = regex.sub(r'(\w{1,3})[\w.-]+@([\w-]+\.[a-z]{2,7})', r'\1***@\2', text)
        # 新增密钥脱敏:AKIA***4567
        text = regex.sub(r'\b([A-Z0-9]{4})[A-Z0-9]{12,}([A-Z0-9]{4})\b', r'\1***\2', text)
        # 保持原有手机号/身份证脱敏...
        return text
    def _read_file_content(self, file_path):
        """提取的公共文件读取方法(唯一实现)"""
        try:
            with open(file_path, 'rb') as f:
                raw_data = f.read(4096)
            encoding_result = chardet.detect(raw_data)
            encoding = encoding_result.get('encoding') if encoding_result else 'utf-8'
            with open(file_path, 'r', encoding=encoding, errors='replace') as f:
                return f.read()
        except Exception as read_error:
            print(f"读取文件失败: {file_path} - {str(read_error)}")
            return None
# 修复点2:添加report_path定义(原181行附近)
def generate_report(results, output_dir):
    """修复None类型操作问题"""
    report_path = os.path.join(output_dir, f"security_report_{datetime.now().strftime('%Y%m%d%H%M')}.html")
    
    risk_levels = {5:0,4:0,3:0,2:0,1:0}
    # 修复重复统计问题
    for item in results:
        severity = item.get('severity', 0) or 0
        risk_levels[severity] += 1
    with open(report_path, 'w', encoding='utf-8') as f:
        f.write(rf"""<html>
        <head><title>安全扫描报告</title>
        <style>
            /* 保持原有样式不变 */
        </style>
        <script>
            // 合并后的切换函数(修复转义问题)
            function toggleAllSnippets(btn) {{
                const snippets = document.querySelectorAll('.code-snippet');
                if(!snippets.length) {{
                    console.error('未找到代码片段');
                    return;
                }}
                const isHidden = window.getComputedStyle(snippets[0]).display === 'none';
                snippets.forEach(s => s.style.display = isHidden ? 'block' : 'none');
                btn.textContent = isHidden ? '折叠所有代码' : '展开所有代码';
            }}
            function toggleCodeSnippet(btn) {{
                const snippet = btn.nextElementSibling;
                const isHidden = window.getComputedStyle(snippet).display === 'none';
                snippet.style.display = isHidden ? 'block' : 'none';
                btn.textContent = isHidden ? '隐藏代码' : '显示代码';
            }}
        </script>
        </head>
        <body>
        <h2>安全扫描报告({datetime.now().strftime('%Y-%m-%d %H:%M')})</h2>
        
        <!-- 新增全局展开按钮 -->
        <button onclick="toggleAllSnippets(this)" 
                style="padding:6px 12px;background:#007bff;color:white;border-radius:4px;margin-bottom:16px">
            一键展开所有代码
        </button>
        <!-- 筛选按钮部分保持不变 -->
        <!-- 单一表格结构 -->
        <table>
            <thead>
                <tr>
                    <th>文件</th>
                    <th>行号</th>
                    <th>风险详情</th>
                    <th>风险等级</th>
                </tr>
            </thead>
            <tbody>
                {"".join(
                    f'''
                    <tr data-severity="{item['severity']}">
                        <td>{html.escape(item['file'])}</td>
                        <td>{item['line']}</td>
                        <td>
                            <div class="risk-details">
                                <span class="risk-type">{html.escape(item['type'])}</span>
                                <div>
                                    <button onclick='toggleCodeSnippet(this)' 
                                            style='padding:2px 8px;margin:2px'>
                                        显示代码
                                    </button>
                                    <div class='code-snippet' style='display:none'>
                                        {html.escape(item['sanitized']).replace(chr(10), '<br>')}
                                    </div>
                                </div>
                            </div>
                        </td>
                        <td>{'★'*item['severity']}</td>
                    </tr>
                    ''' 
                    for item in sorted(results, key=lambda x: (-x['severity'], x['file']))
                )}
            </tbody>
        </table>
        <script>
            // 新增全局切换函数
            // 将两个切换函数合并到同一个script标签中
            function toggleAllSnippets(btn) {{
                const snippets = document.querySelectorAll('.code-snippet');
                if(snippets.length === 0) return;
                
                const isHidden = snippets[0].style.display === 'none';
                snippets.forEach(s => s.style.display = isHidden ? 'block' : 'none');
                btn.textContent = isHidden ? '折叠所有代码' : '展开所有代码';
            }}
            function toggleCodeSnippet(btn) {{
                const snippet = btn.nextElementSibling;
                snippet.style.display = snippet.style.display === 'none' ? 'block' : 'none';
                btn.textContent = snippet.style.display === 'none' ? '显示代码' : '隐藏代码';
            }}
        <\/script>
        </body>
        </html>
        """)  # 这是唯一的闭合标签
        # 删除下方重复的闭合标签和脚本 ▼▼▼
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='安全信息扫描系统')
    parser.add_argument('-d', '--directory', required=True, help='扫描目录路径')
    parser.add_argument('-o', '--output', default='reports', help='报告输出目录')
    args = parser.parse_args()
    scanner = SecurityScanner()
    total_results = []
    
    # 修复点:删除重复的线程池执行代码(原281行附近)
    # 修正后的线程池配置(添加容错处理)
    max_workers = min(os.cpu_count() * 2, 32) if os.cpu_count() else 4
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = []
        for root, _, files in os.walk(args.directory):
            for file in files:
                if file.lower().endswith(('.js','.html','.jsx')):  # 添加大小写不敏感检查
                    full_path = os.path.join(root, file)
                    futures.append(executor.submit(scanner.scan_file, full_path))
        
        # 添加进度跟踪
        print(f"正在扫描 {len(futures)} 个文件...")
        for i, future in enumerate(futures, 1):
            try:
                total_results.extend(future.result())
                if i % 10 == 0:
                    print(f"已处理 {i}/{len(futures)} 个文件")
            except Exception as e:
                print(f"线程执行出错: {str(e)}")
    # 修复点3:移除重复的扫描结果处理代码(文件末尾)
    # 删除以下重复代码块:
    #    scanner._save_cache()
    #    os.makedirs(args.output, exist_ok=True)
    #    generate_report(...)
    #    print(...)
    # 类型安全检查
    for item in total_results:
        item['severity'] = item.get('severity', 0) or 0
    
    generate_report(total_results, args.output)
    print(f"扫描完成,报告已生成至:{os.path.abspath(args.output)}")

security_rules.yaml代码

rules:
- group: Fingerprint
  rule:
  - name: Shiro
    f_regex: \b(rememberMe=[^;]+|JSESSIONID=)\b  # 增加单词边界限制
    loaded: true
    f_regex: (=deleteMe|rememberMe=)
    s_regex: ''
    format: '{0}'
    color: green
    scope: any header
    engine: dfa
    sensitive: true
  - name: JSON Web Token
    loaded: true
    f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
    s_regex: ''
    format: '{0}'
    color: green
    scope: any
    engine: nfa
    sensitive: true
  - name: Swagger UI
    loaded: true
    f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
    s_regex: ''
    format: '{0}'
    color: red
    scope: response body
    engine: dfa
    sensitive: false
  - name: Ueditor
    loaded: true
    f_regex: (ueditor\.(config|all)\.js)
    s_regex: ''
    format: '{0}'
    color: green
    scope: response body
    engine: dfa
    sensitive: false
  - name: Druid
    loaded: true
    f_regex: (Druid Stat Index)
    s_regex: ''
    format: '{0}'
    color: orange
    scope: response body
    engine: dfa
    sensitive: false
- group: Maybe Vulnerability
  rule:
  - name: Java Deserialization
    loaded: true
    f_regex: (\bjavax\.faces\.ViewState\b)  # 添加单词边界限制
    scope: any  # 扩大作用范围到请求和响应
- group: Basic Information
  rule:
  - name: Email
    f_regex: (\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b)  # 简化邮箱正则
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response body
    engine: dfa
    sensitive: false
  - name: Debug Logic Parameters
    loaded: true
    f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=))
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: request
    engine: dfa
    sensitive: false
  - name: URL As A Value
    loaded: true
    f_regex: (=(https?)(://|%3a%2f%2f))
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: any
    engine: nfa
    sensitive: false
  - name: Upload Form
    loaded: true
    f_regex: (type\=\"file\")
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response body
    engine: dfa
    sensitive: false
  - name: DoS Paramters
    loaded: true
    f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: request
    engine: dfa
    sensitive: false
- group: Basic Information
  rule:
  - name: Email
    loaded: true
    f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5}))
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response
    engine: nfa
    sensitive: false
  - name: Chinese IDCard
    loaded: true
    f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]'
    s_regex: ''
    format: '{0}'
    color: orange
    scope: response body
    engine: nfa
    sensitive: true
  - name: Chinese Mobile Number
    loaded: true
    f_regex: '[^\w]((?:(?:\+|0{0,2})86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]'
    s_regex: ''
    format: '{0}'
    color: orange
    scope: response body
    engine: nfa
    sensitive: false
  - name: Internal IP Address
    loaded: true
    f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))'
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: response
    engine: nfa
    sensitive: true
  - name: MAC Address
    loaded: true
    f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}))
    s_regex: ''
    format: '{0}'
    color: green
    scope: response
    engine: nfa
    sensitive: true
# 重复的 Basic Information 组(合并至单个组)
- group: Basic Information
  rule:
  # 合并两个 Email 规则(原第41/129行)
  - name: Email
    f_regex: >-
      (?i)(\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,7}\b)(?<!\.js)(?<!\.css)(?<!\.png)(?<!\.jpg)
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response body
    engine: dfa
    sensitive: false
  - name: Debug Logic Parameters
    loaded: true
    f_regex: ((access=)|(adm=)|(admin=)|(alter=)|(cfg=)|(clone=)|(config=)|(create=)|(dbg=)|(debug=)|(delete=)|(disable=)|(edit=)|(enable=)|(exec=)|(execute=)|(grant=)|(load=)|(make=)|(modify=)|(rename=)|(reset=)|(root=)|(shell=)|(test=)|(toggl=))
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: request
    engine: dfa
    sensitive: false
  - name: URL As A Value
    loaded: true
    f_regex: (=(https?)(://|%3a%2f%2f))
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: any
    engine: nfa
    sensitive: false
  - name: Upload Form
    loaded: true
    f_regex: (type\=\"file\")
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response body
    engine: dfa
    sensitive: false
  - name: DoS Paramters
    loaded: true
    f_regex: ((size=)|(page=)|(num=)|(limit=)|(start=)|(end=)|(count=))
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: request
    engine: dfa
    sensitive: false
# 重复的 Fingerprint 组(合并到原组)
- group: Fingerprint
  rule:
  - name: Shiro
    # 合并两个 f_regex(原第5/7行)
    f_regex: \b(rememberMe=[^;]+|JSESSIONID=|deleteMe|rememberMe=)\b
    loaded: true
    f_regex: (=deleteMe|rememberMe=)
    s_regex: ''
    format: '{0}'
    color: green
    scope: any header
    engine: dfa
    sensitive: true
  - name: JSON Web Token
    loaded: true
    f_regex: (eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]{10,}|eyJ[A-Za-z0-9_\/+-]{10,}\.[A-Za-z0-9._\/+-]{10,})
    s_regex: ''
    format: '{0}'
    color: green
    scope: any
    engine: nfa
    sensitive: true
  - name: Swagger UI
    loaded: true
    f_regex: ((swagger-ui.html)|(\"swagger\":)|(Swagger UI)|(swaggerUi)|(swaggerVersion))
    s_regex: ''
    format: '{0}'
    color: red
    scope: response body
    engine: dfa
    sensitive: false
  - name: Ueditor
    loaded: true
    f_regex: (ueditor\.(config|all)\.js)
    s_regex: ''
    format: '{0}'
    color: green
    scope: response body
    engine: dfa
    sensitive: false
  - name: Druid
    loaded: true
    f_regex: (Druid Stat Index)
    s_regex: ''
    format: '{0}'
    color: orange
    scope: response body
    engine: dfa
    sensitive: false
- group: Maybe Vulnerability
  rule:
  - name: Java Deserialization
    loaded: true
    f_regex: (\bjavax\.faces\.ViewState\b)  # 添加单词边界限制
    scope: any  # 扩大作用范围到请求和响应
- group: Basic Information
  rule:
  - name: Email
    loaded: true
    f_regex: (([a-z0-9]+[_|\.])*[a-z0-9]+@([a-z0-9]+[-|_|\.])*[a-z0-9]+\.((?!js|css|jpg|jpeg|png|ico)[a-z]{2,5}))
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response
    engine: nfa
    sensitive: false
  - name: Chinese IDCard
    loaded: true
    f_regex: '[^0-9]((\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)))[^0-9]'
    s_regex: ''
    format: '{0}'
    color: orange
    scope: response body
    engine: nfa
    sensitive: true
  - name: Chinese Mobile Number
    loaded: true
    f_regex: '[^\w]((?:(?:\+|0{0,2})86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8})[^\w]'
    s_regex: ''
    format: '{0}'
    color: orange
    scope: response body
    engine: nfa
    sensitive: false
  - name: Internal IP Address
    loaded: true
    f_regex: '[^0-9]((127\.0\.0\.1)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3}))'
    s_regex: ''
    format: '{0}'
    color: cyan
    scope: response
    engine: nfa
    sensitive: true
  - name: MAC Address
    loaded: true
    f_regex: (^([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5})|[^a-zA-Z0-9]([a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}))
    s_regex: ''
    format: '{0}'
    color: green
    scope: response
    engine: nfa
    sensitive: true
# 在 Fingerprint 组中新增
- group: Fingerprint
  rule:
  - name: FTP/SFTP Leak
    loaded: true
    f_regex: (sftp:\/\/|ftp:\/\/)[^\s]+(:[^\s]+@)
    color: red
    sensitive: true
# 在 Sensitive Information 组中新增
- group: Sensitive Information
  rule:
  # 新增企业管理平台密钥检测
  - name: Enterprise WeChat Key
    f_regex: (\b(corpid|corpsecret)=[a-zA-Z0-9_-]{32}\b)
    color: red
    sensitive: true
  - name: Feishu Credentials
    f_regex: (\b(app_id|app_secret)=[a-zA-Z0-9_-]{32}\b)
    color: red
    sensitive: true
  # 增强管理员凭证检测
  - name: Admin Credentials
    f_regex: (?i)(['"`]?(admin(_?name|_?user)|root)(['"`]?\s*[:=]\s*['"`]?[^'\"]+))
    color: red
    sensitive: true
# 在 Cloud Key 规则中新增地图密钥检测
  - name: Cloud Key
    f_regex: >-
      (?i)((['"`]?(access_key|map_key|mapsecret)\b['"`]?\s*[:=]\s*['"`]?([A-Z0-9\-_=]{20,})['"`]?)|
      (\b(AKIA|AMAP_)[A-Z0-9]{16}\b))
    # 补充缺失的规则参数
    s_regex: ''
    format: '{0}'
    color: red
    scope: any
    engine: nfa 
    sensitive: true
# 在 Network Infrastructure 组中新增
- group: Network Infrastructure
  rule:
  - name: Sensitive URL Paths
    f_regex: (?i)/(admin|backup|phpmyadmin|\.git|\.svn)/
    color: orange
    sensitive: false
  - name: Dangerous Extensions
    f_regex: \.(git|svn|bak|swp|sql)(\?|$)
    color: yellow
    sensitive: false
  - name: Authorization Header
    loaded: true
    f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100}))
    s_regex: ''
    format: '{0}'
    color: yellow
    scope: response body
    engine: nfa
    sensitive: true
  - name: Password Field
    loaded: true
    f_regex: ((|\\)(|'|")(|[\w]{1,10})(

(ass|wd|asswd|assword))... s_regex: '' format: '{0}' color: yellow scope: response body engine: nfa sensitive: true - name: Username Field loaded: true f_regex: ((|\\)(|'|")(|[\w]{1,10})(([u](ser|name|sername))|(account)|((((create|update)((d|r)|(by|on|at)))|(creator))))(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()( |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\))) s_regex: '' format: '{0}' color: green scope: response body engine: nfa sensitive: false - name: WeCom Key loaded: true f_regex: ((corp)(id|secret)) s_regex: '' format: '{0}' color: green scope: response body engine: dfa sensitive: false - name: JDBC Connection loaded: true f_regex: (jdbc:[a-z:]+://[a-z0-9\.\-_:;=/@?,&]+) s_regex: '' format: '{0}' color: yellow scope: any engine: nfa sensitive: false - name: Authorization Header loaded: true f_regex: ((basic [a-z0-9=:_\+\/-]{5,100})|(bearer [a-z0-9_.=:_\+\/-]{5,100})) s_regex: '' format: '{0}' color: yellow scope: response body engine: nfa sensitive: false - name: Sensitive Field loaded: true f_regex: >- (?i)((['"`]?([\w_]{0,5}((key|secret|token|auth|access|credential|password|config)(s|_id|_key|_store|_name)?|api_?key|private_?key|encryption_?key|admin_?pass))\b['"`]?\s*[:=]\s*['"`]?([A-Z0-9\-_=]{8,64})['"`]?)| (\b(AKIA|ASIA)[A-Z0-9]{16}\b)| (\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36}\b)) s_regex: '' format: '{0}' color: red scope: any engine: nfa sensitive: true # 启用脱敏 - name: Mobile Number Field loaded: true f_regex: ((|\\)(|'|")(|[\w]{1,10})(mobile|phone|sjh|shoujihao|concat)(|[\w]{1,10})(|\\)(|'|")(:|=|\)\.val\()( |)(|\\)('|")([^'"]+?)(|\\)('|")(|,|\))) s_regex: '' format: '{0}' color: green scope: response body engine: nfa sensitive: false - group: Other rule: - name: Linkfinder loaded: true f_regex: (?:"|')((?:(?:[a-zA-Z]{1,10}://|//)[^"'/]{1,}\.[a-zA-Z]{2,}[^"']{0,})|(?:(?:(?:/|\.\./|\./)?[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,}\.[a-zA-Z]{1,4})|(?:(?:/|\.\./|\./)?[^"'><,;|*()(%%$^/\\\[\]][^"'><,;|()]{1,}/[^"'><,;|()]{1,}(?:\.[a-zA-Z]{1,4}|action)?)))(?:[\?|#][^"|']{0,})?(?:"|') s_regex: '' format: '{0}' color: gray scope: response body engine: nfa sensitive: true - name: Source Map loaded: true f_regex: (\.js\.map) s_regex: '' format: '{0}' color: pink scope: response body engine: dfa sensitive: false - name: Create Script loaded: true f_regex: (\{[^{}]*\}\s*\[[^\s]*\]\s*\+\s*"[^\s]*\.js") s_regex: '"?([\w].*?)"?:"(.*?)"' format: '{0}.{1}' color: green scope: response body engine: nfa sensitive: false - name: URL Schemes loaded: true f_regex: (\b(?![\w]{0,10}?https?://)(([-A-Za-z0-9]{1,20})://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])) s_regex: '' format: '{0}' color: yellow scope: response body engine: nfa sensitive: false - name: Router Push loaded: true f_regex: (\$router\.push) s_regex: '' format: '{0}' color: magenta scope: response body engine: dfa sensitive: false - name: All URL loaded: true f_regex: (https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;\u4E00-\u9FFF]+[-A-Za-z0-9+&@#/%=~_|]) s_regex: '' format: '{0}' color: gray scope: response body engine: nfa sensitive: true - name: Request URI loaded: true f_regex: ' ((?!.*\.js(\?.*)?$)(.*?[^.js$])) ' s_regex: '' format: '{0}' color: gray scope: request line engine: nfa sensitive: false - name: 302 Location loaded: true f_regex: 'Location: (.*?)\n' s_regex: '' format: '{0}' color: gray scope: response header engine: nfa sensitive: false

使用方法

python3 .js_sensitive_scanner.py -h
JSRadar (JavaScript敏感信息辅助脚本)
python3 .js_sensitive_scanner.py -d 扫描目录路径 -o 报告输出目录
JSRadar (JavaScript敏感信息辅助脚本)

报告如下

JSRadar (JavaScript敏感信息辅助脚本)

原文始发于微信公众号(漏洞谷):JSRadar (JavaScript敏感信息辅助脚本)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月26日14:19:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JSRadar (JavaScript敏感信息辅助脚本)http://cn-sec.com/archives/3884729.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息