转自: http://www.cnblogs.com/sunkaixuan/p/4550580.html
只用JavaScript验证安全不安全
谁都知道,答案是不安全,非常的不安全。因为在客户端进行的验证相当于“让用户自己验证自己”,很明显是不靠谱的。你不能避免一些恶意用户人为的修改自己的表单进行欺骗,也不能避免第三方对表单进行截获后进行篡改再提交。
所以说,从安全的角度来说,单纯的依靠js验证,是不安全的,任何健壮的系统都必须在后端进行验证。
双验证大大增加了工作量,如何解决?
方案1:笨方法,都写一遍
方案2:现有框架 ,比如MVC自带验证支持双向验证 ,不足点是要写 model加attrbute 也要有一定工作量
方案3:自已封装
我的选择方案:方案3
思路
page 加载时通过Key去存储表 form规则,通过form规则生成前台元素的绑定,完成前台验证。后台函数通过key在获取表单规则进行后台验证。(可以用缓存机质提高性能)
实现
后台代码:
通过GetInitScript存储form规则并且赋值给 ViewState["intisript"]去前台绑定
前台调用只要绑定 viewState["intiscript"] (其实什么都不要写,保证元素name和 viewstate中一致就可以了):
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
|
<body> <form id= "form1" runat= "server" class = "contact_form" > <ul> <li> <h2> 表单验证</h2> <span class = "required_notification" >* 表示必填项</span> </li> <li> <label for = "name" > 姓名:</label> <input type= "text" name= "name" /> </li> <li> <label> 姓别:</label> <input type= "radio" value= "1" name= "sex" />男 <input type= "radio" value= "0" name= "sex" />女 </li> <li> <label for = "email" > 电子邮件:</label> <input type= "email" name= "email" /> </li> <li> <label for = "website" > 手 机:</label> <input type= "text" name= "phone" /> </li> <li> <label for = "website" > 学 历:</label> < select name= "education" > <option value= "" >==请选择==</option> <option value= "1" >大学</option> </ select > </li> <li> <label for = "message" > 备注:</label> <textarea name= "remark" cols= "40" rows= "6" ></textarea> </li> <li></li> </ul> <br /> <asp:Button ID= "Button1" runat= "server" Text= "submit" CssClass= "submit" OnClick= "Button1_Click" /> </form> <span style= "color: #ff0000;" > <%=ViewState[ "intiscript" ]%></span> </body> |
ViewState["intiscript"] 将生成一段脚本 给HTML元素添加 pattern、placeholder和requierd 等属性 ,有了这些属性可以很方便的使用JS等插件进行前端验证
下面是通过ViewState["intiscript"] 生成出来的HTML
1
|
|
后台使用 PostValidation函数进行验证
我们来看看效果:
提交成功验证通过了,下面我来改下前端元素采 用恶意参数 提交后台
前台验证通过:
后台还是要把你给揪出来
最后附上C#验证类代码:
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; /// <summary> /// ** 描述:可以方便实现前后端双验证,基于jquery /// ** 创始时间:2015-6-4 /// ** 修改时间:- /// ** 作者:sunkaixuan /// ** 使用说明:- /// </summary> public class ValidationSugar { private static List<ValidationOption> ValidationOptionList = new List<ValidationOption>(); /// <summary> /// 前台注入 /// </summary> /// <param name="pageKey"></param> /// <param name="itemList"></param> public static string GetInitScript( string pageKey, List<OptionItem> itemList) { //初始化后不在赋值 if (ValidationOptionList.Any(it => it.PageKey == pageKey)) { return (ValidationOptionList.Single(c => c.PageKey == pageKey).Script); } else { ValidationOption option = new ValidationOption(); string uk = Guid.NewGuid().ToString().Replace( "-" , "" ); //唯一函数名 string script = @"<script> var bindValidation{1}=function(name,params){{ var selectorObj=$(""[name='""+name+""']""); selectorObj.after(""<span class=\""form_hint\"">""+params.tip+""</span>""); if(params.pattern!=null) selectorObj.attr(""pattern"",params.pattern); if(params.placeholder!=null) selectorObj.attr(""placeholder"",params.placeholder); if(params.isRequired=true) selectorObj.attr(""required"",params.isRequired); }} {0}</script>" ; StringBuilder itemsCode = new StringBuilder(); foreach ( var item in itemList) { switch (item.Type) { case OptioItemType.Mail: item.Pattern = @"^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$" ; break ; case OptioItemType.Int: item.Pattern = @"^\\d{1,11}$" ; break ; case OptioItemType.Double: item.Pattern = @"^\\d{1,11}$" ; break ; case OptioItemType.IdCard: item.Pattern = @"^(\\d{15}$|^\\d{18}$|^\\d{17}(\\d|X|x))$" ; break ; case OptioItemType.Date: item.Pattern = @"^(((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/])(10|12|0?[13578])([-\\/])(3[01]|[12][0-9]|0?[1-9])$)|(^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/])(11|0?[469])([-\\/])(30|[12][0-9]|0?[1-9])$)|(^((1[8-9]\\d{2})|([2-9]\\d{3}))([-\\/])(0?2)([-\\/])(2[0-8]|1[0-9]|0?[1-9])$)|(^([2468][048]00)([-\\/])(0?2)([-\\/])(29)$)|(^([3579][26]00)([-\\/])(0?2)([-\\/])(29)$)|(^([1][89][0][48])([-\\/])(0?2)([-\\/])(29)$)|(^([2-9][0-9][0][48])([-\\/])(0?2)([-\\/])(29)$)|(^([1][89][2468][048])([-\\/])(0?2)([-\\/])(29)$)|(^([2-9][0-9][2468][048])([-\\/])(0?2)([-\\/])(29)$)|(^([1][89][13579][26])([-\\/])(0?2)([-\\/])(29)$)|(^([2-9][0-9][13579][26])([-\\/])(0?2)([-\\/])(29))|(((((0[13578])|([13578])|(1[02]))[\\-\\/\\s]?((0[1-9])|([1-9])|([1-2][0-9])|(3[01])))|((([469])|(11))[\\-\\/\\s]?((0[1-9])|([1-9])|([1-2][0-9])|(30)))|((02|2)[\\-\\/\\s]?((0[1-9])|([1-9])|([1-2][0-9]))))[\\-\\/\\s]?\\d{4})(\\s(((0[1-9])|([1-9])|(1[0-2]))\\:([0-5][0-9])((\\s)|(\\:([0-5][0-9])\\s))([AM|PM|am|pm]{2,2})))?$" ; break ; case OptioItemType.Mobile: item.Pattern = @"^[0-9]{11}$" ; break ; case OptioItemType.Telephone: item.Pattern = @"^(\\(\\d{3,4}\\)|\\d{3,4}-|\\s)?\\d{8}$" ; break ; case OptioItemType.Fax: item.Pattern = @"^[+]{0,1}(\\d){1,3}[ ]?([-]?((\\d)|[ ]){1,12})+$" ; break ; case OptioItemType.Regex: break ; } itemsCode.AppendFormat( "bindValidation{0}('{1}',{{ tip:'{2}',pattern:'{3}',placeholder:'{4}',isRequired:{5} }})" , uk, item.FormFiledName, item.Tip, item.Pattern, item.Placeholder, item.IsRequired ? "true" : "false" ); itemsCode.AppendLine(); } option.Script = string .Format(script, itemsCode.ToString(), uk); script = null ; itemsCode.Clear(); option.PageKey = pageKey; option.ItemList = itemList; ValidationOptionList.Add(option); return (option.Script); } } /// <summary> /// 后台验证 /// </summary> /// <param name="pageKey"></param> /// <param name="errorMessage">json格式</param> /// <returns></returns> public static bool PostValidation( string pageKey, out string errorMessage) { bool isSuccess = true ; errorMessage = string .Empty; if (!ValidationOptionList.Any(c => c.PageKey == pageKey)) { throw new ArgumentNullException( "ValidationSugar.PostValidation.pageKey" ); } var context = System.Web.HttpContext.Current; var itemList = ValidationOptionList.Where(c => c.PageKey == pageKey).Single().ItemList; var successItemList = itemList.Where(it => (it.IsRequired && ! string .IsNullOrEmpty(context.Request[it.FormFiledName]) || !it.IsRequired)).Where(it => Regex.IsMatch(context.Request[it.FormFiledName], it.Pattern.Replace( @"\\" , @"\" ))).ToList(); isSuccess = (successItemList.Count == itemList.Count); if (!isSuccess) { errorMessage = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(itemList); } return isSuccess; } private class ValidationOption { public string PageKey { get ; set ; } public string Script { get ; set ; } public List<OptionItem> ItemList { get ; set ; } } public enum OptioItemType { Mail = 0, Int = 2, Double = 3, IdCard = 4, Date = 5, /// <summary> /// 移动电话 /// </summary> Mobile = 6, /// <summary> /// 座机 /// </summary> Telephone = 7, Fax = 8, /// <summary> /// 没有合适的,请使用正则验证 /// </summary> Regex = 1000 } /// <summary> /// 验证选项 /// </summary> public class OptionItem { /// <summary> /// 验证类型 /// </summary> public OptioItemType Type { get ; set ; } /// <summary> /// 正则 /// </summary> public string Pattern { get ; set ; } /// <summary> /// 是否必填 /// </summary> public bool IsRequired { get ; set ; } /// <summary> /// 表单字段名(name或者id) /// </summary> public string FormFiledName { get ; set ; } /// <summary> /// 水印 /// </summary> public string Placeholder { get ; set ; } /// <summary> /// 提醒 /// </summary> public string Tip { get ; set ; } } } |
源码下载:http://pan.baidu.com/s/1mgoXpsW
时间问题只支持HTML5验证,需要高版本浏览器,以后我会慢慢完善