第四篇 互联网公司安全运营

这篇应该是大部分安全行业工作者最容易忽视的一个部分。

第16章 互联网业务安全

一个优秀的安全方案应该有,良好的用户体验,优秀的性能。

对互联网垃圾的处理:同一客户端多次同样的请求,UserAgent不像浏览器,验证码可以阻挡。

对不同业务有不同的反垃圾处理规则。

注意对用户隐私的保护,告知获取了什么数据,用户有权拒绝。

应妥善保管数据。

第17章 安全开发流程

1. 核心安全培训
2. 确定安全要求
3. 质量门/bug栏
4. 安全和隐私风险评估
5. 设计要求
6. 减小攻击面
7. 威胁建模
8. 使用指定的工具
9. 弃用不安全的函数
10. 静态分析
11. 动态程序分析
12. 模糊测试
13. 威胁模型和攻击面评析
14. 事件响应计划
15. 最终安全评析
16. 发布/存档

SDL开发准则:
1. 与项目经理进行充分沟通,排出足够的时间。
2. 规范公司的立项流程,确保所有项目都能通知到安全团队。
3. 树立安全部门的权威,项目必须由安全部门审核完成后才能发布。
4. 将技术方案写入开发,测试的工作手册中。
5. 给工程师培训安全方案。
6. 记录所有的安全bug,激励程序员编写安全的代码。

第18章 安全运营

建立类似bugtracker的漏洞跟踪机制,并为漏洞的紧急程度选择优先级。

建立漏洞分析机制,与程序员一起制定修补方案,同时review补丁的代码实现。

对曾经出现的漏洞进行归档,并定期统计漏洞修补情况。

安全监控入侵检测

报警手段:邮件,im,短信。

建立紧急响应小组

保护安全事件的现场,最快速度解决问题。

第九章 认证与会话管理

认证的目的是为了认出用户是谁,而授权的目的是为了决定用户能做什么。

密码是最常见的认证手段。
密码长度应为8位以上,区分大小写,为大写字母,小写字母,数字,特殊符号两种以上。

网站保存密码应加密加盐比如md5(password+salt)

一般MD5解密通过彩虹表。

敏感操作多因素认证,如为敏感操作多设置一道密码,进行手机验证,密保问题等。

SessionID加密保存在cookie中。
一定时间强制销毁Session。

第十章 认证与会话管理

当访问控制存在缺陷时,会出现未授权访问漏洞,可能会直接进入后台

垂直权限,水平权限:
垂直权限是获得比自不应该有的权限。
水平权限是获得其他用户,但是和自己相同的权限。

第十一章 加密算法与随机数
略显深奥,有时间再去细细研究。暂时略过。

第十二章 Web框架安全

在MVC框架中,model,view,controller各层处理自己的安全问题。

针对不同情况有不同安全策略,不可盲从。缺少相关保护功能则需要自己添加。

框架自身也会存在安全问题,不可迷信。

第十三章 应用层拒绝服务攻击

cc攻击和DDoS不同,前者是正常的请求,ip也可能是真实的ip。是对服务器不断进行资源消耗较大的请求,例如查询数据库,读写硬盘等。

限制每个ip请求数量,使用验证码,可以编写脚本实现自动屏蔽异常访问的ip。

资源耗尽类的攻击本质上是对有限资源的无限制滥用。

第十四章 php安全

文件包含漏洞:用户可以通过动态变量控制引入的文件,能够读取敏感文件,或者源代码。


Impossible File Inclusion Source
<?php
// 限定允许打开的文件
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
    // This isn't the page we want!
    echo "ERROR: File not found!";
    exit;
}

High File Inclusion Source
//用file协议开头
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
    // This isn't the page we want!
    echo "ERROR: File not found!";
    exit;
}

Medium File Inclusion Source
// str_replace函数是极其不安全的,因为可以使用双写绕过替换规则
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );

Low File Inclusion Source
// 没有防护
$file = $_GET[ 'page' ];

?> 

确保register_globals = OFF,可以在代码中控制,养成初始化变量的好习惯。


eval(),assert(),system(),exec(),shell_exec(),passthru(),escapeshellcmd(),pcntl_exec()//执行代码

include(),include_once(),require(),requrie_once()//文件包含

file_put_contents(),fwrite(),fputs()//本机文件写入

定制安全的php环境


register_globals = OFF,避免变量覆盖。

open_basedir限制操作指定目录的文件。

allow_url_include = OFF
allow_url_fopen = OFF,对抗远程文件包含。

display_errors = OFF,关闭错误回显。

session.cookie_httponly = 1,开启httponly。

第十五章 Web Server配置安全

Apache安全
以专门的用户帐号和组运行 Apache。

默认的Apache安装后会在默认页面体现出端口、版本信息等,我们需要隐藏这些信息。


ServerTokens Prod
ServerSignature Off

禁用不必要的模块

Nginx安全
关闭服务器标记,如果开启的话(默认情况下)所有的错误页面都会显示服务器的版本和信息。
将server_tokens off;//声明添加到Nginx配置文件来解决这个问题

封杀各种user-agent

if ($http_user_agent ~* "java|python|perl|ruby|curl|bash|echo|uname|base64|decode|md5sum|select|concat|httprequest|httpclient|nmap|scan" ) {
    return 403;  
}

通过IP地址限制访问
你可以通过IP地址来限制访问目录/admin/:

location /admin/ {
## block one workstation
deny    192.168.1.1;
## allow anyone in 192.168.1.0/24
allow   115.159.160.21;
## drop rest of the world
deny    all;
}

0x00 起因

下午我的服务器错误日志剧增

2016/12/20 18:47:58 [error] 6773#0: *26058 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:47:59 [error] 6773#0: *26059 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:47:59 [error] 6773#0: *26060 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:48:00 [error] 6773#0: *26061 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:48:01 [error] 6773#0: *26062 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:48:03 [error] 6773#0: *26063 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:48:03 [error] 6773#0: *26064 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:48:03 [error] 6773#0: *26066 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"
2016/12/20 18:48:04 [error] 6773#0: *26067 access forbidden by rule, client: 191.96.249.80, server: octfive.cn, request: "POST /xmlrpc.php HTTP/1.0", host: "115.159.160.21"

来自191.96.249.80的请求不断post到xmlrpc.php

0x01 原理

通常wordpress登录接口都是做了防暴力破解防护的,这种利用xmlrpc.php的攻击可以绕过这些限制。攻击的方式直接POST以下数据到xmlrpc.php.

<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
  <methodName>wp.getUsersBlogs</methodName>
  <params>
   <param><value>username</value></param>
   <param><value>password</value></param>
  </params>
</methodCall>

0x02 防御手段

1. 可以屏蔽攻击者ip
/etc/nginx/sites-enabled/

server {
	deny 191.96.249.80;//屏蔽ip
}

2. 可以直接删除xmlrpc.php(前提是用不到这个文件)。

3. 设置xmlrpc.php禁止访问。

0x03 后记

至文章发布,该ip仍然在请求。虽然不会有实质上的危害,但是会写入大量的日志,对其他信息的处理带来不便。暂时关闭了访问日志的记录并且错误日志只记录critical。

access_log off;
error_log /var/log/nginx/error.log crit;
#access_log /var/log/nginx/access.log;
#error_log /dev/null;

又检查了之前的日志,发现10月的日志比其他的多得多,从2016/10/15 23:46:29到2016/10/18 12:45:23在不断地进行爆破,到2016/12/03为止也是不断地偶有这种情况发生,ip地址多为191.96.249.53,191.96.249.54,191.96.249.80(疑似智利ip)。

建议可以屏蔽以上ip。

第八章 文件上传漏洞

文件上传漏洞:一般是指上传用户脚本,导致代码执行。

文件上传检查流程一般为:
1. 客户端 javascript 检测 (通常为检测文件扩展名)
2. 服务端 MIME 类型检测 (检测 Content-Type 内容)
3. 服务端目录路径检测 (检测跟 path 参数相关的内容)
4. 服务端文件扩展名检测 (检测跟文件 extension 相关的内容)
5. 服务端文件内容检测 (检测内容是否合法或含有恶意代码)

客户端检测绕过(javascript 检测)
直接删除js,或者抓包修改后缀。

服务端检测绕过(MIME 类型检测)
抓包修改Content-Type

服务器检测绕过(目录路径检测)
%00字符截断,%00会被认作终止符,xx.php%00.jpg认作xx.php

服务端检测绕过(文件扩展名检测)
1. 文件名大小写绕过
用像 AsP,pHp 之类的文件名绕过黑名单检测
2. 名单列表绕过
用黑名单里没有的名单进行攻击,比如黑名单里没有 asa 或 cer 之类
3. 特殊文件名绕过
比如发送的 http 包里把文件名改成 test.asp. 或 test.asp_(下划线为空格),这种命名方式在 windows 系统里是不被允许的,所以需要在 burp 之类里进行修改,然后绕过验证后,会被 windows 系统自动去掉后面的点和空格,但要注意 Unix/Linux 系统没有这个特性。
4. 服务器解析漏洞
IIS 6.0
目录解析:/xx.asp/xx.jpg xx.jpg可替换为任意文本文件(e.g. xx.txt),文本内容为后门代码
IIS6.0 会将 xx.jpg 解析为 asp 文件。
后缀解析:/xx.asp;.jpg /xx.asp:.jpg(此处需抓包修改文件名)
IIS6.0 都会把此类后缀文件成功解析为 asp 文件。
还可以解析:/xx.asa /xx.cer /xx.cdx

IIS 7.0/IIS 7.5/Nginx <=0.8.37
在默认Fast-CGI开启状况下,在一个文件路径(/xx.jpg)后面加上/xx.php会将 /xx.jpg/xx.php 解析为 php 文件。
找个地方上传 xy.jpg ,然后找到 xy.jpg 的地址,在地址后加上 /xx.php 即可执行恶意文本。

Apache
后缀解析:test.php.x1.x2.x3
Apache将从右至左开始判断后缀,若x3非可识别后缀,再判断x2,直到找到可识别后缀为止,然后将该可识别后缀进解析
test.php.x1.x2.x3 则会被解析为php
经验之谈:php|php3|phtml 多可被Apache解析

Nginx <=0.8.37
在Fast-CGI关闭的情况下,Nginx <=0.8.37 依然存在解析漏洞,在一个文件路径(/xx.jpg)后面加上%00.php会将 /xx.jpg%00.php 解析为php文件。 服务端检测绕过(文件内容检测)
文件头加入GIF89a

防御文件上传漏洞
1. 文件上传目录设置为不可执行。
2. 文件类型采用白名单。
3. 用随机数改写文件名。

一个安全的上传代码:

 //处理图片上传
bool uploadPic = false;  //这里获取是否上传了文件  默认否
string newsPic = "";  //上传文件名默认为空
if (this.fuNewsPic.HasFile)//获取file服务器控件是否有文件
{
    string Pic = this.fuNewsPic.FileName.ToLower(); //把文件名转换为小写的
    string ext = Pic.Substring(Pic.LastIndexOf("."));//获取最后一个“.”号后面的文字  即为后缀
    if (ext == ".jpg" || ext == ".png" || ext == ".gif") //判断后缀名是否违规
    {
        int FileLength = this.fuNewsPic.PostedFile.ContentLength; //获取文件大小
        if (FileLength < 200000) //判断文件大小是否大于200K
        {
            //如果成立
            uploadPic = true; //更改uploadPic为true  因为有上传文件
            newsPic = System.DateTime.Now.ToFileTime().ToString() + ext; //把文件名以时间格式命名然后写入数据库
            }
            else
            {
                //如果不成立 则弹出警告框
                Page.RegisterStartupScript("", "<script langue=javascript>" + "alert('您上传的图片过大!系统只接受“小于200KB”的图片!');" + "</script>");
                return;
            }
        }
        else
        {
            //如果违规则弹出警告框提示
            Page.RegisterStartupScript("", "<script langue=javascript>" + "alert('您上传的可能不是可以接收的图片类型,系统只接受“jpg,gif,png”类型的图片!');" + "</script>");
            return;
         }
     }


    //文件上传部分

     if (uploadPic == true)  //判断upload是否为true  如果为true说明拥有文件需要上传
            {
                this.fuNewsPic.PostedFile.SaveAs(Server.MapPath("../../PagePic/" + newsPic));// 把文件重命名为时间格式   即是写入数据库的名字  并上传到根目录下的PagePic文件夹
            }

第七章 注入攻击

SQL注入:把SQL命令插入到请求的查询字符串,执行恶意的SQL命令。

盲注:根据页面变化判断时候存在注入漏洞。也就是and 1=1出现内容,and 1=2则没有。

延时注入:通过执行时间的长短来判断是否执行成功。

sql注入可以写入webshell:
union select “”,2,3 INTO OUTFILE ‘/var/www/test/webshell.php
要求:
1. 数据库用户需要拥有FILE权限。
2. 需要知道网站物理地址

可以执行系统命令,自己写比较麻烦,sqlmap集成了功能:
sqlmap.py -u “http://url/news?id=1” –os-cmd=ls

cookie注入,一般未做防护。

SQL注入的防御
1. 过滤
2. 推荐使用预编译语句
3. 检查数据类型

// low security
if( isset( $_REQUEST[ 'Submit' ] ) ) { 
    // Get input
    $id = $_REQUEST[ 'id' ]; //未做任何防护

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";//存在注入
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
} 

// medium security
if( isset( $_POST[ 'Submit' ] ) ) { 
    // Get input 
    $id = $_POST[ 'id' ];
    $id = mysql_real_escape_string( $id ); //进行了过滤但是并不够安全

    // Check database 
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
}

// high security
if( isset( $_SESSION [ 'id' ] ) ) { 
    // Get input 
    $id = $_SESSION[ 'id' ]; 

    // Check database // 从session中获取id还是会注入
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"; 
    $result = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' ); 
}

//impossible security
if( isset( $_GET[ 'Submit' ] ) ) { 
    // Get input 
    $id = $_GET[ 'id' ]; 

    // Was a number entered? 
    if(is_numeric( $id )) { 
        // Check the database // 采取了预编译语句,无论id是什么都会被当作id的属性
        $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' ); 
        $data->bindParam( ':id', $id, PDO::PARAM_INT ); 
        $data->execute(); 
        $row = $data->fetch();
}

XML注入

与sql注入类似,也可以通过过滤防御

代码注入:系统提供命令执行类函数主要方便处理相关应用场景的功能.而当不合理的使用这类函数,同时调用的变量未考虑安全因素,就会执行恶意的命令调用,被攻击利用。

system.php?dir=

<?php
    $dir = $_GET["dir"];
    if (isset($dir)){
        system("ls -al ".$dir);
   }
?>

我们提交http://www.site.com/system.php?cmd=cat /etc/passwd

提交以后,命令变成了 system(“ls -al | cat /etc/passwd”);

CRLF注入:是回车 + 换行(\r\n)的简称。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。所以,一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS。

如果能控制HTTP请求头,就能控制整个HTTP请求。