服务器安全维护方案
本文讲解服务器安全维护方案,教您怎么用便捷的办法达到服务器最安全的设置。
一、开启Rewrite功能
输入以下指令开启Rewrite模组(笔者使用Ubuntu系统)
$ sudo a2enmod rewrite
=> LoadModule rewrite_module modules / mod_rewrite.so
确认是否已经开启模组了
$ sudo apache2ctl -M | grep rewrite
rewrite_module(共享)
随后就可以开始在Site Config里面编写所需的rewriterule了。
在理解Rewrite的过程中,经常会出现与之搭配的.htaccess档案,.htaccess称为“ 阿帕奇设定的档案(Override Config),简单来说就是可以根据每个资料夹Override原本Site Config,可以针对一个资料夹改写网址,所以RewriteRule .htaccess并非只能只能在其中哦!
但本篇文章还是会以使用.htaccess作为范例,若要打开Override的功能,只要修改Site Config,加入AllowOverride All就可以了(例如/etc/apache2/site-enabled/default000-conf)
<Directory“ / var / www / html”>
AllowOverride All
</ Directory>
全部,FlieInfo都可以让.htaccess使用重写功能,详情可以参考官方设定值的意义。
二、改写规则
基本用法
就是改写网址条件的规则,它的写法结构如下:
RewriteRule [match_uri] [rewrite_uri] [标志]
match_uri:符合模式的URI
rewrite_uri:将被改写的URI
这两个也都可以使用距离表达式撰写,一个规则范例长这样:
重写^ match / .html $ rewrite.html [NC,L]
意义相等于以下 伪 码:
假设输入网址:http :
//domain.com/a/b/c.html uri = a / b / c.html
if(uri.match(“ match.html”){
url =“ rewrite.html”
}
RewriteRule的标志
最后面的flags代表设定Rule的行为,可用逗号代表多个Flag,中间不能有空格,介绍以下常用的:
[L]:最后一次,代表成功执行这个规则后就会停止,不继续往下执行。
[NC]:不区分大小写,代表match_uri不比对大小写差异。
[QSA]:查询字符串追加,代表保留网址尾端带的GET参数,没使用的标志的预设是会把参数去掉的。
[QSD]:查询字符串丢弃,与QSA相反的作用,apache的V2.4才有。
[R]:重定向,代表用转址的方式转到新的网址,预定是302状态码,如:[R = 301],也可以回传400、200、404等的状态码,通常会跟[L]一起代表结束,也是除错误的常用标志
[DPI]:不要再接续的Rule中结尾中加上PathInfo,会在“五,一些小特性”的伴随说明。
[F]:Forbdien就是不给看啦!
※更多标志:http: //httpd.
范例
接着来举例多个规则,再加上Flag的功用,假设网站资料夹结构如下:
根/
├match.html
├rewrite.html
├的.htaccess
└秘密/
└database_password.json
而范例的.htaccess的内容为:
#前面有井字号是注解
###开启重写
RewriteEngine于
###设置重写前面会补充的路径,预定会是DocumentRoot(如:/ var / www / html)
RewriteBase /
#规则
将由
上往下依序执行
#直到最后一行或遇到有符合且有L标志的规则就会停止
###规则1.输入domain.com/match.html将会显示rewrite.html的内容RewriteRule ^ match / .html $ rewrite.html [NC,L]
###规则2。输入domain.com/redirect.html将被导至domain.com/rewrite.html
RewriteRule ^ redirect / .html $ rewrite.html [NC,R = 302,L]
### Rule 3.如果输入domain.com/secret / …这样的格式的网址,则去掉secret /后,转回root并加.html
### $ 1是距离表达式的组捕获,就是$ 1 =(。*)取得括号内的值
RewriteRule ^ secret /(.*)$ $ 1.html [NC,L]
在浏览器上输入match.html或redirect.html均会显示rewrite.html的内容,显然是Rule 2将网址更改了,而Rule 1没有,因为就是302状态码的关系。
在此建议可以使用或POSTMAN通过cURL命令列或请求获取取回等工具观察整个响应的差异,将会非常容易除错,详细原因将在“四,如何除错”逐步中解释,以下笔者使用cURL示范:
1.要求:http://localhost/match.html
$
curl’http
://localhost/match.html’
我是rewrite.html
2.要求: http://localhost/redirect.html
$ curl’http ://localhost/redirect.html’
<!DOCTYPE HTML PUBLIC“-// IETF // DTD HTML 2.0 // EN”>
<html> <head>
<title>找到302 </ title>
</ head> <body>
<h1>找到</ h1>
<p>文档已移动<a href=” http://localhost/rewrite.html “>此处</a>。</ p>
<hr>
<地址> Apache / 2.4.33(Win32)OpenSSL / 1.1.0h PHP / 7.2.7位于本地主机端口80的服务器</地址>
</ body> </ html>
与浏览器不同的是,这里的响应会指出网址被转移到另外一个新网址http://localhost/rewrite.html,浏览器自动帮忙处理转定位的工作,根据上面回传的网址,再发出另一个请求获取网页内容,让使用者仅感觉到网址与页面改变而已。
这时候读者可试着把RewriteBase这行注解,将会发现回传结果出错了,
改写结果与Domain之间被加上DocumentRoot预定的路径。
$ curl’http ://localhost/redirect.html’
<!DOCTYPE HTML PUBLIC“-// IETF // DTD HTML 2.0 // EN”>
<html> <head>
<title>找到302 </ title>
</ head> <body>
<h1>找到</ h1>
<p>文档已移到<a href=” http://localhost/var/www/html/rewrite.html “>此处</a>。</ </ p>
<hr>
<地址> Apache / 2.4.33(Win32)OpenSSL / 1.1.0h PHP / 7.2.7位于本地主机端口80的服务器</ address>
</ body> </ html>
“`
前面两个Rule仅是一个基本的示范,平常应用当然会难上许多,来继续看第三个Rule吧
### Rule 3.如果输入domain.com/secret / …这样格式的网址,则去掉secret /后,转回root并添加.html
### $ 1是距离表达式的group capture,就是取得那个满足括号内的值
RewriteRule ^ secret /(.*)$ $ 1.html [NC,L]
第三个规则是禁止使用者访问敏感的secret资料夹,来试着访问database_password.json看能不能得到结果:
$
curl’http
://localhost/secret/database_password.json’HTTP
/ 1.1 404找不到
日期:Sun,2018年10月7日13:39:11 GMT
服务器:Apache / 2.4.33(Win32)OpenSSL / 1.1.0h PHP /7.2.7
变化:accept-language,accept-charset
accept-Ranges:个字节
内容类型:text / html; charset = utf-8
内容语言:zh
不管网址改成任何http://localhost/secret/…,都会回传404找不到頁面,是理想中的状况,非常棒!但是访问反而http://localhost/secret/变成403禁止訪問了耶,咦?
$
curl’http
:// localhost / secret /’
HTTP / 1.1 403 Forbidden
Date:Sun,07 Oct 2018 13:43:08 GMT
Server:Apache / 2.4.33(Win32)OpenSSL / 1.1.0h PHP / 7.2.7
不同:接受语言,接受字符集
接受范围:个字节
内容类型:text / html; charset = utf-8
内容语言:zh
这时候不知出了什么错的话,可以将Rule的Flag加上R=302,检查最后改写的结果出了什么状况:
$ curl’http :// localhost / secret /’
<!DOCTYPE HTML PUBLIC“-// IETF // DTD HTML 2.0 // EN”>
<html> <head>
<title> 302找到</ title>
</ head > <body>
<h1>找到</ h1>
<p>文档已移动<a href=” http://localhost/.html “>此处</a>。</ p>
<hr>
<地址>本地端口80上的Apache / 2.4.33(Win32)OpenSSL / 1.1.0h PHP / 7.2.7服务器</ address>
</ body> </ html>
发现$1没有抓到任何字串,所以没有任何档名加上.html,就变成403的结果,所以只要在Rule 3之前加上以下新的Rule就可以啰!
RewriteRule ^ secret / $ / [R = 302,NC,L]
以上就是RewriteRule与简单的 除错 方法,只要再加上看得懂距离表达式就能理解一般常见的规则啰!继续看更难一点的RewriteCond吧〜
三、重写条件
RewriteRule仅仅只能判断请求URI是否匹配而改写URI,但有很多需求是希望根据一些请求标头(主机,用户代理)与 阿帕奇 的环境变数做改写,先满足某些条件后,再次重写URI,因此有了RewriteCond的出现,它的写法结构如下:
RewriteCond [test_string] [match_string] [flags]
RewriteRule…
test_string:要比对的条件
match_string:符合的条件
以上这两个都可以使用尺寸表达式撰写,而且RewriteCond结束一定会接着一个RewriteRule,真正的范例会长这样:
RewriteCond%{HTTP_USER_AGENT}(facebookexternalhit)
RewriteRule ^ blog /(.*)$ fb-bot.html?path = $ 1&type =%1 [L]
在以上意义相等于以下 伪 码:
if($ HTTP_USER_AGENT ==’facebookexternalhit’){
if(url.match(’^ blog /(.*)$’)){url</p>
<p>
=’fb-bot.html?path = $ 1&type = facebookexternalhit’;</p>
<p>
}
}
※前面说到$1是Group Capture的用法,而RewriteCond则是用%1表示
RewriteCond可使用的变数
${HTTP_USER_AGENT}是RewriteCond可使用的变数,有以下常见的变数:
%{REQUEST_URI}:域后面完整的URI路径,规则实际上会拿到不完整的URI,详情可以参考“五,一些小特性”类别
%{QUERY_STRING}:后面GET带的参数
%{HTTP_HOST}:域
% {HTTP_COOKIE}:Cookie
%{HTTPS}:判断是否用https或http,如果是https就等于“ on”,否则为“ off”
%{HTTP_USER_AGENT}:用户代理
%{REQUEST_FILENAME}:访问的档案名称
※更多变量:https :
//httpd.
以及判断档案时,很常见搭配这两个match用法:
-d:目录。代表如果有这个资料夹
-f:常规文件。代表如果有这个档案
搭配起来写法就像以下范例,代表着如果没有这个档案就转到首页:
RewriteCond%{REQUEST_FILENAME}!-f
RewriteRule ^(。*)$ index.html
其实还有不同的match写法,可以参考Apache官网的RewriteCond。
RewriteCond的标志用法
NC:不区分大小写
OR:就是OR条件,下面会说明
RewriteCond也有或遵循AND的条件,先前提到RewriteCond后面一定会接一个RewriteRule,有个特性是它只吃接续的第一个规则,来看下面的范例:
###范例A
RewriteCond 1
RewriteRule 1
RewriteRule 2
//等同于
if(Cond1){
Rule1
}
Rule 2
###范例B
RewriteCond 1
RewriteCond 2
RewriteRule 1
// //等同于
if(Cond1 && Cond2){
Rule1
}
## #范例C
RewriteCond 1 [OR]
RewriteCond 2
RewriteRule 1
//等于于
if(Cond1 || Cond2){
Rule1
}
了解RewriteCond跟RewriteRule的用法与每一行执行下来的逻辑,就可以更容易的改写网路上别人写好的规则啰〜
四、如何侦错
先前在RewriteRule的先前有稍微简单展示 除错 的流程,这里仅使用文字讲解解一些小秘诀。
1.该使用什么 除错 工具
使用POSTMAN,cURL等的工具,浏览器另外还有Cache外,也会帮忙转址,此时就没办法观察第一次转址的网址内容,例如:最常见就是浏览器直接显示转位太多次的错误,但使用工具的话,就可以看到回应回传的转址结果。
2.如何知道撰写的正则表达式是否正确?
把RewriteRule的标志加[R=302],302状态代码代表Moved Temporarily,浏览器并不会缓存302的转址结果,但301会,可以确定Rule无误后再拿掉或替换原本301就好,像这样观察转址的结果:
RewriteRule ^(。*)$ = $ 1 [L,R = 302]
3.非得要修改正在运行中的网站怎么办?
可以用一些识别的标题,加上RewriteCond来测试撰写的RewriteRule,例如如自定义一个User-agent,每次Reuqest都用这个User-agent即可。
4.浏览器有缓存
前面提到浏览器cache的问题,若认为写的没问题,但访问网站仍是旧有结果的话,就开启私密浏览访问看看,最后仍没办法只好重开 阿帕奇 看看啰。
五、关于一些小特性
1. RewriteRule在巢状.htaccess当中不会获得完整URI路径
直接看范例,假设资料夹结构如下:
根/
├一个/
├b.html
└htaccess的
├c.html
└htaccess的
###两个htaccess的都只有这一行内容
重写规则^(。*)$ $ 1 [L]
1.要求: c.html
使用root / .htaccess
规则结果:c.html
2.要求: a/b.html
使用root / a / .htaccess
规则结果:b.html
没错,发生了不会拿到a/的路径的情况。
※阿帕奇 会自动选择最接近的.htaccess档案(详情会在下一阶段说明)
3.如果拿掉root/a/.htaccess档案,重新请求:a/b.html
使用root / .htaccess
规则结果:a / b.html
这样的结果又正常了,如果真的想确保拿到完整的URI路径,可以使用%{REQUEST_URL}变数来获得URI啰!
RewriteCond%{REQUEST_URI} ^(。*)$
RewriteRule ^%1 [L]
2. RewriteRule自动附加在结尾的PATH_INFO
有时候会希望遇到Rule1改写之后,再传递至下一个Rule2判断与改写,可是会遇到后面莫名多了先前的URI,来看一个简单的示范例子:
RewriteRule ^(。*)$ web / $ 1 [NC]
RewriteRule ^(。*)$ sec ======= $ 1 ====== [NC,R = 301]
请求: a/b/c.html
经过第一个规则变成:web / a / b / c.html
最后到第二个规则变成:sec ======= web / a / b / c.html / b / c.html ==== ==
发现它在Rule1的结果末端多了一个不需要的/b/c.html,这是因为因为PATH_INFO缘故,若不需要后面Path的话,可以在Rule 1加上DPIFlag移除它,由于一些php的CMS或Framework会使用到PATH_INFO的功能,所以是否关掉PATH_INFO的作用还是要注意一下啰!
3.重新寻找.htaccess档案
有时候要的规则很纯,就只是将所有请求转到都web/资料夹数下:
RewriteBase /
RewriteRule ^(。*)$ web / $ 1 [QSA,L]
但在 阿帕奇2.4后就会遇到出现Redirect Loop的错误,原因是第一次请求URI被改写后成web/xxxx,它会根据新URI找对应最近的.htaccess并再重跑一次RewriteRule
此时有两种解法,第一种是在web/资料夹下放一个空的.htaccess,第二种是可以参考下一点停止Redirect Loop的写法,放在RewriteRule前面。
4.停止重定向循环的情况
有时候会遇到无穷Loop的问题:
由于可能的配置错误,请求超出了10个内部重定向的限制。必要时使用’LimitInternalRecursion’增加限制。使用’LogLevel调试以获取回溯。
可以在所有RewriteRule之前加上判断,若重定向状态是200的话,就停止Loop:
RewriteCond%{ENV:REDIRECT_STATUS} 200
RewriteRule ^-[L]
不过建议还是检查RewriteRule哪边有写错的,毕竟这解不太算是万灵药。
参考:https : //stackoverflow.com/a/20930010
六、使用Htaccess的缺点
使用Override很方便,.htaccess只要将放到资料夹下面会有效果,但官方实际上不推荐开启Override的功能,它会降低效能,主要是有以下缺点:
1.每次请求的巢状搜寻
每一次请求都会 阿帕奇透过巢状递回的方式搜寻.htaccess档案,导致阿帕奇 缓慢,例如如送出这个请求:
请求: /example/sub/index.html
阿帕奇初步根据路径寻找以下.htaccess档案
/var/www/.htaccess
/var/www/example/.htaccess
/var/www/example/sub/.htaccess
最后 阿帕奇 选择离档案最近的 /var/www/example/sub/.htaccess
2.重复编译RewirteRule
由于每次请求都会巢状搜寻.htaccess,所以再遇到RewriteRule都会重新编译一次,不像Site Config只会编译一次后做缓存,所以RewriteRule非常多的话,也会导致阿帕奇 缓慢。
笔者使用 阿帕奇Benchmark做了一个小型的压力测试,连续访问10000次,同时1000个连线,取其三次执行ab指令的相应,分别比较是否有打开Override功能,并且重写了数量多寡核苷酸有影响,重写了的状况都是以跑到倒数第三行结束为主,且每行RewriteRule跟RewriteCond不重复。
此时上的Total平均值,有开启Override功能还是比没开启花的时间多了一些,而行数的多寡也会影响到花费的时间,但笔者还是有点疑惑,差0.1秒好像也没什么关系呐,这边还有请专业的读者可以为笔者解惑呢QQ
官方认为.htaccess主要还是给无法编辑Site Config的情况下使用,像是一台主机上有多站共享,但真的想用.htaccess效果又不想开启Override的话怎么办呢?也是有一个取得中间值的做法。
在Site Config中预先指定.htaccess档案路径
把Override功能关掉,并使用包括指定.htaccess约会的,如下写法:
的DocumentRoot在/ var / WWW /
<目录/ var / WWW />
设置AllowOverride无
包含/var/www/.htaccess
…
</目录>
但这方法也是有个小缺点:每次更新.htaccess都必须重新启动阿帕奇 重新重新设定。
如果主机流量不大的话,效能问题并没这么严重,最后还是得依据主机上的网站与情况,据估计这样做比较好哦!