1. 程式人生 > >怎樣防止CSRF和XSS攻擊

怎樣防止CSRF和XSS攻擊

本文說一下SpringMVC如何防禦CSRF(Cross-site request forgery跨站請求偽造)和XSS(Cross site script跨站指令碼攻擊)。

轉載:http://itindex.net/blog/2013/10/25/1382688300000.html

說說CSRF

CSRF來說,其實Spring3.1ASP.NET MVC3RailsDjango等都已經支援自動在涉及POST的地方新增Token(包括FORM表單和AJAX POST等),似乎是一個tag的事情,但如果瞭解一些實現原理,手工來處理,也是有好處的。因為其實很多人做web開發,但涉及到web安全方面的都是比較資深的開發人員,很多人安全意識非常薄弱,

CSRF是什麼根本沒有聽說過。所以對他們來說,CSRF已經是比較高深的東西了。先說說什麼是CSRF?你這可以這麼理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義傳送惡意請求。CSRF能夠做的事情包括:以你名義傳送郵件,發訊息,盜取你的賬號,甚至於購買商品,虛擬貨幣轉賬......造成的問題包括:個人隱私洩露以及財產安全。CSRF一般都是利用你的已登入已驗證的身份來發送惡意請求。比較著名的一個例子就是2009年黑客利用Gmail的一個CSRF漏洞成功獲取好萊塢明星Vanessa Hudgens的獨家豔照。其攻擊過程非常簡單,給該明星的gmail賬戶發了一封email,標題是某大導演邀請你來看看這個電影,裡面有個圖片:<img src="https://mail.google.com/mail?ui=2&fw=true&
[email protected]
">,結果她登入Gmail,開啟郵件就默默無聞的中招了,所有郵件被轉發到黑客的賬號。因為當時Gmail設定轉發的設定頁面有漏洞,其設定方法是開啟一個視窗,點選確定後實際URL是https://mail.google.com/mail?ui=2&fw=true&[email protected]:

 

其實即使不是在同一個頁面開啟,在不同的tab開啟也是一樣可以通過網站登入驗證的,因為受害者首先已經登入了網站,在瀏覽網站的過程中,若網站設定了Session cookie,那麼在瀏覽器程序的生命週期內,即使瀏覽器同一個視窗打開了新的tab頁面,Session cookie也都是有效的,他們在瀏覽器同一個視窗的多個tab頁面裡面是共享的(注:現在Gmail支援多個tab同時持有多個SessionID)。所以攻擊步驟是,第一,受害者必須在同一瀏覽器視窗(即使不是同一tab)內訪問並登陸目標站點;第二,這使得Session cookie有效,從而利用受害者的身份進行惡意操作。 

再舉個實際的例子,假設我們介面上有刪除某一項的連結,例如:<a href="javascript:void(0)" onclick="region_del.do?name=0000001">Delete</a>;

其Java Spring MVC後臺有個函式是刪除某個item,注意是GET不是POST:

複製程式碼 @RequestMapping(value = "region_del.do", method = RequestMethod.GET)
public String regionDel(@RequestParam String name, Locale locale)
{
    //Delete region [email protected]        
    return "redirect:/region.html";
} 複製程式碼

點選介面上那個<a href="javascript:void(0)" onclick="region_del.do?name=0000001">Delete</a>連結,就後臺刪除某項,看起來非常正常啊。 

好,現在你登入你的網站,然後在另外一個tab開啟這個html檔案:

複製程式碼 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>hack</title>
</head>
<body>
  <img src="http://localhost/testsite/region_del.do?name=0000001"/>
 </body>
</html> 複製程式碼

發現同樣被刪除了某項。試想,如果是網銀,你的錢已經被轉賬......(除了referer不一樣,session cookie被利用)

 

好了,現在 後臺改成POST(寫操作儘量用POST),前臺介面那個刪除的連結改成Form提交:

<form action="region_del.do" method="POST">
 <input type="hidden" name="name" value="0000001">
        <input type="submit" value="Delete"/>
</form>

看起來安全多了。OK,現在你登入你的網站,然後在另外一個tab開啟這個html檔案:

複製程式碼 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>Hack</title>
    <script>function steal(){
        
var mySubmit = document.getElementById('steal_form');
        mySubmit.submit();
      }
    
</script>
  </head>
  <body onload='steal()'>
<form id = "steal_form" method="POST" action="http://localhost/testsite/region_del.do">
   <input type="hidden" name="func" value="post">
<input type="hidden" name="name" value="0000001">
</form>
  </body>
</html> 複製程式碼 發現同樣被刪除了某項。試想,如果是網銀,你的錢已經被轉賬......

當然,你如果前臺還是用連結,但改成js,用AJAX POST提交,也是一樣的效果:

$.ajax({
 type: "POST",
 url:....
});

解決辦法就是在Form表單加一個hidden field,裡面是服務端生成的足夠隨機數的一個Token,使得黑客猜不到也無法仿照Token。 

先寫一個類,生成足夠隨機數的Token(注:Java的Random UUID已經足夠隨機了,參考這個這個) 

複製程式碼 package com.ibm.cn.web.beans;

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
* A manager for the CSRF token for a given session. The {
@link #getTokenForSession(HttpSession)} should used to
* obtain the token value for the current session (and this should be the only way to obtain the token value).
* **
*/

public final class CSRFTokenManager {

    /**
     * The token parameter name
     
*/
    static final String CSRF_PARAM_NAME = "CSRFToken";

    /**
     * The location on the session which stores the token
     
*/
    public static final  String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CSRFTokenManager.class
            .getName() + ".tokenval";

    public static String getTokenForSession(HttpSession session) {
        String token = null;
        
        // I cannot allow more than one token on a session - in the case of two
        
// requests trying to
        
// init the token concurrently        synchronized (session) {
            token = (String) session
                    .getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
            if (null == token) {
                token = UUID.randomUUID().toString();
                session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
            }
        }
        return token;
    }

    /**
     * Extracts the token value from the session
     * 
     * 
@param request
     * 
@return*/
    public static String getTokenFromRequest(HttpServletRequest request) {
        return request.getParameter(CSRF_PARAM_NAME);
    }

    private CSRFTokenManager() {
    };

} 複製程式碼

開啟Form頁面的時候在服務端生成Token並儲存到Session中,例如:model.addAttribute("csrf", CSRFTokenManager.getTokenForSession(this.session));

然後在Form中新增Hidden field: 

<input type="hidden" name="CSRFToken" value="${csrf}" />

然後在後臺提交的時候驗證token :

複製程式碼 @RequestMapping(value = "region_del.do", method = RequestMethod.GET)
public String regionDel(@RequestParam String name, @RequestParam String CSRFToken, Locale locale)
    {
        if(CSRFToken == null || !CSRFToken.equals(session.getAttribute(CSRFTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME).toString())){
                logger.debug("CSRF attack detected. URL: region_edit.do");
                return "redirect:/login.form";
        } 
                
    //Delete region [email protected]        
    return "redirect:/region.html";
} 複製程式碼

你還可以把上面的步驟寫到BaseController裡面,或者寫到攔截器裡面,攔截所有POST請求,驗證CSRF Token。這裡掠過....

如果你用AJAX POST的方法,那麼後臺一樣,前臺也要有Hidden field儲存Token,然後在提交AJAX POST的時候加上該csrf引數即可。(更多csrf參考這個這個。)

AJAX POST的CSRF防禦

首先在頁面進入的時候從後臺生成一個Token(每個session),放到一個Hidden input(用Spring tag或freemarker可以寫) 。然後在ajax post提交的時候放到http請求的header裡面:

複製程式碼     var headers = {};
    headers['__RequestVerificationToken'] = $("#CSRFToken").val();
    
    $.ajax({
        type: "POST",
        headers: headers,
        cache: false,
        url: base + "ajax/domain/delete.do",
        data: "id=123",
        dataType:"json",
        async: true,
        error: function(data, error) {},
        success: function(data)
        {
            
        }
    }); 複製程式碼

然後在後臺controller裡面校驗header裡面這個token,也可以把這個函式放到baseController裡面:

複製程式碼 protected boolean isValidCsrfHeaderToken() {
        if (getRequest().getHeader("__RequestVerificationToken") == null
                || session
                        .getAttribute(CSRFTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME) == null
                || !this.getRequest()
                        .getHeader("__RequestVerificationToken")
                        .equals(session
                                .getAttribute(
                                        CSRFTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME)
                                .toString())) {
            return false;
        }
        return true;
    } 複製程式碼

xss

關於xss的介紹可以看這個這個網頁,具體我就講講Spring MVC裡面的預防:

web.xml加上:

<context-param>
   <param-name>defaultHtmlEscape</param-name>
   <param-value>true</param-value>
</context-param>

Forms加上:

<spring:htmlEscape defaultHtmlEscape="true" />

更多資訊檢視OWASP頁面

第二種方法是手動escape,例如使用者可以輸入:<script>alert()</script> 或者輸入<h2>abc<h2>,如果有異常,顯然有xss漏洞。

首先新增一個jar包:commons-lang-2.5.jar ,然後在後臺呼叫這些函式:StringEscapeUtils.escapeHtml(string); StringEscapeUtils.escapeJavaScript(string); StringEscapeUtils.escapeSql(string);

前臺js呼叫escape函式即可。

第三種方法是後臺加Filter,對每個post請求的引數過濾一些關鍵字,替換成安全的,例如:< > ' " \ /  # & 

方法是實現一個自定義的HttpServletRequestWrapper,然後在Filter裡面呼叫它,替換掉getParameter函式即可。

首先新增一個XssHttpServletRequestWrapper:

複製程式碼 package com.ibm.web.beans;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {  
    public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
        super(servletRequest);
    }
    public String[] getParameterValues(String parameter) {
      String[] values = super.getParameterValues(parameter);
      if (values==null)  {
                  return null;
          }
      int count = values.length;
      String[] encodedValues = new String[count];
      for (int i = 0; i < count; i++) {
                 encodedValues[i] = cleanXSS(values[i]);
       }
      return encodedValues;
    }
    public String getParameter(String parameter) {
          String value = super.getParameter(parameter);
          if (value == null) {
                 return null;
                  }
          return cleanXSS(value);
    }
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null)
            return null;
        return cleanXSS(value);
    }
    private String cleanXSS(String value) {
                //You'll need to remove the spaces from the html entities below        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", "");
        return value;
    }

複製程式碼

然後新增一個過濾器XssFilter :

複製程式碼 package com.ibm.web.beans;

import java.io.IOException;  

import javax.servlet.Filter;  
import javax.servlet.FilterChain;  
import javax.servlet.FilterConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;

public class XssFilter implements Filter {
    FilterConfig filterConfig = null;

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    public void destroy() {
        this.filterConfig = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        chain.doFilter(new XssHttpServletRequestWrapper(
                (HttpServletRequest) request), response);
    }
} 複製程式碼

最後在web.xml裡面配置一下,所有的請求的getParameter會被替換,如果引數裡面 含有敏感詞會被替換掉:

複製程式碼   <filter>
     <filter-name>XssSqlFilter</filter-name>
     <filter-class>com.ibm.web.beans.XssFilter</filter-class>
  </filter>
  <filter-mapping>
     <filter-name>XssSqlFilter</filter-name>
     <url-pattern>/*</url-pattern>
     <dispatcher>REQUEST</dispatcher>
  </filter-mapping> 複製程式碼

 (這個Filter也可以防止SQL注入攻擊) 

登入頁面的攻擊例子

假設登入頁面有個輸入使用者名稱和密碼的輸入框,可以有很多Xss/csrf/注入釣魚網站/SQL等的攻擊手段,例如:

 輸入使用者名稱 :    >"'><script>alert(1779)</script>
 輸入使用者名稱:     usera>"'><img src="javascript:alert(23664)">
 輸入使用者名稱:     "'><IMG SRC="/WF_XSRF.html--end_hig--begin_highlight_tag--hlight_tag--">
 輸入使用者名稱:     usera'"><iframe src=http://demo.testfire.net--en--begin_highlight_tag--d_highlight_tag-->

Web安全漏洞檢測工具

推薦使用IBM Rational AppScan(IBM Rational AppScan下載、版權購買和破解、註冊碼自己解決)

 

可以錄製指令碼,設定URL,如果網站需要登入,可以設定自動登入:

 

檢測結果還可以保持為專業的pdf檢測報告 

 

業界安全程式設計標準最佳實踐

相關推薦

怎樣防止CSRFXSS攻擊

本文說一下SpringMVC如何防禦CSRF(Cross-site request forgery跨站請求偽造)和XSS(Cross site script跨站指令碼攻擊)。 轉載:http://itindex.net/blog/2013/10/25/1382688

php對前臺提交的表單資料做安全處理(防SQL注入XSS攻擊等)

/** * 防sql注入字串轉義 * @param $content 要轉義內容 * @return array|string */ public static function escapeString($content) { $pa

SQL注入XSS攻擊

SQL注入: 所謂SQL注入,就是通過把SQL命令插入到提交的Web表單或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令,導致資料庫中的資訊洩露或者更改。 防範: 1.永遠不要信任使用者的輸入,將使用者輸入的資料當做一個引數處理: 使用引數化的形式,也就是將使用者輸入的東西以一

CSRFXSS攻擊與防禦

 CSRF概念:CSRF跨站點請求偽造(Cross—Site Request Forgery),跟XSS攻擊一樣,存在巨大的危害性,你可以這樣來理解:        攻擊者盜用了你的身份,以你的名義傳送惡意請求,對伺服器來說這個請求是完全合法的,

CSRFXSS攻擊的原理與防範

CSRF 1、概念與原理 CSRF,跨站請求偽造,攻擊方偽裝使用者身份傳送請求從而竊取資訊或者破壞系統。例如: 使用者訪問A網站登陸並生成了cookie,再訪問B網站,如果A網站存在CSRF漏洞,此時B網站給A網站的請求(此時相當於是使用者訪問),A網站會認為是使用者發的請求,從而B網站就成功偽裝了你的

web安全CSRFXSS

web端的安全攻擊有CSRF和XSS兩種,將通過以下三個方面介紹這兩種安全攻擊:   1、基本概念和縮寫   2、攻擊原理   3、防禦措施   CSRF    1、基本概念和縮寫     CSRF(Cross-site request forgery)跨站請求偽造,是通過偽

Spring MVC防禦CSRFXSS

本文說一下SpringMVC如何防禦CSRF(Cross-site request forgery跨站請求偽造)和XSS(Cross site script跨站指令碼攻擊)。 說說CSRF 對CSRF來說,其實Spring3.1、ASP.NET MVC3、Rails、D

防止SpringMVC的XSS攻擊的方法

XSS攻擊,即Cross Site Script,跨指令碼攻擊,往web頁面注入html程式碼或者script程式碼,造成頁面混亂。 HttpServletRequestWrapper 是HttpSe

很好用的 web防止sql 注入 xss 攻擊 目錄遍歷程式碼

最近公司網站被黑的嚴重,在專案入口問價加入次下程式碼有很好的效應,自己又加了一些程式碼方便一會檢視 <?php /*雲體檢通用漏洞防護補丁v1.1 更新時間:2013-05-25 功能說明:防護XSS,SQL,程式碼執行,檔案包含等多種高危漏洞 */ $url_arr

Web安全之CSRFXSS

實驗原理: CSRF(Cross-Site Request Forgery,跨站點偽造請求)是一種網路攻擊方式,該攻擊可以在受害者毫不知情的情況下以受害者名義偽造請求傳送給受攻擊站點,從而在未授權的情況下執行在許可權保護之下的操作,具有很大的危害性。具體來講,可以這樣理解C

java防止跨站點XSS攻擊的方法

com.frameworkset.common.filter.CharsetEncodingFilter CharsetEncodingFilter是不具備防止跨站攻擊功能的,但是為其增加兩個init-param引數後就可以了:     wallfilterrules 指定

CSRF XSS 的區別

代碼實現 情況 計算 scrip 命令行 for req 改密 js代碼 XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任 XSS: 跨站腳本攻擊 原名為Cross Site Scriptin,為避免和網頁層級樣式表概念混淆, 另名為

CSRFXSS區別預防

名詞解釋 CSRF(Cross-site request forgery)跨站請求偽造 XSS (Cross-site scripting)跨站指令碼攻擊,這裡縮寫css被前端層疊樣式表(Cascading Style Sheets)佔用了,為了區分就叫了xss。 攻擊手段描述 CSRF 攻擊場景描述:假

springboot 實現防止xss攻擊sql注入

一、xss攻擊和sql注入(可以使用IBM安全漏洞掃描工具) (1)XSS(Cross Site Scripting),即跨站指令碼攻擊,是一種常見於web application中的電腦保安漏洞。XSS通過在使用者端注入惡意的可執行指令碼,若伺服器端對使用者輸入不進行處理,直接將使用者輸入

Spring 防禦CSRFXSSSQL注入攻擊

對每個post請求的引數過濾一些關鍵字,替換成安全的,例如:< > ' " \ /  # & 方法是實現一個自定義的HttpServletRequestWrapper,然後在Filter裡面呼叫它,替換掉getParameter函式即可。 首先新增一個X

Spring MVC防禦CSRFXSSSQL注入攻擊

說說CSRF 對CSRF來說,其實Spring3.1、ASP.NET MVC3、Rails、Django等都已經支援自動在涉及POST的地方新增Token(包括FORM表單和AJAX POST等),似乎是一個tag的事情,但如果瞭解一些實現原理,手工來處理,也是有好處的。因為其實很多人做web開

防止Xss攻擊Sql注入

Xss攻擊簡介 XSS攻擊全稱跨站指令碼攻擊,是為不和層疊樣式表(Cascading Style Sheets, CSS)的縮寫混淆,故將跨站指令碼攻擊縮寫為XSS,XSS是一種在web應用中的電腦保安漏洞,它允許惡意web使用者將程式碼植入到提供給

防止SQL注入XSS跨站攻擊程式碼

這兩天用360網站安全檢測網站,檢測出SQL注入漏洞和XSS跨站攻擊指令碼N多項bug,然後上網找各種資料,把大部分的資料都看了一遍,最後自己總結了如下程式碼: a、防止SQL注入程式碼: //防止S

網站xss攻擊防止

通常用於檢測使用者輸入的資料是否符合預期的格式,比如日期格式,Email格式,電話號碼格式等等;輸入檢查必須在服務端進行;當然為了提高使用者體驗和減輕服務端的資源客戶端也要進行一次檢查,但服務端檢查始終是必須的,有個別程式設計師卻調過來了認為客戶端檢查是必須的,服務端檢查是可選的,其實這是錯誤的邏輯,因為客戶

跨域post 及 使用token防止csrf 攻擊

發生 guid form eba 代碼 host fault 而不是 發送郵件 環境: 後臺使用的python - flask 前臺使用angular框架 1.一個跨域post的樣例: 跨域post有多種實現方式: