├── LICENSE ├── README.md ├── db_bascline.py ├── doc └── mysql.md ├── image ├── 1524419076653.jpg └── 3041524440631_.pic_hd.jpg ├── log └── .DS_Store ├── loghandle.py ├── requirements.txt └── script ├── __init__.py ├── __init__.pyc ├── db_baseline_basic.py ├── example.py ├── mysql_baseline.py └── mysql_baseline.pyc /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DB_BASELINE 使用说明和检测脚本编写规范 2 | ![](https://github.com/wstart/DB_BaseLine/blob/master/image/1524419076653.jpg?raw=true) 3 | 4 | ![](https://github.com/wstart/DB_BaseLine/blob/master/image/3041524440631_.pic_hd.jpg?raw=true) 5 | 6 | ## 概述 7 | 本文档为DB_BASELINE的使用说明和检测脚本编写规范, 8 | DB_BASELINE主要用于数据库的配置项的基线检查。 9 | 该文档主要描述了DB_BASELINE的使用方法以及检测脚本的编写规范, 10 | 编写规范检测脚本适用于后期导入SYSLOG,SOC等 11 | ## 支持的数据库 12 | - mysql [基线检查详情=>mysql.md](https://github.com/wstart/DB_BaseLine/blob/master/doc/mysql.md) 13 | 14 | ## 安装说明 15 | ### 下载源码 16 | - 直接下载 17 | - 或者 git clone https://github.com/wstart/DB_BaseLine 18 | 19 | ### 安装依赖 20 | - pip install -r requirements.txt 21 | 22 | ## DB_BASE 使用说明 23 | - 帮助信息 python db_baseline.py -h 24 | - 运行检查 python db_baseline.py [options] 25 | - [*] 建议直接在运行数据库的机器上进行基线检查,如果不在运行数据库的机器上进行检查 26 | - 文件安全这一项无法进行 27 | - 部分数据库配置检查也无法进行 28 | 29 | ## DB_BASELINE 基线检查规范 30 | 根据查阅的资料和文档,虽然各个数据库有各自的配置,但是总的来说数据库的基线检查主要涵盖以下四类 31 | 32 | - 账号权限 33 | - 检查运行数据库的账号, 34 | - 数据库里面各个权限账号 35 | - 检查方向:是否有过多的不必要的权限 36 | - 用专有的低权限账号去启动数据库,而不是用root 37 | - 数据库账号不要空密码连接 38 | - 删除或者修改默认账号 39 | - 只有DBA拥有所有权限,其余各个数据库有专门的对应的数据库的账号 40 | - 数据库的账号要限制IP连接 41 | - ... 42 | - 网络连接 43 | - 端口 44 | - 连接类型 45 | - 检查方向:主要用于检查数据库的端口,对外的开放的程度,连接的安全性等等 46 | - 端口改掉默认端口 47 | - 如果提供对外访问 那么网络传输使用SSL或者其他加密的协议 48 | - ... 49 | 50 | - 文件安全 51 | - 配置文件 52 | - 日志文件 53 | - 审计文件 54 | - 备份文件 55 | - 检查方向:主要检查文件权限是否配置准确 56 | - 配置文件,日志文件等应只有数据库账号可以访问 57 | - 限制数据库账号访问其他目录或者对其他目录有写的权限 58 | - ... 59 | - 数据库配置的属性 60 | - 配置文件 61 | - 可以执行的函数 62 | - 检查方向:潜在隐患的配置属性 63 | - 危险函数禁止执行 64 | - 执行系统命令 65 | - 读取文件 66 | - 写入文件 67 | - 导入导出 68 | - ... 69 | - 安全配置是否开启 70 | - 日志文件是否开启 71 | - 审计文件是否开启 72 | - 错误日志是否开启 73 | - 密码复杂度 74 | - 过期账号处理 75 | - ... 76 | 77 | ## DB_BASELINE 编写规范 78 | 所有的检测脚本均在script目录里面。 79 | 80 | db_baseline_basic是检测类的基类,引用基类,编写对应的数据库的检查基线即可 81 | 82 | ## DB_BASELINE 基础构造 83 | - 连接函数 connect 84 | - 用于检查是否满足运行条件 check 85 | - 基线检查主函数 runtest 86 | - 账号权限基线检查 run_power_test 87 | - 网络连接基线检查 run_network_test 88 | - 文件安全基线检查 run_file_test 89 | - 数据库配置基线检查 run_config_test 90 | 91 | ## DB_BASELINE 返回值 92 | ``` 93 | 'Result':{ 94 | 'DBInfo' : {'Host': 'xxx', 'Port': 'xxx'}, #数据库信息 95 | 'VerifyTime': '2018-03-23' ,#检查时间 96 | 'Score': 97,#检查得分 97 | 'Desc': '几乎没有严重的问题',#处理建议 98 | 'Defect': 99 | [ 100 | { 101 | 'Desc':'默认端口没有修改',#描述 102 | 'Level':'低',# 危害等级 103 | 'Suggest':'修改默认端口',# 修复建议 104 | } 105 | ] 106 | } 107 | ``` 108 | 109 | ## TODO 110 | - mongo 基线检查 111 | - oracle 基线检查 112 | - redis 基线检查 113 | - sqlserver 基线检查 114 | 115 | ## 更新记录 116 | - 2017-04-27 117 | - 重新修改检测的类别,以及对应修改了代码,比以前类别更加清晰 118 | - 增加了mysql的检查项的类别 119 | - 2017-04-24 120 | - 0.1 发布,基础架构和雏形 121 | - mysql 基线检查 122 | 123 | -------------------------------------------------------------------------------- /db_bascline.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | reload(sys) 5 | sys.setdefaultencoding('utf-8') 6 | from script.mysql_baseline import * 7 | from loghandle import * 8 | import getopt 9 | 10 | if __name__ == "__main__": 11 | bannber = ''' 12 | ____ ____ ____ _ _ {''' + db_baseline_basic.getVersion() + '''} 13 | | _ \| __ )| __ ) __ _ ___ ___| (_)_ __ ___ 14 | | | | | _ \| _ \ / _` / __|/ _ \ | | '_ \ / _ \\ 15 | | |_| | |_) | |_) | (_| \__ \ __/ | | | | | __/ 16 | |____/|____/|____/ \__,_|___/\___|_|_|_| |_|\___| 17 | (https://github.com/wstart/DB_BaseLine) 18 | --------------------------------------------------''' 19 | 20 | supperdb = ["mysql"] 21 | DBnames = ",".join(supperdb) 22 | small_helper=''' 23 | Usage: python db_baseline.py [options] 24 | python db_baseline.py -h for more infomation 25 | ''' 26 | helper = ''' 27 | Usage: python db_baseline.py [options] 28 | 29 | [Options]: 30 | -v ,--version show version 31 | 32 | -h,--help show help 33 | 34 | -D,--database check DataBase type,default is mysql 35 | support Database list: ''' + DBnames + ''' 36 | 37 | -H,--host host,Default:127.0.0.1 38 | if host is not 127.0.0.1 or localhost only check command 39 | 40 | -P,--database-port database port,Default:Database Default port 41 | it will set by check script 42 | 43 | -u,--database-user database rootuser,default:root 44 | 45 | -p,--database-password database password,default:root 46 | 47 | ''' 48 | 49 | plog = loghandle.getLogEntity() 50 | 51 | plog.output(bannber, "INFO", showtime=False, showlevel=False) 52 | 53 | runconfig = { 54 | "database": "", 55 | "host": "", 56 | "database_port": "", 57 | "database_user": "", 58 | "database_password": "" 59 | } 60 | 61 | try: 62 | opts, args = getopt.getopt(sys.argv[1:], "vhD:H:P:u:p:", 63 | ["version", "help", "database=", "host=", "database-port=", "database-user=", 64 | "database-password="]) 65 | 66 | checkscript = "" 67 | if len(opts) == 0: 68 | print small_helper 69 | exit() 70 | 71 | for o, a in opts: 72 | if o in ("-v", "--version"): 73 | print("DB_BASELINE : " + db_baseline_basic.getVersion()) 74 | sys.exit() 75 | elif o in ("-h", "--help"): 76 | print helper 77 | sys.exit() 78 | elif o in ("-D", "--database"): 79 | runconfig["database"] = a 80 | elif o in ("-H", "--host"): 81 | runconfig["host"] = a 82 | elif o in ("-P", "--database-port"): 83 | runconfig["database_port"] = a 84 | elif o in ("-U", "--database-user"): 85 | runconfig["database_user"] = a 86 | elif o in ("-p", "--database-password"): 87 | runconfig["database_password"] = a 88 | 89 | if runconfig["database"] == "mysql": 90 | checkscript = mysql_baseline(runconfig) 91 | 92 | if checkscript != "": 93 | result = checkscript.runtest() 94 | 95 | else: 96 | plog.output("No match DataBase Type","ERROR") 97 | print small_helper 98 | plog.output("DBBaseline exit()") 99 | except getopt.GetoptError: 100 | print helper 101 | -------------------------------------------------------------------------------- /doc/mysql.md: -------------------------------------------------------------------------------- 1 | # Mysql 基线检查项 2 | 3 | - 账号权限基线检查 run_power_test 4 | - 启动 mysql 的系统账号 是否单独创建 且 不允许登陆 5 | - 默认管理员账号是否存在 6 | - 高级权限账号 是否是必须 7 | - 系统数据库MYSQL的高级权限账号 是否必须 8 | - 具有特定的高级权限账号是否必须 9 | - File_priv 文件权限 10 | - Process_priv 进程权限 11 | - Super_priv 委托权限 12 | - Shutdown_priv 关闭权限 13 | - Create_user_priv 创建用户权限 14 | - Grant_priv 赋权权限 15 | - reload_priv 重载权限 16 | - repl_slave_priv 主从数据库权限 17 | - 密码为空的账号是否存在 18 | - 不受IP限制的账号可登录 19 | - 空用户的账号 20 | - 网络连接基线检查 run_network_test 21 | - 默认端口 是否修改 22 | - 网络连接方式 是否为 SSL 23 | - 文件安全基线检查 run_file_test 24 | - 数据库文件路径 show variables where variable_name = 'datadir' 25 | - 检查MYSQL命令执行历史记录 ~/.mysql_history 26 | - 敏感的日志,查询,错误,审计文件 27 | - log_bin_basename 28 | - log_error 29 | - slow_query_log_file 30 | - general_log_file 31 | - audit_log_file 32 | - relay_log_basename 33 | - 数据库配置基线检查 run_config_test 34 | - 错误日志是否开启 SHOW variables LIKE 'log_error'; 35 | - 错误日志等级设置 SHOW GLOBAL VARIABLES LIKE 'log_warnings'; 36 | - 测试数据库是否存在 SHOW DATABASES LIKE 'test'; 37 | - 是否可以读取本地文件 SHOW VARIABLES WHERE Variable_name = 'local_infile'; 38 | - 是否可以访问数据库目录以外的文件 have_symlink 39 | - 是否存在 daemon_memcached 插件 40 | - 数据库模式 sql_mode 是否包含 STRICT_TRANS_TABLES 41 | - 过期账号是否可以登录 disconnect_on_expired_password 42 | 43 | 44 | -------------------------------------------------------------------------------- /image/1524419076653.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wstart/DB_BaseLine/fb54b4faa199dfdf3539d8b7c855684e850cbee5/image/1524419076653.jpg -------------------------------------------------------------------------------- /image/3041524440631_.pic_hd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wstart/DB_BaseLine/fb54b4faa199dfdf3539d8b7c855684e850cbee5/image/3041524440631_.pic_hd.jpg -------------------------------------------------------------------------------- /log/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wstart/DB_BaseLine/fb54b4faa199dfdf3539d8b7c855684e850cbee5/log/.DS_Store -------------------------------------------------------------------------------- /loghandle.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import platform 3 | import datetime 4 | import pickle 5 | import os 6 | 7 | 8 | class loghandle(): 9 | _logo_entity = "" 10 | 11 | def __init__(self): 12 | self.BLACK, self.RED, self.GREEN, self.YELLOW, self.BLUE, self.MAGENTA, self.CYAN, self.WHITE = range(8) 13 | self.COLORS = { 14 | 'WARNING': self.YELLOW, 15 | 'INFO': self.GREEN, 16 | 'DEBUG': self.BLUE, 17 | 'CRITICAL': self.RED, 18 | 'ERROR': self.RED 19 | } 20 | self.RESET_SEQ = "\033[0m" 21 | self.COLOR_SEQ = "\033[1;%dm" 22 | self.BOLD_SEQ = "\033[1m" 23 | self.level = "INFO" 24 | 25 | # datefmt = '%a, %d %b %Y %H:%M:%S' 26 | 27 | self.logger = logging.getLogger("mylogger") 28 | self.console = logging.StreamHandler() 29 | self.logger.addHandler(self.console) 30 | #self.logger.setLevel(logging.INFO) 31 | self.logger.setLevel(logging.DEBUG) 32 | 33 | 34 | @staticmethod 35 | def getLogEntity(): 36 | if loghandle._logo_entity == "": 37 | loghandle._logo_entity = loghandle() 38 | 39 | return loghandle._logo_entity 40 | 41 | def output(self, msg, level="INFO", showtime=True, showlevel=True, showcolor=True): 42 | formatter = '%(message)s' 43 | if showlevel: 44 | formatter = '[%(levelname)s] ' + formatter 45 | if showtime: 46 | nowtime = datetime.datetime.now().strftime('%H:%M:%S') 47 | formatter = '[' + nowtime + '] ' + formatter 48 | 49 | if showcolor and platform.system() != "Windows": 50 | formatter = self.COLOR_SEQ % ( 51 | 30 + self.COLORS[level]) + formatter + self.RESET_SEQ 52 | 53 | formatter = logging.Formatter(formatter) 54 | self.console.setFormatter(formatter) 55 | 56 | if level == "WARNING": 57 | self.logger.warning(msg) 58 | elif level == "INFO": 59 | self.logger.info(msg) 60 | elif level == "DEBUG": 61 | self.logger.debug(msg) 62 | elif level == "CRITICAL": 63 | self.logger.critical(msg) 64 | elif level == "ERROR": 65 | self.logger.error(msg) 66 | 67 | 68 | if __name__ == "__main__": 69 | plog = loghandle.getLogEntity() 70 | plog = loghandle.getLogEntity() 71 | plog = loghandle.getLogEntity() 72 | plog = loghandle.getLogEntity() 73 | plog = loghandle.getLogEntity() 74 | plog = loghandle.getLogEntity() 75 | 76 | plog.output("aa11a", level="WARNING", showtime=False) 77 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pymongo==3.6.0 2 | PyMySQL==0.8.0 3 | -------------------------------------------------------------------------------- /script/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wstart/DB_BaseLine/fb54b4faa199dfdf3539d8b7c855684e850cbee5/script/__init__.py -------------------------------------------------------------------------------- /script/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wstart/DB_BaseLine/fb54b4faa199dfdf3539d8b7c855684e850cbee5/script/__init__.pyc -------------------------------------------------------------------------------- /script/db_baseline_basic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | reload(sys) 4 | sys.setdefaultencoding('utf-8') 5 | 6 | import json 7 | import hashlib 8 | import time 9 | import platform 10 | from abc import ABCMeta, abstractmethod 11 | from loghandle import * 12 | import platform 13 | 14 | 15 | class db_baseline_basic(object): 16 | @staticmethod 17 | def getOption(): 18 | return {} 19 | 20 | ##扫描基础类 21 | def __init__(self,runconfig): 22 | self.name = "DB_SECBaseLine" 23 | self.info = ''' 24 | [!] legal disclaimer: Usage of '''+self.name +''' for attacking targets withoutprior mutual consent is illegal.It is the end user's responsibilityto obey all applicable local,state and federal laws. Developersassume no liability and are notresponsible for any misuse or damagecaused by this program''' 25 | self.system = platform.system() 26 | self.path = platform.system() 27 | self.runconfig= runconfig #连接参数 28 | self.db = "" #数据库实例 29 | self.plog = loghandle.getLogEntity() 30 | self.result = { 31 | 'DBInfo': {}, # 数据库信息 32 | 'VerifyTime': {}, # 检查时间 33 | 'Score': 100, # 检查得分 34 | 'Desc': "", # 处理建议 35 | 'Defect': [] # 缺陷列表 36 | } 37 | 38 | # 用于连接数据库 39 | @abstractmethod 40 | def connect(self): 41 | pass 42 | 43 | # 返回当前版本 44 | @staticmethod 45 | def getVersion(): 46 | return "0.1" 47 | 48 | # 用于检查是否满足运行条件 49 | @abstractmethod 50 | def check(self): 51 | pass 52 | 53 | 54 | def getFilepower(self,path): 55 | try: 56 | pathdir = os.path.dirname(os.path.abspath(path)) 57 | basename = os.path.basename(os.path.abspath(path)) 58 | command = 'ls -al ' + pathdir+"/ 2>/dev/null | egrep '.*? "+basename+"$' 2>/dev/null | awk '{print $1}'" 59 | checkresult = os.popen(command).readlines() 60 | return checkresult[0].strip("\n") 61 | except Exception,e: 62 | return "" 63 | 64 | def runtest(self): 65 | print self.info 66 | self.result["VerifyTime"] = datetime.datetime.now().strftime('%H:%M:%S') 67 | self.result["endVerifyTime"] = '' 68 | 69 | print ''' 70 | [*] starting at '''+self.result["VerifyTime"]+''' 71 | ''' 72 | self.plog.output("testing connection to the target") 73 | if self.connect(): 74 | self.plog.output("Connect target success") 75 | checkenvir = self.check() 76 | self.plog.output("testing if the target envirment is suitable") 77 | 78 | if checkenvir: 79 | self.plog.output("target envirment is suitable") 80 | 81 | if platform.system() == "Windows": 82 | self.plog.output("Windows platfrom only run command teste", "WARNING") 83 | else: 84 | self.plog.output("testing power baseline") 85 | self.run_power_test() 86 | self.run_network_test() 87 | self.run_file_test() 88 | self.run_config_test() 89 | 90 | 91 | self.close() 92 | self.result["endVerifyTime"] = datetime.datetime.now().strftime('%H:%M:%S') 93 | 94 | for d1 in self.result['Defect']: 95 | if d1["Level"] == '低': 96 | self.result['Score'] = self.result['Score']-5 97 | elif d1["Level"] == '中': 98 | self.result['Score'] = self.result['Score'] - 10 99 | else: 100 | self.result['Score'] = self.result['Score'] - 30 101 | 102 | if self.result['Score'] == 100 : 103 | self.result['Desc'] = '系统状态优,无明显隐患' 104 | elif self.result['Score'] > 70 : 105 | self.result['Desc'] ='系统状态良好,存在微小隐患' 106 | elif self.result['Score'] > 60 : 107 | self.result['Desc'] ='系统状态差,存在许多隐患' 108 | else: 109 | self.result['Desc'] = '系统状态十分危险,存在众多隐患' 110 | md5_C = hashlib.md5() 111 | md5_C.update(str(time.time())) 112 | 113 | print '''DB_Baseline check result : 114 | ---''' 115 | print 'Score:'+str(self.result['Score']) 116 | print 'Desc:'+str(self.result['Desc']) 117 | print 'Defect :' 118 | for r_item in self.result['Defect']: 119 | print ' Defect:'+r_item['Desc'] 120 | print ' Level:'+r_item['Level'] 121 | print ' Suggest:'+r_item['Suggest'] 122 | 123 | print '''---''' 124 | print ''' 125 | [*] test result save -> ./log/'''+ str(md5_C.hexdigest() ) +'''.log''' 126 | with open("./log/"+ str(md5_C.hexdigest() ) +".log","w") as f: 127 | f.writelines(json.dumps(self.result)) 128 | 129 | 130 | print ''' 131 | [*] shutting down at '''+self.result["endVerifyTime"]+''' 132 | ''' 133 | return self.result 134 | 135 | 136 | # 关闭连接 137 | @abstractmethod 138 | def close(self): 139 | pass 140 | 141 | # 账号权限基线检查 142 | @abstractmethod 143 | def run_power_test(self): 144 | pass 145 | 146 | # 网络连接基线检查 147 | @abstractmethod 148 | def run_network_test(self): 149 | pass 150 | 151 | # 文件安全基线检查 152 | @abstractmethod 153 | def run_file_test(self): 154 | pass 155 | 156 | # 数据库配置基线检查 157 | @abstractmethod 158 | def run_config_test(self): 159 | pass 160 | -------------------------------------------------------------------------------- /script/example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | 4 | reload(sys) 5 | sys.setdefaultencoding('utf-8') 6 | 7 | import db_baseline_basic 8 | 9 | 10 | class example_baseline(db_baseline_basic): 11 | def connect(self): pass 12 | 13 | # 用于检查是否满足运行条件 14 | 15 | def check(self): pass 16 | 17 | # 开始基线检查 18 | 19 | def runtest(self): pass 20 | 21 | # 文件权限基线检查 22 | 23 | def run_file_test(self): pass 24 | 25 | # 网络基线检查 26 | 27 | def run_net_test(self): pass 28 | 29 | # 权限基线检查 30 | 31 | def run_power_test(self): pass 32 | 33 | # 配置基线检查 34 | 35 | def run_setting_test(self): pass 36 | 37 | 38 | # 数据文件位置检查 39 | #sql = "show variables where variable_name = 'datadir';" 40 | #result = self.getOneLine(sql) 41 | #checkresult = os.popen('df -h ' + result[1]).readlines() 42 | #if "/dev/disk1s1" in checkresult[1]: 43 | # self.result["Defect"].append( 44 | # { 45 | # 'Desc': '文件存放在系统目录容易损坏', # 描述 46 | # 'Level': '低', # 危害等级 47 | # 'Suggest': '修改配置文件中datadir的值', # 修复建议 48 | # } 49 | # ) 50 | -------------------------------------------------------------------------------- /script/mysql_baseline.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | reload(sys) 6 | sys.setdefaultencoding('utf-8') 7 | 8 | from db_baseline_basic import * 9 | import pymysql 10 | 11 | 12 | class mysql_baseline(db_baseline_basic): 13 | def connect(self): 14 | 15 | # print self.runconfig 16 | host = self.runconfig["host"] 17 | database_port = self.runconfig["database_port"] 18 | database_user = self.runconfig["database_user"] 19 | database_password = self.runconfig["database_password"] 20 | if host == "": 21 | host = '127.0.0.1' 22 | if database_port == "" or database_port == 0: 23 | database_port = 3306 24 | 25 | else: 26 | database_port = int(database_port) 27 | if database_user == "": 28 | database_user = "root" 29 | if database_password == "": 30 | database_password = "" 31 | try: 32 | self.db = pymysql.connect(host=host, user=database_user, passwd=database_password, port=database_port) 33 | self.cursor = self.db.cursor() 34 | return True 35 | except Exception, e: 36 | self.plog.output(e, "CRITICAL") 37 | return False 38 | 39 | def check(self): 40 | return True 41 | 42 | # 账号权限基线检查 43 | def run_power_test(self): 44 | # 启动 mysql 的系统账号 是否单独创建 且 不允许登陆 45 | checkresult = os.popen(' cat /etc/passwd | grep mysql ').readlines() 46 | if len(checkresult) != 0: 47 | checkresult = os.popen(' cat /etc/passwd | grep mysql | grep /usr/bin/false').readlines() 48 | if len(checkresult) == 0: 49 | checkresult = os.popen(' cat /etc/passwd | grep mysql | grep /usr/sbin/nologin').readlines() 50 | if len(checkresult) == 0: 51 | self.plog.output("run mysql system account can login ", "ERROR") 52 | self.result["Defect"].append( 53 | { 54 | 'Desc': '运行mysql的用户可以登陆', # 描述 55 | 'Level': '中', # 危害等级 56 | 'Suggest': '禁止运行mysql的用户登陆,添加/usr/bin/false 或 /usr/sbin/nologin', # 修复建议 57 | } 58 | ) 59 | else: 60 | self.plog.output("Run mysql account not found ", "WARNING") 61 | self.result["Defect"].append( 62 | { 63 | 'Desc': '没有找到运行mysql程序的系统用户', # 描述 64 | 'Level': '低', # 危害等级 65 | 'Suggest': '单独为mysql执行添加系统用户', # 修复建议 66 | } 67 | ) 68 | self.plog.output("run mysql account check finish ") 69 | 70 | #检查默认管理员账号 71 | sql = "SELECT * from MySQL.user where user='root';" 72 | result = self.getOneLine(sql) 73 | if len(result) > 0: 74 | self.plog.output("default user root exits", "ERROR") 75 | self.result["Defect"].append( 76 | { 77 | 'Desc': '默认管理员账号存在', # 描述 78 | 'Level': '低', # 危害等级 79 | 'Suggest': '修改名字或者删除', # 修复建议 80 | } 81 | ) 82 | self.plog.output("default user root check finish") 83 | 84 | 85 | #高级权限账号 是否 为管理员账号 86 | sql="SELECT user, host FROM mysql.user WHERE (Select_priv = 'Y') OR (Insert_priv = 'Y') OR (Update_priv = 'Y') OR (Delete_priv = 'Y') OR (Create_priv = 'Y') OR (Drop_priv = 'Y'); " 87 | result = self.getAllLine(sql) 88 | if len(result) > 0: 89 | self.plog.output("Please make sure this high level account is root:","ERROR") 90 | root_account = [] 91 | for r_item in result: 92 | root_account_string = r_item[0]+","+r_item[1] 93 | self.plog.output( root_account_string,"ERROR",showlevel=False,showtime=False) 94 | root_account.append(root_account_string) 95 | self.result["Defect"].append( 96 | { 97 | 'Desc': '确保以下的账号都是管理员账号'+ "\n".join( root_account), # 描述 98 | 'Level': '低', # 危害等级 99 | 'Suggest': '删除非管理员账号', # 修复建议 100 | } 101 | ) 102 | 103 | 104 | 105 | #系统数据库的高级权限账号是否必须 106 | sql = "SELECT user, host FROM mysql.db WHERE db = 'MySQL' AND ((Select_priv = 'Y') OR (Insert_priv = 'Y') OR (Update_priv = 'Y') OR (Delete_priv = 'Y') OR (Create_priv = 'Y') OR (Drop_priv = 'Y'));" 107 | result = self.getAllLine(sql) 108 | if len(result) > 0: 109 | self.plog.output("Please make sure this high level account is root:","ERROR") 110 | root_account = [] 111 | for r_item in result: 112 | root_account_string = r_item[0]+","+r_item[1] 113 | self.plog.output( root_account_string,"ERROR",showlevel=False,showtime=False) 114 | root_account.append(root_account_string) 115 | self.result["Defect"].append( 116 | { 117 | 'Desc': '确保以下的账号都是管理员账号'+ "\n".join( root_account), # 描述 118 | 'Level': '低', # 危害等级 119 | 'Suggest': '删除非管理员账号', # 修复建议 120 | } 121 | ) 122 | self.plog.output("test high level user finish") 123 | 124 | #具有某个高级权限账号 是否必须 125 | high_level_priv = [] 126 | high_level_priv.append({"name": "File_priv", "desc": "文件权限"}) 127 | high_level_priv.append({"name": "Process_priv", "desc": "进程权限"}) 128 | high_level_priv.append({"name": "Super_priv", "desc": "委托权限"}) 129 | high_level_priv.append({"name": "Shutdown_priv", "desc": "关闭权限"}) 130 | high_level_priv.append({"name": "Create_user_priv", "desc": "创建用户权限"}) 131 | high_level_priv.append({"name": "Grant_priv", "desc": "赋权权限"}) 132 | high_level_priv.append({"name": "reload_priv", "desc": "重载权限"}) 133 | high_level_priv.append({"name": "repl_slave_priv", "desc": "主从数据库权限"}) 134 | 135 | for h_item in high_level_priv: 136 | sql = "select user, host from mysql.user where " + h_item["name"] + " = 'Y' " 137 | result = self.getAllLine(sql) 138 | if len(result) > 0: 139 | self.plog.output("Please make sure this high level " + h_item["name"] + " account is root:", "ERROR") 140 | root_account = [] 141 | for r_item in result: 142 | root_account_string = r_item[0] + "," + r_item[1] 143 | self.plog.output(root_account_string, "ERROR", showlevel=False, showtime=False) 144 | root_account.append(root_account_string) 145 | self.result["Defect"].append( 146 | { 147 | 'Desc': '以下的账号拥有' + h_item["desc"] + ',请确保是管理员账号' + "\n".join(root_account), # 描述 148 | 'Level': '低', # 危害等级 149 | 'Suggest': '删除非管理员账号', # 修复建议 150 | } 151 | ) 152 | self.plog.output("test high level " + h_item["name"] + " user finish") 153 | 154 | #密码为空的账号是否存在 155 | sql ="select * from INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = 'mysql' and TABLE_NAME='user' and COLUMN_NAME='authentication_string' limit 1;" 156 | result = self.getOneLine(sql) 157 | if result > 0: 158 | sql = "SELECT User,host FROM mysql.user WHERE authentication_string='';" 159 | else : 160 | sql = "SELECT User,host FROM mysql.user WHERE password=''; ;" 161 | 162 | result = self.getAllLine(sql) 163 | if len(result) > 0: 164 | self.plog.output(" Null password user exits ", "ERROR") 165 | root_account = [] 166 | for r_item in result: 167 | root_account_string = r_item[0] + "," + r_item[1] 168 | self.plog.output(root_account_string, "ERROR", showlevel=False, showtime=False) 169 | root_account.append(root_account_string) 170 | 171 | self.result["Defect"].append( 172 | { 173 | 'Desc': '存在空账号' + "\n".join(root_account), # 描述 174 | 'Level': '低', # 危害等级 175 | 'Suggest': '为空密码账号添加密码', # 修复建议 176 | } 177 | ) 178 | self.plog.output("test Null password user finish") 179 | 180 | 181 | #不受IP限制的账号 182 | sql = "SELECT user, host FROM mysql.user WHERE host = '%';" 183 | result = self.getAllLine(sql) 184 | if len(result) > 0: 185 | self.plog.output("all ip access account exits ", "ERROR") 186 | root_account = [] 187 | for r_item in result: 188 | root_account_string = r_item[0] + "," + r_item[1] 189 | self.plog.output(root_account_string, "ERROR", showlevel=False, showtime=False) 190 | root_account.append(root_account_string) 191 | 192 | self.result["Defect"].append( 193 | { 194 | 'Desc': '存在允许所有IP访问的账号' + "\n".join(root_account), # 描述 195 | 'Level': '低', # 危害等级 196 | 'Suggest': '限制IP访问去掉%', # 修复建议 197 | } 198 | ) 199 | self.plog.output("test all ip access account finish") 200 | 201 | #空用户账号 202 | sql ="SELECT user,host FROM mysql.user WHERE user = '';" 203 | result = self.getAllLine(sql) 204 | if len(result) > 0: 205 | self.plog.output("null name account exits ", "ERROR") 206 | root_account = [] 207 | for r_item in result: 208 | root_account_string = r_item[0] + "," + r_item[1] 209 | self.plog.output(root_account_string, "ERROR", showlevel=False, showtime=False) 210 | root_account.append(root_account_string) 211 | 212 | self.result["Defect"].append( 213 | { 214 | 'Desc': '存在空用户名的账号号' + "\n".join(root_account), # 描述 215 | 'Level': '低', # 危害等级 216 | 'Suggest': '删除空用户名账号', # 修复建议 217 | } 218 | ) 219 | self.plog.output("test null name account finish") 220 | 221 | 222 | # 网络连接基线检查 223 | def run_network_test(self): 224 | sql = "show global variables like 'port';" 225 | result = self.getOneLine(sql) 226 | 227 | if len(result) > 0 and result[1] == '3306': 228 | self.plog.output("default port now is " + result[1]+" not safe ","ERROR") 229 | self.result["Defect"].append( 230 | { 231 | 'Desc': ' 默认端口没有修改', # 描述 232 | 'Level': '低', # 危害等级 233 | 'Suggest': '修改肥3306端口', # 修复建议 234 | } 235 | ) 236 | self.plog.output("test default port check finish") 237 | 238 | 239 | sql = "SHOW variables WHERE variable_name = 'have_ssl';" 240 | result = self.getOneLine(sql) 241 | if len(result) > 0 and result[1] != 'yes' : 242 | self.plog.output("SSL is not set , now is " + result[1]+" not safe ","ERROR") 243 | self.result["Defect"].append( 244 | { 245 | 'Desc': ' 网络请求未走 SSL 连接', # 描述 246 | 'Level': '低', # 危害等级 247 | 'Suggest': '开启 ssh', # 修复建议 248 | } 249 | ) 250 | self.plog.output("test SSL CONNECT check finish") 251 | pass 252 | 253 | # 文件安全基线检查 254 | def run_file_test(self): 255 | # 数据文件位置检查 256 | sql = "show variables where variable_name = 'datadir';" 257 | result = self.getOneLine(sql) 258 | checkresult = os.popen('df -h ' + result[1]).readlines() 259 | if "/var/usr" in checkresult[1]: 260 | self.plog.output("DataBase file path not safe", "ERROR") 261 | self.result["Defect"].append( 262 | { 263 | 'Desc': '数据文件存放在系统目录容易损坏', # 描述 264 | 'Level': '低', # 危害等级 265 | 'Suggest': '修改配置文件中datadir的值', # 修复建议 266 | } 267 | ) 268 | self.plog.output("DataBase file path check finish") 269 | 270 | checkresult = self.getFilepower(result[1]) 271 | if checkresult != "": 272 | if "drwx------" != checkresult: 273 | self.plog.output("DataBase file Permissions error ,should be 700,now is " + checkresult+"", "ERROR") 274 | self.result["Defect"].append( 275 | { 276 | 'Desc': '文件存放在系统目录容易损坏', # 描述 277 | 'Level': '低', # 危害等级 278 | 'Suggest': '修改配置文件中datadir的值', # 修复建议 279 | } 280 | ) 281 | else: 282 | self.plog.output("Access " + result[1] + " fail,please confirm Permissions is 700", "WARNING") 283 | self.plog.output("DataBase file Permissions check finish") 284 | 285 | 286 | # 检查MYSQL命令执行历史记录 287 | checkresult = os.popen('find ~/.mysql_history 2>/dev/null').readlines() 288 | if len(checkresult) > 0: 289 | self.plog.output(".mysql_history exits ", "ERROR") 290 | self.result["Defect"].append( 291 | { 292 | 'Desc': '存在MYSQL命令执行历史记录', # 描述 293 | 'Level': '中', # 危害等级 294 | 'Suggest': '存在.mysql_history文件且禁止生成', # 修复建议 295 | } 296 | ) 297 | 298 | # 敏感日志文件权限检查 299 | dangerfile_list = [] 300 | dangerfile_list.append({"name": "log_bin_basename", "desc": "日志二进制文件 用于恢复和备份数据库"}) 301 | dangerfile_list.append({"name": "log_error", "desc": "错误日志文件"}) 302 | dangerfile_list.append({"name": "slow_query_log_file", "desc": "慢查询文件"}) 303 | dangerfile_list.append({"name": "general_log_file", "desc": "通用日志文件"}) 304 | dangerfile_list.append({"name": "audit_log_file", "desc": "审计日志文件"}) 305 | dangerfile_list.append({"name": "relay_log_basename", "desc": "中转日志文件的名称和路径"}) 306 | 307 | for file_item in dangerfile_list: 308 | sql = "show variables where variable_name = '" + file_item["name"] + "';" 309 | result = self.getOneLine(sql) 310 | #print result 311 | if result == None or len(result) == 0 or result[1] == "": 312 | self.plog.output(file_item["name"] + " is empty and can not locat ", "ERROR") 313 | else: 314 | filepath = result[1] 315 | file_right = self.getFilepower(filepath) 316 | if file_right == "": 317 | self.plog.output("Access "+file_item['name'] +'->'+ filepath + " fail,please confirm Permissions is 600", 318 | "WARNING") 319 | else: 320 | if file_right != "-rwx------": 321 | self.plog.output("DATABASE FILE " + filepath + " should be 700,now is " + result[1], "ERROR") 322 | self.result["Defect"].append( 323 | { 324 | 'Desc': file_item["name"] + '文件权限有误', # 描述 325 | 'Level': '低', # 危害等级 326 | 'Suggest': '修改' + file_item["name"] + '文件权限的值为600', # 修复建议 327 | } 328 | ) 329 | self.plog.output(file_item["name"] + " check finish") 330 | pass 331 | 332 | 333 | # 数据库配置基线检查 334 | def run_config_test(self): 335 | sql = "SHOW variables LIKE 'log_error';" 336 | result = self.getOneLine(sql) 337 | if len(result ) <=0 or result[1] == '': 338 | self.plog.output("Error log is close ", "ERROR") 339 | self.result["Defect"].append( 340 | { 341 | 'Desc': '错误日志没有打', # 描述 342 | 'Level': '低', # 危害等级 343 | 'Suggest': '配置错误日志', # 修复建议 344 | } 345 | ) 346 | self.plog.output("test error log finish") 347 | 348 | 349 | sql ="SHOW GLOBAL VARIABLES LIKE 'log_warnings';" 350 | result = self.getOneLine(sql) 351 | if len(result ) <=0 or result[1] != '2': 352 | self.plog.output(" log_warings level shouble 2 now is "+result[1], "ERROR") 353 | self.result["Defect"].append( 354 | { 355 | 'Desc': '错误日志等级没有设置恰当', # 描述 356 | 'Level': '低', # 危害等级 357 | 'Suggest': '配置 log_warnings 为 2', # 修复建议 358 | } 359 | ) 360 | self.plog.output("test error log level finish") 361 | 362 | sql = "SHOW DATABASES LIKE 'test';" 363 | result = self.getOneLine(sql) 364 | if len(result) > 0: 365 | self.plog.output("test DATABASE exits","ERROR") 366 | self.result["Defect"].append( 367 | { 368 | 'Desc': '测试数据库存在', # 描述 369 | 'Level': '低', # 危害等级 370 | 'Suggest': '尽快删除', # 修复建议 371 | } 372 | ) 373 | self.plog.output("test DATABASE check finish") 374 | 375 | 376 | sql = "SHOW VARIABLES WHERE Variable_name = 'local_infile';" 377 | result = self.getOneLine(sql) 378 | if len(result) > 0 and result[1] != "OFF": 379 | self.plog.output("local_infile is "+result[1],"ERROR") 380 | self.result["Defect"].append( 381 | { 382 | 'Desc': 'local_infile 可以 ', # 描述 383 | 'Level': '低', # 危害等级 384 | 'Suggest': '在mysql配置文件中新增一行:Local-infile=0;重启mysql', # 修复建议 385 | } 386 | ) 387 | self.plog.output("test local_infile check finish") 388 | 389 | 390 | sql = "SHOW variables LIKE 'have_symlink';" 391 | result = self.getOneLine(sql) 392 | if len(result) > 0 and result[1] != "YES": 393 | self.plog.output(" skip-symbolic-links is "+result[1],"ERROR") 394 | self.result["Defect"].append( 395 | { 396 | 'Desc': ' skip-symbolic-links 开启可以防止数据库用户控制数据文件目录之外的文件。', # 描述 397 | 'Level': '中', # 危害等级 398 | 'Suggest': '在mysql配置文件中配置起启动', # 修复建议 399 | } 400 | ) 401 | self.plog.output("test skip-symbolic-links check finish") 402 | 403 | sql = "SELECT * FROM information_schema.plugins WHERE PLUGIN_NAME='daemon_memcached'" 404 | result = self.getOneLine(sql) 405 | if len(result) > 0 : 406 | self.plog.output(" information_schema.plugin daemon_memcached is On ","ERROR") 407 | self.result["Defect"].append( 408 | { 409 | 'Desc': ' daemon_memcached 插件会导致数据泄漏。', # 描述 410 | 'Level': '中', # 危害等级 411 | 'Suggest': '执行 uninstall plugin daemon_memcached;', # 修复建议 412 | } 413 | ) 414 | self.plog.output("test daemon_memcached check finish") 415 | 416 | sql = "SHOW GLOBAL VARIABLES WHERE Variable_name = 'secure_file_priv' AND Value<>'';" 417 | result = self.getOneLine(sql) 418 | if len(result) <= 0 : 419 | self.plog.output(" secure_file_priv is off ","ERROR") 420 | self.result["Defect"].append( 421 | { 422 | 'Desc': ' secure_file_priv 限制客户端可以读取数据文件的路径。', # 描述 423 | 'Level': '中', # 危害等级 424 | 'Suggest': 'mysql配置文件中 添加 secure_file_priv=>;', # 修复建议 425 | } 426 | ) 427 | self.plog.output("test secure_file_priv check finish") 428 | 429 | 430 | sql = "SHOW VARIABLES LIKE 'sql_mode';" 431 | result = self.getOneLine(sql) 432 | if len(result) > 0 and 'STRICT_TRANS_TABLES' not in result[1] : 433 | self.plog.output(" sql_mode is " +result[1]+" not safe ","ERROR") 434 | self.result["Defect"].append( 435 | { 436 | 'Desc': ' sql_mode 需要设置为 STRICT_ALL_TABLES ', # 描述 437 | 'Level': '低', # 危害等级 438 | 'Suggest': '配置文件添加 sql_mode=STRICT_ALL_TABLES', # 修复建议 439 | } 440 | ) 441 | self.plog.output("test sql_mode check finish") 442 | 443 | 444 | sql = "SHOW GLOBAL VARIABLES like 'disconnect_on_expired_password';" 445 | result = self.getOneLine(sql) 446 | if len(result) > 0 and result[1] != 'ON' : 447 | self.plog.output("disconnect_on_expired_password is " +result[1]+" not safe ","ERROR") 448 | self.result["Defect"].append( 449 | { 450 | 'Desc': ' disconnect_on_expired_password 会导致过期账号依旧可以登陆数据库 ', # 描述 451 | 'Level': '低', # 危害等级 452 | 'Suggest': '配置文件添加 disconnect_on_expired_password =ON', # 修复建议 453 | } 454 | ) 455 | self.plog.output("test disconnect_on_expired_password check finish") 456 | 457 | pass 458 | 459 | def close(self): 460 | self.cursor.close() 461 | self.db.close() 462 | 463 | # DB helper func 464 | def getOneLine(self, sql): 465 | self.cursor.execute(sql) 466 | onedata = self.cursor.fetchone() 467 | if onedata == None: 468 | onedata = [] 469 | return onedata 470 | 471 | def getAllLine(self,sql): 472 | self.cursor.execute(sql) 473 | onedata = self.cursor.fetchall() 474 | return onedata 475 | 476 | if __name__ == "__main__": 477 | mysq_test = mysql_baseline() 478 | mysq_test.runtest() 479 | -------------------------------------------------------------------------------- /script/mysql_baseline.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wstart/DB_BaseLine/fb54b4faa199dfdf3539d8b7c855684e850cbee5/script/mysql_baseline.pyc --------------------------------------------------------------------------------