• Github敏感信息自动扫描

    云管理服务专家新钛云服 侯明明原创

    日常工作中,运维和开发同事通常会有自己的Github账号,用来上传自己编写的代码或存放博客文章、笔记等资料,有时会不经意的把公司或客户的敏感信息上传到Github,轻的会泄漏域名、IP等信息,重则泄漏系统的登录方式、密码,以及数据库的密码等非常重要的信息。

    一旦发生此类事件,可能会给公司带来相当大的损失,影响公司形象,所以,我们需要一个自动化的扫描敏感信息的工具尽早发现此类问题,并对有敏感信息的库进行删除或设置为Private。

    实现流程

     

    脚本使用Python编写,该部分会讲述该脚本的实现的思路与流程。

    1.提交关键字

    Github的搜索需要关键字,例如 example.com、mycompany123(mycompany为示例,实际可能是 cisco123、cisco@123)等,需要各同事集思广益,提交自己负责业务的关键字

    注意关键字不能太过宽泛,例如某个IP地址,或非常常用的密码,例如password、abc123等,应该提供契合业务系统的关键字

    为了方便提交以及删除旧关键字,我们不写到服务器的配置文件内,而是利用维格表 这款工具,下文会详细介绍

    2.程序读取关键字

    将会用维格表的API读取关键字,传给代码使用。

    3.进行扫描,并输出日志

    扫描后,将日志输出到维格表,方便查看。

    4.告警发送

    将扫描到的结果发送到钉钉,方便告警负责人员及时发现。

    5.人工查看扫描日志,并做出判断

    需要人工查看扫描到的结果是否为敏感信息。

    6.处理发现的问题

    如果确实是泄漏了敏感信息,需要找到代码的负责人,并删除仓库或设置为私有

    具体的实现

    提交关键字

     

    1. 维格表可以自行在网上搜索,注册账号后登录,这里不再介绍
    2. 创建扫描关键字表

        表的名称为 “git-scan-info”,表名称不是强制的,可以自定义

        新建四列选项,如下

        - 标题 :维格列类型为单行文本

        - 类型 :维格列类型为 “单选”,目前仅有一个选项,即关键字

        - 提交人 :同样为“单选”,表示是谁提交的关键字

        - 提交时间 :维格列类型为“创建时间”,创建关键字时会自动出现创建时间

     示例图如下:

    Github敏感信息自动扫描

    1. 创建扫描日志表表的列名称如下:- 扫描关键字

    单行文本,脚本会将扫描内容时使用的关键字写到该列

    – 文件名

    单行文本,会展示从哪个文件中扫描到的

        - 用户名

       单行文本,会展示扫描到文件的所属人

        - 仓库

         单行文本,会展示扫描到文件的所属仓库

        - URL

         网址,显示扫描到文件的URL,可以直接点击后访问

        - 扫描日期

         日期,显示扫描时的时间

        - 状态

         单选,需要人工查看,如果是不相关的内容,可设置为白名单

        示例图如下

    准备配置文件

    需要编写一个配置文件,命名为 config.ini,存放access token等信息,示例如下:

    [GitHub]
    Access_Token = XXXXXXXXXX
    
    [Vika_API]
    api_token = XXXXXXXXXX
    git_scan_info_sheet = XXXXXXXXXX
    git_scan_log_sheet = XXXXXXXXXX
    
    
    [DingTalk]
    webhook = https://oapi.dingtalk.com/robot/send?access_token=XXXXXXXXXX
    # 钉钉机器人的 secret
    secret = XXXXXXXXXX
    # 告警消息中显示的图片
    github_pic_link = https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fs10.sinaimg.cn%2Fmiddle%2F8d046893hbad7ee850439%26690&refer=http%3A%2F%2Fs10.sinaimg.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1624937350&t=118aa4b56f3983526c9043779773147d
    mobiles = XXXXXXXXXXX

    我们对上面文件中的内容一一解释:

    • GitHub

        - Access_Token

        调用Github API时,需要该项,按下图示例,可获取到token

    Github敏感信息自动扫描

    • Vika_API

        打开 “git-scan-info” 和 “git-scan-log”表,获取的示例图如下

    Github敏感信息自动扫描

        - api_token:图上第4个

        - git_scan_info_sheet:图上第5个

        - git_scan_log_sheet:图上第5个

    • DingTalk

        - webhook:钉钉群自定义机器人的webhook链接,这里不再截图展示

        - secret:采用secret的加密方式

        - github_pic_link:告警时会附带该照片,可直接使用示例中的链接,也可以自己找一个图片提供一下链接,下文中会有告警的示例

        - mobiles:钉钉告警时会@该人

    脚本的一些准备工作

    1. 创建脚本主程序,命名为 git-scan.py
    2. 导入需要使用的模块
    #!/usr/bin/env python
    
    import time
    import sys
    import logging
    from configparser import RawConfigParser
    
    from github import Github
    from vika import Vika
    from dingtalkchatbot.chatbot import DingtalkChatbot

     有个别模块需要安装,命令如下

    pip3 install DingtalkChatbot
    pip3 install vika
    pip3 install PyGithub

        DingtalkChatbot 是钉钉告警使用的模块,可以降低我们使用钉钉告警的难度

    1.日志格式定义

    logging.basicConfig(level=logging.INFO,
                       filename='/var/log/git-scan.log',
                       datefmt='%Y/%m/%d %H:%M:%S',
                       format='%(asctime)s - %(levelname)s - %(message)s')
    logger = logging.getLogger(__name__)

    2.获取config.ini的配置项

    BASE_DIR = sys.path[0]
    config = RawConfigParser()
    
    logging.info('--------------本次程序执行日志分割线【开始】--------------')
    logging.info('开始获取配置文件【config.ini】中的信息...')
    config.read(BASE_DIR + '/' + 'config.ini')
    gitHub_access_token = config['GitHub']['Access_Token']
    
    vika_api_token = config['Vika_API']['api_token']
    git_scan_log_sheet = config['Vika_API']['git_scan_log_sheet']
    git_scan_info_sheet = config['Vika_API']['git_scan_info_sheet']
    
    
    dingtalk_webhook = config['DingTalk']['webhook']
    dingtalk_secret = config['DingTalk']['secret']
    github_pic_link = config['DingTalk']['github_pic_link']
    mobiles = config['DingTalk']['mobiles']
    logging.info('配置已获取完成...')

    3.映射维格表的字段

    由于表格的字段名是中文,虽然Python支持中文变量名,但不太合规范,且中文使用不太方便,维格表的Python SDK提供了字段映射的功能,具体的代码见如下示例:

    logging.info('映射vika表字段...')
    vika = Vika(vika_api_token)
    git_scan_log_sheet = vika.datasheet(git_scan_log_sheet,field_key_map={
       "scan_keywords": "扫描关键字",
       "path": "文件名",
       "user": "用户名",
       "full_name": "仓库",
       "html_url": "URL",
       "scan_date": "扫描日期",
       "status": "状态"
    })
    
    
    git_scan_scan_info = vika.datasheet(git_scan_info_sheet, field_key_map={
       "title": "标题",
       "type": "类型",
    })

    脚本读取关键字

    1. 编写获取关键字的函数
    def get_scan_info():
       # 过滤关键字
       logging.info('获取vika中定义的扫描关键字...')
       keywords = git_scan_scan_info.records.filter(type="关键字")
       keywork_list = []
       for keyword in keywords:
           keywork_list.append(keyword.title)
           logging.info('已获取到关键字【{}】'.format(keyword.title))
       return keywork_list

        使用了records.filter过滤了类型为关键字的行,最终返回的是关键字的列表。

    告警发送

    1. 编写钉钉发送告警的函数
    def dingtalk_notify(scan_keywords, user, path, full_name, scan_date, github_pic_link, html_url):
       at_mobiles = [mobiles]
       logging.info('初始化Dingtalk机器人...')
       xiaoding = DingtalkChatbot(dingtalk_webhook, dingtalk_secret)
       
       xiaoding.send_markdown(title='Github关键字扫描告警', text='### 扫描到新的内容!请关注! \n\n'
                              '> 关键字: {} \n\n'
                              '> 用户名: {}\n\n'
                              '> 文件名: {}\n\n'
                              '> 仓库名: {}\n\n'
                              '> 扫描时间: {}\n\n'
                              '> ![Github]({})\n\n'
                              '> #### [点击打开扫描到的文件链接]({})\n\n'.format(scan_keywords, user, path, full_name, scan_date, github_pic_link, html_url), at_mobiles=at_mobiles)
       logging.info('扫描到关键字【{}】,已发送钉钉群消息,请查收'.format(scan_keywords))

    我们使用Markdown的方式发送告警,告警效果会比较美观,同时使用了 DingtalkChatbot 模块,简化了告警的代码,降低了使用的难度。

        告警的效果见下图:

    Github敏感信息自动扫描

    进行扫描,并输出日志

    1.先编写判断维格表重复内容的函数

    def vika_whitelist(html_url):
        logging.info('开始判断目标vika表是否有重复内容...')
        records = git_scan_log_sheet.records.all()
        html_url_list = []
    
        for record in records:
            html_url_list.append(record.html_url)
    
        try:
            if html_url in html_url_list:
                logging.info('该信息已存在...')
                result = '已存在'
            else:
                logging.info('该信息不存在,即将开始插入信息...')
                result = '不存在'
        except KeyError:
            logging.error('判断URL时出现报错,请查看...')
            pass
            
        return result

    程序首次扫描到敏感信息写入到日志后,后续会再次扫描,但不会重复写入到日志,vika_whitelist函数的作用是检查是否重复的,以url链接为判断依据,但也有缺点,如果在同一URL的文件再次添加了敏感信息,则扫描不到,这是需要优化的地方。

    2.进行扫描

    使用Github的API扫描结果,临时保存到列表中,并判断如果之前未扫描同样的结果时,发送钉钉告警,并写入到日志中

    def git_search(query_keyword_list):
        logging.info('初始化github Token')
        g = Github(gitHub_access_token)
    
        result_list = []
        
    
        for i in query_keyword_list:
            results = g.search_code(i)
            
            for result in results:
                logging.info('创建一个空字典,保存搜索到的结果')
                result_dict = {}
              
                result_dict['scan_keywords'] = i
                result_dict['user'] = result.repository.owner.login
                result_dict['path'] = result.path
                result_dict['full_name'] = result.repository.full_name
                result_dict['html_url'] = result.html_url
                result_dict['scan_date'] = time.strftime("%Y-%m-%d %H:%M", time.localtime())
                logging.info('写入搜索到的结果...')
                
            
                # 判断字典是否为空,空表示通过关键字没有搜到结果,如果不做这一步,后面会无法在vika批量插入数据
                if result_dict:
                    if vika_whitelist(result_dict['html_url']) == '不存在':
                        result_list.append(result_dict)
                        logging.info('发现新的扫描结果,准备发送钉钉消息...')
                        dingtalk_notify(i, result.repository.owner.login, result.path, result.repository.full_name, result_dict['scan_date'], github_pic_link, result.html_url)
                        logging.info('钉钉消息发送完成...')
                        
        return result_list

    3.写入数据

    批量插入扫描到的数据

    def vika_log(records_list):
        git_scan_log_sheet.records.bulk_create(records_list)

    4.main函数

    if __name__ == '__main__':
        vika_log(git_search(get_scan_info()))
        logging.info('程序执行完成!')
        logging.info('--------------本次程序执行日志分割线【结束】--------------')

    crontab调度脚本

    将主程序脚本和配置文件放在 /data/git-scan/ 目录下,并使用crontab 定时调度程序,下面示例为每6小时执行一次,也可根据自己的需求修改

    * */6 * * * /usr/bin/python3 /data/git-scan/git-scan.py

    人工查看告警,并做出判断

    如果钉钉中发出了告警信息,可以直接进去查看,如果单次告警较多时,可以直接进入扫描脚本的log中查看,示例log如下:

    Github敏感信息自动扫描

    处理发现的问题

    如果扫描到的结果确实有敏感信息泄漏的情况,应立即找到上传的人员,并删除仓库或者设置为私有库。

    结语:至此,该脚本算是初步完成,希望能提供一些思路,对我们日常工作中敏感信息检查提供一些帮助。

    «
    »
以专业成就每一位客户,让企业IT只为效果和安全买单

以专业成就每一位客户,让企业IT只为效果和安全买单