1.1反射xss
1.1.1 输出在html 标签内或者普通属性(非href、非事件类)
调用如下的HtmlEncode 函数进行编码,php 示例代码,其他语言可以参照实现。
1function HtmlEncode($str)
2
3{
4
5 $str = str_replace("&","&", $str);
6
7 $str = str_replace(">",">", $str);
8
9 $str = str_replace("<","<", $str);
10
11 $str = str_replace("\"",""", $str);
12
13 $str = str_replace("'","'", $str);
14
15 $str = preg_replace("/\s+/"," ", $str);
16
17 return $str;
18
19}
注:为了预防业务将参数输出至属性值时未使用引号包裹的情况,这里自己实现的HtmlEncode 也将空格编码。
1.1.2 输出在html 标签的 href 属性内
一般来说,如果变量是完整的url,应该先检查下是否以^http开头,且目的域名是允许的域名列表内,以保证不会出现伪协议类的XSS攻击。接着使用 HtmlEncode编码下;或者因为此处是地址栏,可以用 php标准函数urlencode 编码变量。
1.1.3 输出在html 标签的事件属性内(如onXX 事件)
使用 jsencode(\uUUUU) 编码,php 示例代码,其他语言可以参照实现。
1function jsencode($str)
2
3{
4
5 $arr = array();
6
7 $str_len = strlen($str);
8
9 $need_encode = "<>\"&#`()[]';";
10
11 for($i = 0; $i < $str_len; $i++)
12
13 {
14
15 if (strpos($need_encode, $str[$i]))
16
17 {
18
19 $arr[$i] = "\\u00" . bin2hex($str[$i]);
20
21 }
22
23 else
24
25 {
26
27 $arr[$i] = $str[$i];
28
29 }
30
31 }
32
33 return join("", $arr);
34
35 }
1.1.4 在富文本框内进行过滤
参考 这里 进行标签和属性的白名单过滤。
1.2 存储XSS
同反射XSS,在输出变量时参照如上情形描述。
1.3 DOMXSS
在"$var" 输出到
Javascript 函数代码示例如下:
1function jsencode(str) {
2
3 var arr = [];
4
5 var need_encode = "<>\"&#`()[]';";
6
7 for (var i = 0; i < str.length; i++) {
8
9
10
11 if (need_encode.indexOf(str[i]) != -1)
12
13 {
14
15 arr[i] = ("\\u00" + str.charCodeAt(i).toString(16)).slice(-6);
16
17 }
18
19 else
20
21 {
22
23 arr[i] = str[i];
24
25 }
26
27 }
28
29 return arr.join("");
30}
31
32
33
34function HtmlEncode(sStr)
35
36{
37
38 sStr = sStr.replace(/&/g,"&");
39
40 sStr = sStr.replace(/>/g,">");
41
42 sStr = sStr.replace(/
26
27
28
29
30
31
32
33
34
35
36
37
38
39
52
53
54
55
56
57
58
59
2.3 验证码
在特别关键的操作建议启用,其他操作建议预埋验证码,紧急时刻(如出现CSRF蠕虫)可以临时启用。
3、SQL 注入
3.1 首选绑定参数的方式进行sql 操作
PHP5中,增加了MySQL支持,提供了mysqli扩展:
PHP手册地址:http://php.net/mysqli
1
3.2 检测参数类型,添加转义
如果php版本太低,或者改造代码成本比较大,在使用参数拼接方式实现sql 操作时,务必做到以下两点:
3.2.1 检查参数类型
当用户输入的为数字时可以使用如下方式:
使用is_int()函数(或is_integer()或is_long()函数)
使用gettype()函数
使用intval()函数
使用settype()函数
检查用户输入字符串的长度使用strlen()函数。
检查日期或时间是否是有效的,可以使用strtotime()函数。
3.2.2 查询之前进行转义操作
对于一个已经存在的程序来说,可以写一个通用函数来过滤:
1function safe($string)
2
3{
4
5 return "'" . mysql_real_escape_string($string) . "'";
6
7}
调用方式:
1$variety = safe($_POST[' variety ']);
2
3$query = "SELECT * FROM wines WHERE variety=" . $variety;
注:mysql_real_escape_string 必须在(PHP 4 >= 4.3.0, PHP 5)的情况下才能使用,否则只能用 mysql_escape_string,转义字符有:\x00 , \n , \r , \ , ' , " and \x1a。
Safe 函数中需要将转义之后的字符串用引号包裹起来(对于int类型说查询结果一致),否则黑客本来就不用考虑去闭合引号,转义操作也等于没有效果。
如果数据库字符集是gbk,可能存在宽字节绕过问题,需要在查询之前设置一下character_set_client,如下所示:
1mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn);
4、文件包含漏洞
4.1 本地文件包含
php中可以使用 open_basedir 将用户文件访问限制在指定的区域。如将文件访问限制在 /dir/user/ 中,在php.ini中设置 open_basedir = /dir/user/。
对包含文件的名称、路径进行严格限制和过滤(建议白名单方式),避免被篡改为恶意文件,单纯过滤 ”../” 等相对路径串很可能会被绕过。过滤以下字符(以逗号分隔):
1../, %2e%2e%2f, %2e%2e/, ..%2f, %2e%2e%5c, %2e%2e\, ..%5c, %252e%252e%255c, ..%c0%af(仅windows需要), ..%c1%9c(仅windows需要)
4.2 远程文件包含
关闭allow_url_fopen、allow_url_include。
5、命令执行漏洞
对于传入的变量做过滤,对于 \n $ & ; | ' " ( ) `(反单引号) 过滤或转义这些特殊字符。
6、SSRF 服务端请求伪造
限制 C(libcurl)/php(cURL) 允许的协议
如设置CURLPROTO_HTTP选项,仅仅允许http和https请求,可以防止类似于file:///, gopher://, ftp:// 等引起的问题。
限制访问内网
从url 中提取出域名 www.test.com ,解析域名,获得域名指向的地址 X.X.X.X
检查地址是否为内网地址段:10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8等,
如果是内网地址则屏蔽;
如果不是内网地址,设置 CURLOPT_RESOLVE(libcurl),使libcurl 将域名解析到我们刚才解析出的ip 地址进行访问,即真正发起请求时不再进行dns解析,避免被 dns rebinding 绕过。
避免30x 跳转绕过
设置不跟随跳转,即设置 CURLOPT_FOLLOWLOCATION 为false。如果一定要跟随跳转,这里因为使用了库函数,故不能自己在跟随跳转前判断下域名解析的目标ip 是否是内网(如2),但可以通过设置 CURLOPT_OPENSOCKETFUNCTION 回调函数,在socket 创建之前先校验目标地址。
1// curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_callback);
1curl_socket_t opensocket_callback(void *clientp, curlsocktype purpose,
2 struct curl_sockaddr *addr)
3{
4 struct sockaddr_in *addr_in = (struct sockaddr_in *) &addr->addr;
5 unsigned int uip = ntohl(addr_in->sin_addr.s_addr);
6
7 char ipbuf[50];
8 inet_ntop(addr_in->sin_family, &addr_in->sin_addr, ipbuf, sizeof(ipbuf));
9 INF("Connect IP: %s\n", ipbuf);
10
11 if ((uip >= 0x7F000000 && uip <= 0x7FFFFFFF) ||
12 (uip >= 0x0A000000 && uip <= 0x0AFFFFFF) ||
13 (uip >= 0xAC100000 && uip <= 0xAC1FFFFF) ||
14 (uip >= 0xC0A80000 && uip <= 0xC0A8FFFF) ||
15 (uip == 0x00000000 || uip == 0xFFFFFFFF))
16 {
17 //过滤来自内网的访问
18 WRN("target ip is illegal.\n");
19 return CURL_SOCKET_BAD;
20 }
21
22 return socket(addr->family, addr->socktype, addr->protocol);
23}
这样,每当libcurl 试图创建socket 连接某个服务器时,都会先执行 opensocket_callback 回调函数。若目标地址不合法,则可以在这个回调函数中返回CURL_SOCKET_BAD,libcurl 也会因此无法建立连接返回失败。
7、文件上传漏洞
7.1 目录安全配置
如果Apache以daemon普通用户启动,则黑客通过网站漏洞入侵服务器后,将获得Apache的daemon权限,因此需要确认网站web目录和文件的属主与Apache启动用户不同,防止网站被黑客恶意篡改和删除。
a) 网站web目录和文件的属主可以设置为root等(非Apache启动用户)。
b) Web目录权限统一设置为755,web文件权限统一设置为644(cgi文件若需执行权限可设置为755),只有上传目录等需要可读可写权限的目录可以设置为777(目录需要有执行权限才可进入)。
目录默认不可写,可写目录不解析,Web Server非root,管理页面不对外。
7.2判断文件类型
可以结合使用mime type、后缀方式、文件头部(getimagesize /exif_imagetype),强烈推荐白名单方式。
7.3 改写文件名和路径
使用随机数改写文件名和文件路径;或者把文件放在非web 目录下(或者统一的一个文件服务器),且设置open_basedir 以避免被文件包含。
7.4上传目录只允许访问特定类型的文件
配置文件如下:
1
2
3order deny,allow
4
5deny from all
6
7
8、管理后台
管理后台默认不对外开放,如果一定要对外开放,需要有安全配置。
对于管理目录,需要做到只允许合法ip(一般内网)可以访问,nginx限制白名单ip访问的配置如下:
1location ~ ^/private/ {
2
3allow 192.168.1.0/24;
4
5deny all;
6
7}
管理目录建议启用密码认证,密码认证依靠Web应用自身的认证机制。如果Web应用无认证机制,可启用nginx(apache同理)的密码认证机制,配置如下:
1location ^~ /soft/ {
2
3location ~ .*\.(php|php5)?$ {
4
5fastcgi_pass unix:/tmp/php-cgi.sock;#这里按照你自己的设置
6
7fastcgi_index index.php;
8
9include fcgi.conf;
10
11}
12
13auth_basic "Authorized users only";
14
15auth_basic_user_file 这里写前面脚本返回的文件路径;
16}
来源:https://www.hacking8.com/MiscSecNotes/xiufu.html#title-120daybank
文章评论