Optional Client-Side Input Validation That Matches Server-side Validation
It is common practice to make forms more user-friendly by giving immediate feedback on the inputs with client-side scripting. Everyone with a bit of secure programming knowledge knows, however, that the server side needs to do the final input validation. If the two validations are not equivalent, then an input that passes client-side validation may be rejected later, confusing and annoying the customer, or the client-side validation may be needlessly restrictive. Another problem is when the form stops working if JavaScript is disabled, due to the way input validation was attempted.
I was delighted to discover that the regular expression syntax in JavaScript and Ruby match, and the matching differs only in greedy vs non-greedy behavior, and not whether a match is possible or not. This means that regular expressions describing a white list of correct inputs can be used for both (this probably works for Perl, Python and PHP as well but I haven’t checked).
In the code for ReAssure, all inputs are defined by classes that create the html for forms, as well as perform input validation. This means that the regular expression can be defined in a single place, when the class is instantiated:
def initialize(...) (...) @regexp = Regexp.new(/^\d+$/) # positive integer end
This regular expression can be used to perform the initial server-side input validation:
def validate(input) if input == nil unescaped = default() else unescaped = CGI.unescapeHTML(input.to_s.strip) end unescaped.scan(@regexp) { |match| return @value = match.untaint } if input != '' raise 'Input "' + @ui_name + '" is not valid' end end
To perform client-side input validation, the onblur event is used to trigger validation when focus is lost. The idea is to make the input red and bold (for color-blind people) when validation fails, and green when it passes. The onfocus event is used to restore the input to a neutral state while editing (this is the Ruby code that generates the form html):
def form $cgi.input('NAME'=>@name, 'VALUE'=>to_html(), 'onblur' => onblur(), 'onfocus' => onfocus()) end def onblur() return "if (this.value.search(/" + @regexp.source + "/) < 0) {this.className = 'bad'} else {this.className = 'good'};" end def onfocus() return "this.className = 'normal';" end
where the classes “bad”, “good” and “normal” are specified in a style sheet (CSS).
There are cases when more validation may happen later on the server side, e.g., if an integer must match an existing key in a database that the user may be allowed to reference. Could the extra validation create a mismatch? Perhaps. However, in these cases the client-side interface should probably be a pre-screened list and not a free-form input, so the client would have to be malicious to fail server-side validation. It is also possible to add range (for numbers) and size (for strings) constraints in the “onblur” JavaScript. In the case of a password field, the JavaScript contains several checks matching the rules on the server side. So, a lone regular expression may not be sufficient for complete input validation, but it is a good starting point.
Note that the form still works even if JavaScript is disabled! As you can see, it is easy to perform client-side validation without forcing everyone to turn on JavaScript
on Saturday, June 9, 2007 at 06:47 AM