分类 MySQL 下的文章

LNMP服务器安全指南

平时工作中需要维护一些Linux服务器, 有时候也需要自己配置下服务器, 一般主要是配置LNMP服务。整理了一些服务器安装配置方面的知识,现在就总结下。

  • Linux最低权限安全配置
    Nginx、MySQL、PHP-FPM三者都应该是以最低权限用户组运行。
    网站目录的文件用户组和PHP运行用户属主应该区分开, 设置PHP对网站文件只有可执行权限, 对于静态文件都交给Nginx处理。比如PHP-FPM的用户组为php-fpm:php-fpm, 网站目录www.lezhizhe.net其用户组为www:www, 文件属性为644,也就是说php合nginx在网站根目录只有读权限。对于文件上传目录和缓存目录, 可以设置777权限, 生成的文件要设置为644属性, 禁止上传的文件具有可执行权限。
    对于PHP使用open_basedir限制虚拟主机跨目录访问(网上有说在php.ini文件中修改open_basedir, 经测试不生效)。不过可以在nginx的每个站点的con.f文件中设置PHP的open_basedir属性(注意如果在某个站点设置了该属性其它站点未设置, 会造成其它站点打不开的情况):
fastcgi_param  PHP_VALUE  "open_basedir=$document_root:/tmp/";
  • Linux防火墙及端口配置
    1、禁止SSH密码登陆方式, 采用公钥方式登陆。
    2、开启iptables。只开放必要的端口出来。iptable配置如下:
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [46:7024]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth1 -j ACCEPT
-A INPUT -p tcp -m tcp --sport 53 -j ACCEPT //DNS端口
-A INPUT -p udp -m udp --sport 53 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT //HTTP端口
-A INPUT -p tcp -m state --state NEW -m tcp --dport 4000 -j ACCEPT //FTP端口
-A INPUT -p tcp -m state --state NEW -m tcp --dport 4355 -j ACCEPT //SSh端口
-A INPUT -p tcp -m state --state NEW -m tcp --dport 30000:60000 -j ACCEPT //FTP被动模式端口
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
  • PHP安装配置
    1.禁用不安全PHP函数
    disable_functions = show_source,system,shell_exec,passthru,exec,popen,proc_open,proc_get_status,phpinfo
    2.关闭错误日志防止输出错误信息, 记录log方便程序调试
    display_errors = Off
    log_errors=On
    error_log=/var/log/php-fpm/sie-error.log

    3.关闭远程代码执行。如果启用,PHP可以通过allow_url_fopen,在file_get_contents()、include、require中获取诸如FTP或网页内容这些远程数据。如果忘记了对输入数据进行过滤,而这些函数调用了这些数据,则形成了注入漏洞。
    allow_url_fopen=Off

    4.防止PHP信息泄露, 不轻易透露自己php版本信息,防止黑客针对这个版本的php发动攻击.
    expose_php = Off

    5.禁止动态加载链接库:
    enable_dl = Off

    6.将文件上传到远程服务器, 例如nfs等。当然也可以调用你们写好的php接口。 即使有上传漏洞,那么文件也被传到了静态服务器上。木马等文件根本无法执行。

  • Nginx限制访问
    有些目录存放的是一些类库文件或者模板文件, 不需要用户访问的,可以在该目录访问限制返回404。如lib类文件目录和template模板目录:
    location ~ ^/(lib|template)/ {
    return 404;
    }

    对于静态文件(如js、css、图片等)限制通过PHP执行:
    location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ {
    if (-f $request_filename) {
    expires 0d;
    break;
    }
    }

    对于PHP程序文件,一般都是很少的几个入口文件, 可以直接限制这几个PHP文件的执行。例如现在只有index.php、user.php、admin.php三个入口文件,那我们只把这三个PHP文件交给fast-cgi文件去执行。
    location ~ ^/(index|user|admin)\.php {
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    fastcgi_split_path_info ^(.+\.php)(/?.+)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    fastcgi_param PHP_VALUE "open_basedir=$document_root:/tmp/";
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9000;
    }

Pure-FTP安装配置

Pure-FTPd 是一款免费(BSD)的,安全的,高质量和符合标准的FTP服务器。 侧重于运行效率和易用性。 这篇文章介绍如何在CentOS下安装Pure-FTP服务, 并且使用MySQL存储虚拟FTP用户。

安装Pure-FTP

  • yum方式安装
yum -y install pure-ftpd
  • 编译安装方式,下载地址,为了方便起见,我在这里使用了几个基本的编译命令来配置编译一个全功能版本的程序
