http协议简介:
Web开发是和Http协议打交道的,必须了解Http协议。Http协议版本:Http/0.9、Http/1.0、Http/1.1,现在主流是Http/1.1版本。
Http协议分析工具:1、DebugBar,Http(s)标签的内容。免费的。只能分析当前浏览器中的内容。2、Httpwatch,收费的,只能分析当前浏览器中的内容,推荐使用。3、HttpAnalyzer,收费的,能分析计算机上所有的Http请求数据。
Http协议的几个概念:
1、连接(Connection):浏览器和服务器之间传输数据的通道。一般请求完毕就关闭,不会保持连接。
2、请求(Request):浏览器向服务器发送的“我要......”的消息,包含请求的类型、请求的数据、浏览器的信息(语言、浏览器版本等)。
3、响应(Response):服务器对浏览器请求的返回数据,包含是否成功、错误码等。
Http请求报文:
用HttpWatch查看访问一个网站(用DiscuzNT测试环境)的响应情况。敲入一个网址后,浏览器向服务器发出请求,页面中的图片、JS、Css在单独的请求中。
GET /19%E9%AA%8C%E8%AF%81%E7%A0%81%E6%A1%88%E4%BE%8B/YZM.ashx HTTP/1.1
Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5
Referer: http://localhost:5048/19楠岃瘉鐮佹渚?NZMCS.aspx
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ZHCN)
Accept-Encoding: gzip, deflate
Host: localhost:5048
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=5u1v1owjcmwz1tmbnnfhz1wm
Get/Http/1.1表示向服务器用Get方式请求首页,使用Http/1.1协议
Accept-Encoding gzip、deflate表示浏览器支持gzip、deflate两种压缩算法
Accept-Language zh-cn表示浏览器支持的语言,很多进入后自动就是中文界面的国际网站就是通过读取这个头的值实现的。
Connection Keep-Alive。一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了Connection:Keep-alive,则Tcp连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
Cookie是浏览器向服务器发送和当前网站关联的Cookie,这样在服务器端也能读取浏览器端的Cookie了。
User-Agent为浏览器的版本信息。通过这个信息可以读取浏览器是IE还是FireFox、支持的插件、.Net版本等。
http:响应码:
浏览器向服务器发出请求,服务器处理可能是是成功、可能是失败、可能没有权限访问等原因,服务器会通过响应码来告诉浏览器处理结果。
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Tue, 28 Aug 2012 02:52:39 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 949
Connection: Close
以下只是服务器返回的一部分
<!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"> <head><title> </title></head> <body> <form method="post" action="NZMCS.aspx" id="form1"> <div class="aspNetHidden"> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE0MDM4MzYxMjNkZAUKOIECrqp3wBLbkTfIxmjhRosyHStua1myJqPRpqnw" /> </div> <div class="aspNetHidden"> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAwLq4dDfAQLs0bLrBgKM54rGBvmQXOijJexMKHrEz+anr1aiPF8OyrAuVu+KcrMFfUoE" /> </div> <div> <img src="YZM.ashx" onclick="this.src=‘YZM.ashx?aaa=‘+new Date()" alt="楠岃瘉鐮? /> <br /> <input name="TextBox1" type="text" id="TextBox1" /> <input type="submit" name="Button1" value="Button" id="Button1" /> </div> </form> </body> </html>
“200”:ok
“301”:Moved Permanently 永久转移
“302”:Found暂时转移
“307”:Temporary Redirect
“400”:Bad Request 错误请求,发出错误的不符合Http协议的请求
“401”:Unauthorized未认证
“403”:Forbidden禁止
“404”:Not Found未找到
“500”:Internal Server Error 服务器内部错误
“503”:Service Unavailable 一般是访问人数过多。
200段是成功;300段需要对请求做进一步的处理;400段表示客户端请求错误;500段是服务器的错误。
服务器返回的报文:
Server:Cassini/3.5.0.5表示服务器的类型
Content-Type: text/html; charset=utf-8表示返回的数据类型
服务器通过Content-Type告诉客户端响应的数据的类型,这样浏览器就根据返回数据的类型来进行不同的处理,如果是图片类型就显示,如果是文本类型就直接显示内容,如果用Html类型就用浏览器显示内容,如果是下载类型就弹出下载工具等。
常用Content-Type:text/Html、image/GIF、image/JPEG、text/plain、text/javascript、application/x-execl、application/octet-stream(二进制文件)
Content-Length:19944表示后续数据消息体的长度,报文头只是描述,返回的具体数据(比如Html文本、图片数据等)在两个回车之后的内容中。
Http其他:
http是无状态的,不会记得“上个请求***”,所以哪怕是同一个页面中的JS、CSS、JPG也都要重复的提交Accept-Language、Accept-Encoding、Cookie等。
网页中如果有图片、Css、JS等外部文件的话图片、CSS、JS都在单独的请求中,也就是并不是页面的所有内容都在一个请求中完成,而是每个资源一个请求。
一般情况下,只有浏览器请求服务器端,服务器端才有给浏览器响应数据,不会主动向浏览器推送数据,这样是安全考虑,也是提高服务器的性能考虑。如果要服务器向浏览器推送数据,则需要使用ServerPush等额外的技术。
Http是“请求--响应”的工作方式,因此页面会不断刷新,如果不希望页面刷新则要使用AJAX等技术。
断点续传的原理。多线程下载基于断点续传(*)
请求响应模型的例子:
按钮实现表格行删除效果;使用超链接删除,代码见备注。
Htm页面:
<form id="form1" action="行删除.ashx" method="post"> <input type="hidden" name="Name" id="Name" /> <table> <tr><td>姓名</td><td>年龄</td><td>操作</td></tr> <tr><td>tom</td><td>20</td><td><a href="行删除.ashx?Name=tom">删除</a><input type="button" value="删除”onclick="document.getElementById(‘Name‘).value=‘tom‘;document.getElementById(‘form1‘).submit();" /></td></tr> <tr><td>jerry</td><td>30</td><td><a href="行删除.ashx?Name=jerry">删除</a><input type="button" value="删除" onclick="document.getElementById(‘Name‘).value=‘jerry‘;document.getElementById(‘form1‘).submit();" /></td></tr> </table> </form>
一般处理程序页面:
<%@ WebHandler Language="C#" Class="行删除" %> using System; using System.Web; public class 行删除 : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/html"; string name= context.Request["Name"]; context.Response.Write(name+"被删除了!"); } public bool IsReusable { get { return false; } } }
这就是Asp.net中数据绑定控件中行按钮和行超链接实现方式的不同。ListView中Button、HyperLink两种行删除方式。按钮方式是将行的Id通过Psot提交表单到服务器,行超链接的方式是通过超链接的URL通过Get的方式提交给处理页面,超链接的方式由于没有提交所有的表单信息,因此很多服务端控件的高级用法用不了。Htm页面的默认提交方式是Get,Aspx页面的默认提交方式是Post。
新建一个Aspx页面:
<form id="form1" runat="server"> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <input type="hidden" name="Name" id="Name" /> <br /> <div> <table> <tr><td>姓名</td><td>年龄</td><td>操作</td></tr> <tr><td>tom</td><td>20</td><td><a href="行删除.aspx?Name=tom">删除</a><input type="button" value="删除" onclick="document.getElementById(‘Name‘).value=‘tom‘;document.getElementById(‘form1‘).submit();" /></td></tr> <tr><td>jerry</td><td>30</td><td><a href="行删除.aspx?Name=jerry">删除</a><input type="button" value="删除" onclick="document.getElementById(‘Name‘).value=‘jerry‘;document.getElementById(‘form1‘).submit();" /></td></tr> </table> </div> </form>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class 行删除 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (IsPostBack) { string name = Request["Name"]; Response.Write(name + "被删除了!"); } } }
用aspx重写,且超连接点击没有反应,超链接的因为没有向服务器提交ViewState等隐藏字段,所以处理时IsPostBack是False,而按钮的则是提交了表单,所以IsPostBack=True。用Httpwatch查看两种提交方式的不同。
超连接向服务器的请求内容:
GET /23%E8%A1%8C%E5%88%A0%E9%99%A4%E4%BE%8B%E5%AD%90%E8%AE%B2%E8%A7%A3%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E5%93%8D%E5%BA%94/%E8%A1%8C%E5%88%A0%E9%99%A4.aspx?Name=tom HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:3832/23琛屽垹闄や緥瀛愯瑙h姹傚鐞嗗搷搴?琛屽垹闄?aspx?Name=jerry
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ZHCN)
Accept-Encoding: gzip, deflate
Host: localhost:3832
Connection: Keep-Alive
/23%E8%A1%8C%E5%88%A0%E9%99%A4%E4%BE%8B%E5%AD%90%E8%AE%B2%E8%A7%A3%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E5%93%8D%E5%BA%94/%E8%A1%8C%E5%88%A0%E9%99%A4.aspx?Name=tom HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:3832/23琛屽垹闄や緥瀛愯瑙h姹傚鐞嗗搷搴?琛屽垹闄?aspx?Name=tom
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ZHCN)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:3832
Content-Length: 194
Connection: Keep-Alive
Cache-Control: no-cache
__VIEWSTATE=%2FwEPDwULLTE3NDI1ODEyMjhkZEgJY%2Bq9obtv87c2%2BveH5QrYkpXZYvRuWGo3pT7huSYG&__EVENTVALIDATION=%2FwEWAgKImYWIAgLs0bLrBiQCpm00f0cJvbOCF%2FKvVqqnTX27%2Fd8fsSyarynzwB5H&TextBox1=&Name=to
可以看出Button方式提交了隐藏字段。
服务端判断客户端哪个按钮被按下了
这就是Asp.net事件驱动的实现原理。
客户端、服务端由于在两台计算机中,所以无法做到两边变量的互相读取或者两边函数的互相调用,所以如果想看起来好像做到,那么必须通过提交的方式来将客户端变量值做为一个表单字段提交到服务器、或者服务端将服务端变量打印到客户端代码中。
Web开发的一些基本原则
最小权限原则。只允许用户做***,而不是“不允许用户做***”
浏览器查看的是服务端代码的执行输出的文本,除非服务器有漏洞,否则浏览者无法查看服务端的aspx、cs代码,目标另存为也是保存的aspx的执行结果,而看不到aspx的源代码。js、html是被输出到浏览器上执行的,因此无法禁止浏览者查看js、html。
C#代码是运行在服务器端的,JS代码是运行在浏览器客户端的。
能在浏览器端完成的事情,就不要到服务端去做。
客户端是不可信的。
Aspx代码:
<form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" onclick="Button1_Click" onclientclick="return confirm("真的要删除吗?")" Text="删除" />(这种“询问是否要真的删除”的代码没有必要放到服务端,放在客户端最好。return confirm("真的要删除吗?")反回“真”就执行,“假”就不执行,把这段代码放到onclientclick这个属性中) <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> <br /> <asp:Button ID="Button2" runat="server" onclick="Button2_Click" Text=""服务端"alert" />当点击这个按钮时服务器端向客户端写回了一段JavaScript代码,这段代码得到先执行(查看htm源代码可以看到JavaScript在最顶上,所以先执行),等用户“确定”后才渲染出htm页面上的其它内容。但它并没有阻断服务器端代码的执行。 <br /> <asp:Button ID="Button3" runat="server" onclick="Button3_Click" Text="服务器端MessageBox.Show" /> <br /> <asp:Button ID="Button4" runat="server" onclick="Button4_Click" Text="中木马" />这个中木马也是写到服务器上,不会写到客户端。 <br /> <asp:Button ID="Button5" runat="server" onclick="Button5_Click" Text="隐藏" /> <input type="button" value="客户端隐藏" onclick="document.getElementById(‘TextBox1‘).style.display=‘none‘;" /> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> </div> </form>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Windows.Forms; using System.IO; public partial class 客户端确认 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { Label1.Text = "删除成功" + DateTime.Now; } protected void Button2_Click(object sender, EventArgs e) { Response.Write("<script type=‘text/javascript‘>alert(‘你点出我啦!‘);</script>"); } protected void Button3_Click(object sender, EventArgs e) { MessageBox.Show("多简单呀,服务端弹出对话框!");//在项目中添加对System.Windows.Forms的引用,然后在服务器端的代码中MessageBox.Show("Hello"),就会弹出windows的messageBox哪种对话框。这种写法在浏览器端是看不到的,只有在服务器端能看到执行结果。 } protected void Button4_Click(object sender, EventArgs e) { File.WriteAllText("c:/muma.exe", "木马(){葵花点穴手();降龙十八掌();熊猫烧香();}");//这个程序也只会输出到服务器,不会输出到浏览者的机子上。 } protected void Button5_Click(object sender, EventArgs e) { TextBox1.Visible = false; } }
原则1:
C#代码是运行在服务器端的,JS代码是运行在浏览器客户端的按钮确认提交的实现在Button的OnClient中写。<input type="submit" name="delete" value="删除" onclick="return confirm(‘真的要删除吗?‘)"/>,代码是运行在浏览器端的,和服务器没有关系。
在服务器端“弹出消息窗口” context.Response.Write("<script type=‘text/javascript‘>alert(‘删除成功‘</script>)");并不是真的是在服务器端运行的,只是生成了JavsScript代码到浏览器端,浏览器会在解析方根的时候运行alert,不推荐用这种写法,读懂即可,推荐用后面讲的RegisterClientStartupScript。只是渲染到浏览器端,所以并不会得到对话框关闭服务端的代码才会执行下去(在context.Response.Write("script type=‘text/javascript‘>alert(‘删除成功‘</script>)"后设置断点)对于服务器端的代码来说,生成一堆HTML代码就是一堆字符串,没有任何意义,只有到了浏览器端执行才有意义。
案例1:在项目中添加对System.Windows.Forms的引用,然后MessageBox.Show("Hello");用CassiniDev.exe启动测试程序让学生们远程测试。证明C#代码是运行在服务器端的。
127.0.0.1是回环地址(LoopBack),就是表示访问本机,数据不走网卡。localhost就是127.0.0.1别名,是无法在外部访问。0.0.0.0是任意IP(Any IP),不用写死在绑定的IP了,通过任何一块网卡都可以访问网络程序。
案例2:伟大的Asp.net,可以在访问者磁盘中创建木马文件,File.WriteAllText("c:/muma.exe","木马(){葵花点穴手();降龙十八掌();熊猫烧香();}");
用CassiniDev.exe启动测试程序让学生们远程测试(VS内置的服务器不能远程访问)。exe生成到了服务器的磁盘中,而不是访问者的磁盘中,因为C#代码是运行在服务器中的,而不是浏览器中的,浏览器得到的只有返回的HTML内容。
案例3:两个学生分别访问点击自增1的界面,互不影响,因为不同用户的变量内容是隔离的。
原则2: 能在浏览器端完成的事情,就不要到服务端去做。
按钮隐藏一个控件就不要写服务端代码,在客户端用JavaScritp、Dom来操作就可以。比如要操作数据库,显然是在浏览器端做不到的,这时候就要写服务端代码。校验用户名、密码这样的操作可以放到浏览器端(用户名、密码是写死的),技术上可以,但是这样安全性太差,因此必须放到服务器端。
原则3: 客户端验证不能代替服务端验证
Aspx页面:
<form id="form1" runat="server"> <div> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:Button ID="Button1" runat="server" OnClientClick="var value=document.getElementById(‘TextBox1‘).value;if(parseInt(value,10)>100){alert(‘金额不能大于100‘);return false;}" onclick="Button1_Click" Text="取款" /> <!--<asp:Button来讲,onclick是服务端事件,OnClientClick是最终生成到浏览器中的onclick代码--> <asp:Label ID="Label1" runat="server"></asp:Label> </div> </form>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class 取款 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { if(Convert.ToInt32(TextBox1.Text)>100) { Label1.Text = "禁止取款金额大于100"; } else { Label1.Text = "取款成功,金额:" + TextBox1.Text; } } }
上例如果只在客户端做检验,禁用了客户端JavaScript,就能取出大于100元的存款,所以服务端也要检验。客户端的检验是为了方便用户,服务器端检验是为了把关。<asp:Button来讲,onclick是服务端事件,OnClientClick是最终生成到客户端浏览器的onclick代码.
设置取款金额不能高于100元,客户端:<form id="form1" runat="server" onsubmit="if(parseInt(document.getElementById(‘TextBox1‘).value.10).100){alert(‘最多只能取款100元‘);return false}">。服务器端:Label1.Text="取款成功,金额:"+TextBox1.Text;
如果禁用JavaScript(Internet选项-安全-自定义级别-脚本-活动脚本-禁用,用maxthon里的【内容控制】-【禁用JavaScript】进行讲课),那么客户端JavaScript检验就被禁用了,就可以取款多于100元了。
在服务端也要进行数据校验。
客户端校验是为了很好的客户端体验,服务器端校验是最后一次把关,防止恶意请求。后面要讲的Asp.net Validation就是Asp.net内置的数据校验技术,会在客户端和服务器端同时校验。
原则4:不要把敏感数据、算法写在浏览器端:
$("#btnLogin").click(function(){ if($("#username").val()=="admin"&&$("#password").val()=="123456"){ alert("登录成功!"); } else{ alert("登录失败!"); } });
用户在浏览器中查看源代码就可以看到用户名、密码是什么。在动态网站还是很稀有的哪个年代,很多个人主页都是用这种方法进行数据的“保密”。
原则5:不要把机密信息隐藏在html中
例:只有密码输对了才显示下载地址,实现代码
Aspx页面:
<form id="form1" runat="server"> <div> <input type="text" id="password" /><input type="button" value="确定" onclick="var password=document.getElementById(‘password‘).value;if(password==‘111‘){document.getElementById(‘div1‘).style.display=‘‘;}" /> <div id="div1" style="display:none;"><a href="http://www.xxx.com/xxx333.zip">点击下载</a></div> 密码这样设置用户查看源代码就看到了密码。 <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="确定" /> <br /> <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="http://www.xxx.com//zz.zip" Visible="False">下载地址</asp:HyperLink> 这样用户输入不对密码就看不到下载地址。 </div> </form>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class 下载地址隐藏 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { if (TextBox1.Text == "111") { HyperLink1.Visible = true; } } }
应该在服务端控制密码不对则Visible=False,服务端控件的HyperLink1.Visible=False是根本不输出到客户端。在和JQuery等结合的时候是无法用$("#控件ID").show()来显示Visible=False的控件,因为控件根本没有渲染到Html中。应该是在机密页面打开之前做权限校验,而不是在一个页面中做校验,如果正确就倒向机密页面,不正确就不导向。
XSS漏洞:
不要轻信用户提交上来的数据
Aspx页面:
<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" CodeFile="ShowMsg.aspx.cs" Inherits="ShowMsg" %> <!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"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> </div> </form> <p> 中国工商银行ICBC</p> </body> </html>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class ShowMsg : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Label1.Text=Request["Msg"]; } }
再建一个aspx页面:
<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!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"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" /> <asp:Literal ID="Literal1" runat="server" Mode="Encode" Text="<a href="#">哈哈</a>"></asp:Literal> <asp:Label ID="Label1" runat="server" Text="<a href="#">哈哈</a>"></asp:Label> </div> </form> <!-- <form action="http://www.nrpeng.com/haha.aspx" method="get">登陆网银领取工行赠送礼金50元。帐户<input type="text" name="acctno" />密码:<input type="password" name="pwd" /><input type="submit" value="领取" /> </form> --> </body> </html>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { Response.Redirect("ShowMsg.aspx?Msg=用户名不能为空!");//使页面导向ShowMsg.aspx //Response.Redirect("ShowMsg.aspx?Msg=<script type=‘text/javascript‘>alert(‘工行领导说,送小汽车一辆,联系44444领取!‘)"); } }
运行这个页面,点击Button,页面就显示“用户名不能为空!”。利用这个漏洞,可以传好多好玩的东西,如“<script type=‘text/javascript‘>alert(‘工行领导说,送小汽车一辆,联系44444领取!‘)”、“<form action="http://www.nrpeng.com/haha.aspx" method="get">登陆网银领取工行赠送礼金50元。帐户<input type="text" name="acctno" />密码:<input type="password" name="pwd" /><input type="submit" value="领取" /> </form>
”可能把银行的帐号和密码都给他人了。
注:要使上面程序正常运行,必须将要传的数据先URLEncode,再传过去,才能正常显示。在Asp.net中这个漏洞已经关了,要看效果,还必须将ValidateRequest="false"添加到apsx页面顶部。经过URLEncode后的地址发送到网上,他人一点,就显示你发的内容。
alert消息太难看,因此开发一个ashx页面用来统一展示消息ShowMessage.ashx
context.Response.Write("<center><font>color=‘red‘>"+context.Reques["msg"]+"</font></center>");
context.Response.Write("<a href=‘javascript history.back();‘>返回上一页</a>");
系统内部需要弹出消息的时候只要将用户Redirect到ShowMessage.ashx?msg=消息就可以,比如:Response.Redirect("ShowMessage.ashx?msg=用户名不能为空!");
利用漏洞1:送奖品的消息框。
利用漏洞2:收集帐号、密码。
上面的这两个例子就是XSS(跨站脚本,Cross-site scripting)
XSS漏洞2:
用户发贴时也存在Xss的问题。将发帖内容保存到一个文本文件中,不用数据库,降低问题复杂度。代码见程序。
<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" CodeFile="添加贴.aspx.cs" Inherits="添加贴" %> <!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"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <asp:TextBox ID="TextBox1" runat="server" Height="134px" TextMode="MultiLine" Width="380px"></asp:TextBox> <div> <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" /> </div> <!-- <script type="text/javascript"> alert(‘去如鹏网吧!‘); </script> --> </form> </body> </html>
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.IO; public partial class 添加贴 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { File.AppendAllText("c:/1.txt", TextBox1.Text + "<br/>"); } }
看贴aspx页面:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="看贴.aspx.cs" Inherits="看贴" %> <!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"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html>
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.IO; public partial class 看贴 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string s = File.ReadAllText("c:/1.txt"); Response.Write(HttpUtility.HtmlEncode(s)); Response.Write(File.ReadAllText("c:/1.txt")); } }
在发贴页面的文本框中输入“<script type="text/javascript">
alert(‘去如鹏网吧!‘);”,在看贴页面中浏览页面就会显示对话框“去如鹏网吧!”
我们可以对请求的数据做检测,如果请求数据中有<等就认为是恶意请求,禁止提交。aspx默认就是采用这种策略,这样做的缺点是如果做的是一个程序员讨人论坛,程序员就无法发表HTML代码的帖子了,因此更好的处理策略是将用户发表的内容按照原样显示出来,而不是以HTML的方式显示出来。使用HttpUtility.HtmlEncode就可以将字符串中的<、/、等特殊字符转换为HTML显示的字符,也就是不把<script>当成定义的脚本的标签,而是当成"<script>"这样可以在页面上直接显示出来的内容。
修改看贴代码,将context.Response.Write(line+"<hr/>")修改为context.Response.Write(HttpUtility.HtmlEncode(line)+"<hr/>");即可。
XSS漏洞3:
aspx中默认对请求的数据进行了校验,如果数据中有<、/等有潜在Xss攻击的字符,则会报错。对于一些CMS系统确实需要提交HTML内容的地方要关闭它,在页面顶部的Page中加入ValidateRequest="false"这个属性。
在显示的时候如果需要对内容在显示之前进行HTMLEncode,除了可以使用HttpUtility.HtmlEncode进行手动编码的话,还可以使用Lieral控件显示,如果修改Literal的Mode属性为Encode那么就会自动进行HtmlEncode然后显示。