# ./configure --prefix=/usr/local/pure-ftpd/ --with-language=simplified-chinese --with-everything
# make && make check && make install
  • 设置Pure-FTP开机启动
chkconfig --add pure-ftpd
chkconfig --level 2345 pure-ftpd on
  • 配置Pure-FTP
ChrootEveryone              yes         # 启用chroot, 将每个用户限制在自己的home目录下
BrokenClientsCompatibility yes # 兼容不同客户端, 默认:no
Daemonize yes # 后台运行
MaxClientsNumber 20 # 最大用户连接数
MaxClientsPerIP 4 # 每个ip最大连接数
VerboseLog yes # 记录日志
DisplayDotFiles no # 显示隐藏文件
AnonymousOnly no # 只允许匿名用户访问
NoAnonymous yes # 不允许匿名用户连接
SyslogFacility none # 不将日志在syslog日志中显示
DontResolve yes # 不进行客户端DNS解析
MaxIdleTime 5 # 最大空闲时间
LimitRecursion 2048 16 # 浏览限制,文件2000,目录8层
AnonymousCanCreateDirs no # 匿名用户可以创建目录
MaxLoad 4 # 如果系统负载超过下面所给的数字,那么匿名用户将无法下载
PassivePortRange 45000 60000 # 被动模式端口范围
#AnonymousRatio 1 10 # 匿名用户上传/下载比率
#UserRatio 1 10 # 所有用户上传/下载比率
AntiWarez yes # 禁止下载匿名用户上传但未经验证的文件
#AnonymousBandwidth 200 # 匿名用户带宽限制(KB)
UserBandwidth 128 # 所有用户最大带宽(KB)
Umask 133:022 # 创建文件/目录默认掩码
MinUID 100 # 验证登录用户的最小UID
AllowUserFXP no # 仅运行用户进行FXP传输
AllowAnonymousFXP no # 对匿名用户和非匿名用户允许进行匿名 FXP 传输
ProhibitDotFilesWrite no # 不能删除/写入隐藏文件
ProhibitDotFilesRead no # 禁止读取隐藏文件
AutoRename no # 有同名文件时自动重新命名
AnonymousCantUpload yes # 不允许匿名用户上传文件
AltLog clf:/var/log/pureftpd.log # clf格式日志文件位
MySQLConfigFile /etc/pure-ftpd/pureftpd-mysql.conf # 用户数据库文
MaxDiskUsage 99 # 当磁盘使用量打到99%时禁止上传
CreateHomeDir yes # 如果虚拟用户的目录不存在则自动创建#需要ftp根目录权限为755 chmod 775 /data/ftpdata/
CustomerProof yes # 防止命令误操作

安装免费开源的Pure-ftp Web管理工具vftp

  • 下载地址
  • 上传到Web目录以后安装, 执行安装, 根据提示安装相应的PHP模块, 配置/etc/pure-ftpd/pureftpd-mysql.conf 文件即可。配置好以后就可以通过web界面新建FTP虚拟用户了
#php安装php-posix, 安装以后需要重启web服务器
yum -y install php-process

MySQL Replication(主从同步)基本原理

1、复制进程

Mysql的复制(replication)是一个异步的复制,从一个Mysql instace(称之为Master)复制到另一个Mysql instance(称之Slave)。实现整个复制操作主要由三个进程完成的,其中两个进程在Slave(Sql进程和IO进程),另外一个进程在 Master(IO进程)上。
要实施复制,首先必须打开Master端的binary log(bin-log)功能,否则无法实现。因为整个复制过程实际上就是Slave从Master端获取该日志然后再在自己身上完全顺序的执行日志中所记录的各种操作。

复制的基本过程如下:

1)、Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
2)、Master接收到来自Slave的IO进程的请求后,通过负责复制的IO进程根据请求信息读取指定日志指定位置之后的日志信息,返回给Slave 的IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置;
3)、Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的 bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的高速Master“我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我”;
4)、Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行。

实际上在老版本的Mysql的复制实现在Slave端并不是两个进程完成的,而是由一个进程完成。但是后来发现这样做存在较大的风险和性能问题,主要如下:
首先,一个进程就使复制bin-log日志和解析日志并在自身执行的过程成为一个串行的过程,性能受到了一定的限制,异步复制的延迟也会比较长。
另外,Slave端从Master端获取bin-log过来之后,需要接着解析日志内容,然后在自身执行。在这个过程中,Master端可能又产生了大量变化并声称了大量的日志。如果在这个阶段Master端的存储出现了无法修复的错误,那么在这个阶段所产生的所有变更都将永远无法找回。如果在Slave 端的压力比较大的时候,这个过程的时间可能会比较长。
所以,后面版本的Mysql为了解决这个风险并提高复制的性能,将Slave端的复制改为两个进程来完成。提出这个改进方案的人是Yahoo!的一位工程师“Jeremy Zawodny”。这样既解决了性能问题,又缩短了异步的延时时间,同时也减少了可能存在的数据丢失量。当然,即使是换成了现在这样两个线程处理以后,同样也还是存在slave数据延时以及数据丢失的可能性的,毕竟这个复制是异步的。只要数据的更改不是在一个事物中,这些问题都是会存在的。如果要完全避免这些问题,就只能用mysql的cluster来解决了。不过mysql的cluster是内存数据库的解决方案,需要将所有数据都load到内存中,这样就对内存的要求就非常大了,对于一般的应用来说可实施性不是太大。 2、复制实现级别

Mysql的复制可以是基于一条语句(Statement level),也可以是基于一条记录(Row level),可以在Mysql的配置参数中设定这个复制级别,不同复制级别的设置会影响到Master端的bin-log记录成不同的形式。
Row Level:日志中会记录成每一行数据被修改的形式,然后在slave端再对相同的数据进行修改。
优点:在row level模式下,bin-log中可以不记录执行的sql语句的上下文相关的信息,仅仅只需要记录那一条记录被修改了,修改成什么样了。所以row level的日志内容会非常清楚的记录下每一行数据修改的细节,非常容易理解。而且不会出现某些特定情况下的存储过程,或function,以及 trigger的调用和触发无法被正确复制的问题。
缺点:row level下,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如有这样一条update语句:update product set owner_member_id = ‘b’ where owner_member_id = ‘a’,执行之后,日志中记录的不是这条update语句所对应额事件(mysql以事件的形式来记录bin-log日志),而是这条语句所更新的每一条记录的变化情况,这样就记录成很多条记录被更新的很多个事件。自然,bin-log日志的量就会很大。尤其是当执行alter table之类的语句的时候,产生的日志量是惊人的。因为Mysql对于alter table之类的表结构变更语句的处理方式是整个表的每一条记录都需要变动,实际上就是重建了整个表。那么该表的每一条记录都会被记录到日志中。
Statement Level:每一条会修改数据的sql都会记录到 master的bin-log中。slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行。
优点:statement level下的优点首先就是解决了row level下的缺点,不需要记录每一行数据的变化,减少bin-log日志量,节约IO,提高性能。因为他只需要记录在Master上所执行的语句的细节,以及执行语句时候的上下文的信息。
缺点:由于他是记录的执行语句,所以,为了让这些语句在slave端也能正确执行,那么他还必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在slave端杯执行的时候能够得到和在master端执行时候相同的结果。另外就是,由于Mysql现在发展比较快,很多的新功能不断的加入,使mysql得复制遇到了不小的挑战,自然复制的时候涉及到越复杂的内容,bug也就越容易出现。在statement level下,目前已经发现的就有不少情况会造成mysql的复制出现问题,主要是修改数据的时候使用了某些特定的函数或者功能的时候会出现,比如:sleep()函数在有些版本中就不能真确复制,在存储过程中使用了last_insert_id()函数,可能会使slave和master上得到不一致的id等等。由于row level是基于每一行来记录的变化,所以不会出现类似的问题

从官方文档中看到,之前的Mysql一直都只有基于statement的复制模式,直到5.1.5版本的Mysql才开始支持row level的复制。从5.0开始,Mysql的复制已经解决了大量老版本中出现的无法正确复制的问题。但是由于存储过程的出现,给Mysql的复制又带来了更大的新挑战。另外,看到官方文档说,从5.1.8版本开始,Mysql提供了除Statement Level和Row Level之外的第三种复制模式:Mixed,实际上就是前两种模式的结合。在Mixed模式下,Mysql会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。新版本中的Statment level还是和以前一样,仅仅记录执行的语句。而新版本的Mysql中队row level模式也被做了优化,并不是所有的修改都会以row level来记录,像遇到表结构变更的时候就会以statement模式来记录,如果sql语句确实就是update或者delete等修改数据的语句,那么还是会记录所有行的变更。 3、复制常用架构

Mysql复制环境90%以上都是一个Master带一个或者多个Slave的架构模式,主要用于读压力比较大的应用的数据库端廉价扩展解决方案。因为只要master和slave的压力不是太大(尤其是slave端压力)的话,异步复制的延时一般都很少很少。尤其是自slave端的复制方式改成两个进程处理之后,更是减小了slave端的延时。而带来的效益是,对于数据实时性要求不是特别的敏感度的应用,只需要通过廉价的pc server来扩展slave的数量,将读压力分散到多台slave的机器上面,即可解决数据库端的读压力瓶颈。这在很大程度上解决了目前很多中小型网站的数据库压力瓶颈问题,甚至有些大型网站也在使用类似方案解决数据库瓶颈。一个Master带多个slave的架构实施非常简单,多个slave和单个slave的实施并没有太大区别。在Master端并不care有多少个 slave连上了master端,只要有slave进程通过了连接认证,向他请求binlog信息,他就会按照连接上来的io进程的要求,读取自己的 binlog信息,返回给slave的IO进程。对于slave的配置细节,在Mysql的官方文档上面已经说的很清楚了,甚至介绍了多种实现slave 的配置方法。 Mysql不支持一个Slave instance从属于多个Master的架构。就是说,一个slave instance只能接受一个master的同步源,听说有patch可以改进这样的功能,但没有实践过。Mysql AB之所以不实现这样的功能,主要是考虑到冲突解决的问题。 Mysql也可以搭建成dual master模式,也就是说两个Mysql instance互为对方的Master,也同时为对方的Slave。不过一般这种架构也是只有一端提供服务,避免冲突问题。因为即使在两边执行的修改有先后顺序,由于复制的异步实现机制,同样会导致即使在晚做的修改也可能会被早做的修改所覆盖,就像如下情形: 时间点 Mysql A Mysql B 1 更新x表y记录为10 2 更新x表y记录为20 3 获取到A日志并应用,更新x表的y记录为10(不符合期望) 4 获取B日志更新x表y记录为20(符合期望) 这样,不仅在B库上面的数据不是用户所期望的结果,A和B两边的数据也出现了不一致的情况。除非能将写操作根据某种条件固定分开在A和B两端,保证不会交叉写入,才能够避免上面的问题。

MYSQL中InnoDB和MyISAM的区别

InnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。基本的差别为:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持已经外部键等高级数据库功能。

MyIASM是IASM表的新版本,有如下扩展:

二进制层次的可移植性。
NULL列索引。
对变长行比ISAM表有更少的碎片。
支持大文件。
更好的索引压缩。
更好的键码统计分布。
更好和更快的auto_increment处理。

以下是一些细节和具体实现的差别:

1.InnoDB不支持FULLTEXT类型的索引。
2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。
3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。
另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。

MySQL通过localhost无法连接数据库的解决

问题:一台服务器的PHP程序通过localhost地址无法连接数据库,但是如果设置为127.0.0.1则可以正常连接,连接其他数据库服务器也正常。MySQL的权限设置正确,且通过mysql命令行客户端可以正常连接数据库。

分析:这是典型的socket没有正确设置的情况。

连接MySQL数据库有两种方式:TCP/IP(一般理解的端口的那种)和Unix套接字(一般叫socket或者sock)。大部分情况下,可以用localhost代表本机127.0.0.1(注意:localhost(local)是不经网卡传输!这点很重要,它不受网络防火墙和网卡相关的的限制。127.0.0.1是通过网卡传输,依赖网卡,并受到网络防火墙和网卡相关的限制),但是在MySQL连接时,二者不可混用,而且MySQL中权限设置中localhost与127.0.0.1也是分开设置的。当设置为127.0.0.1时,系统通过TCP/IP方式连接数据库;当设置为localhost时,系统通过socket方式连接数据库。

解决:首先要看本机MySQL的socket套接字文件在哪里,查看命令是:

mysql --verbose --help | grep socket

输出结果显示套接字文件的位置,比如:这台服务器显示的是:

socket /var/run/mysqld/mysqld.sock

然后修改php的配置文件php.ini与之对应起来就好了。找到这两项:

mysql.default_socket =
mysqli.default_socket =


一般来说这一项都是空的,改成:

mysql.default_socket = /var/run/mysqld/mysqld.sock
mysqli.default_socket = /var/run/mysqld/mysqld.sock


这里应写上一步查询到的文件,根据你的情况设置。至此php配置就修改好了,如果是CLI(命令行)方式或者CGI方式的话,立即就生效,如果是FASTCGI方式,需要重启一下fastcgi进程。