views:

43

answers:

1

I am calling on the collective mind of the stackoverflow community to help this novice programmer fix a major issue.

I have a multipart form written in Classic ASP that is based on the code below. I use stored procedures and parameters to write to the sql DB, I use Server.HTMLEncode before it is submitted also. I have javascript based validation (jquery validation plugin) as well as server side ASP validation for all the fields. I am not worried about injection but the page is vulnerable to XSS code listed below.

My question is: How do I prevent this type of cross site scripting on a classic ASP page like the one below?

Basically, all the data is collected then on the last "page" after submit is hit I run it through the server side validation. But I need to know how to prevent XSS before the user gets to the submission point.

Thanks very very much in advance for any thorough advice and tips!

XSS CODE:

';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>

CODE:

    <%
Const NUMBER_OF_PAGES = 3

Dim intPreviousPage
Dim intCurrentPage
Dim strItem

' What page did we come from?
intPreviousPage = Request.Form("page")

' What page are we on?
Select Case Request.Form("navigate")
    Case "< Back"
        intCurrentPage = intPreviousPage - 1
    Case "Next >"
        intCurrentPage = intPreviousPage + 1
    Case Else
        ' Either it's our first run of the page and we're on page 1 or
        ' the form is complete and pages are unimportant because we're
        ' about to process our data!
        intCurrentPage = 1
End Select

' If we're not finished then display the form.
If Request.Form("navigate") <> "Finish" Then %>
    <form action="<%= Request.ServerVariables("URL") %>" method="post">
    <input type="hidden" name="page" value="<%= intCurrentPage %>">

    <%
    ' Take data and store it in hidden form fields.  All our fields are
    ' prefixed with numbers so that we know what page it belongs to.
    For Each strItem In Request.Form
        ' Ignore the "page" and "navigate" button form fields.
        If strItem <> "page" And strItem <> "navigate" Then
            ' If the data is from the current page we don't need
            ' the hidden field since the data will show in the visible
            ' form fields.
            If CInt(Left(strItem, 1)) <> intCurrentPage Then
                Response.Write("<input type=""hidden"" name=""" & strItem & """" _
                & " value=""" & Request.Form(strItem) & """>" & vbCrLf)
            End If
        End If
    Next

    ' Display current page fields.  The fields are all named with
    ' numerical prefix that tells us which page they belong to.
    ' We need a Case for each page.
    Select Case intCurrentPage
        Case 1
            %>
            <table>
            <tr>
                <td><strong>Name:</strong></td>
                <td><input type="text" name="1_name"  value="<%= Request.Form("1_name") %>"></td>
            </tr><tr>
                <td><strong>Email:</strong></td>
                <td><input type="text" name="1_email" value="<%= Request.Form("1_email") %>"></td>
            </tr>
            </table>
            <%
        Case 2
            %>
            <table>
            <tr>
                <td><strong>Address:</strong></td>
                <td><input type="text" name="2_address" value="<%= Request.Form("2_address") %>"></td>
            </tr><tr>
                <td><strong>City:</strong></td>
                <td><input type="text" name="2_city"    value="<%= Request.Form("2_city") %>"></td>
            </tr><tr>
                <td><strong>State:</strong></td>
                <td><input type="text" name="2_state"   value="<%= Request.Form("2_state") %>"></td>
            </tr><tr>
                <td><strong>Zip:</strong></td>
                <td><input type="text" name="2_zip"     value="<%= Request.Form("2_zip") %>"></td>
            </tr>
            </table>
            <%
        Case 3
            ' Notice that you can do other types of form fields too.
            %>
            <table>
            <tr>
                <td><strong>Sex:</strong></td>
                <td>
                    <input type="radio" name="3_sex" value="male"   <% If Request.Form("3_sex") = "male"   Then Response.Write("checked=""checked""") %>>Male
                    <input type="radio" name="3_sex" value="female" <% If Request.Form("3_sex") = "female" Then Response.Write("checked=""checked""") %>>Female
                </td>
            </tr><tr>
                <td><strong>Age:</strong></td>
                <td>
                    <select name="3_age">
                        <option></option>
                        <option<% If Request.Form("3_age") = "< 20"    Then Response.Write(" selected=""selected""") %>>&lt; 20</option>
                        <option<% If Request.Form("3_age") = "20 - 29" Then Response.Write(" selected=""selected""") %>>20 - 29</option>
                        <option<% If Request.Form("3_age") = "30 - 39" Then Response.Write(" selected=""selected""") %>>30 - 39</option>
                        <option<% If Request.Form("3_age") = "40 - 49" Then Response.Write(" selected=""selected""") %>>40 - 49</option>
                        <option<% If Request.Form("3_age") = "50 - 59" Then Response.Write(" selected=""selected""") %>>50 - 59</option>
                        <option<% If Request.Form("3_age") = "60 - 69" Then Response.Write(" selected=""selected""") %>>60 - 69</option>
                        <option<% If Request.Form("3_age") = "70 - 79" Then Response.Write(" selected=""selected""") %>>70 - 79</option>
                        <option<% If Request.Form("3_age") = "80 +"    Then Response.Write(" selected=""selected""") %>>80 +</option>
                    </select>
                </td>
            </tr>
            </table>
            <%
        Case Else
            ' You shouldn't see this error unless something goes wrong.
            Response.Write("Error: Bad Page Number!")
    End Select
    %>
    <br />
    <!-- Display form navigation buttons. -->
    <% If intCurrentPage > 1 Then %>
        <input type="submit" name="navigate" value="&lt; Back">
    <% End If %>
    <% If intCurrentPage < NUMBER_OF_PAGES Then %>
        <input type="submit" name="navigate" value="Next &gt;">
    <% Else %>
        <input type="submit" name="navigate" value="Finish">
    <% End If %>
    </form>
    <%
Else
    ' This is where we process our data when the user submits the final page.
    ' I just display the data, but you're free to store the data in a
    ' database, send it via email, or do whatever you want with it.

    'For Each strItem In Request.Form
    '   Response.Write(strItem & ": " & Request.Form(strItem) & "<br />" & vbCrLf)
    'Next
    %>
    <p><strong>
    Here's what you entered:
    </strong></p>

    <pre>
    <strong>Name:</strong>    <%= Request.Form("1_name") %>
    <strong>Email:</strong>   <%= Request.Form("1_email") %>
    <strong>Address:</strong> <%= Request.Form("2_address") %>
    <strong>City:</strong>    <%= Request.Form("2_city") %>
    <strong>State:</strong>   <%= Request.Form("2_state") %>
    <strong>Zip:</strong>     <%= Request.Form("2_zip") %>
    <strong>Sex:</strong>     <%= Request.Form("3_sex") %>
    <strong>Age:</strong>     <%= Request.Form("3_age") %>
    </pre>

    <p>
    <a href="<%= Request.ServerVariables("URL") %>">Start Again</a>
    </p>
    <%
End If
%>
+1  A: 

You should use Server.HTMLEncode before writing the user input to the page, not before writing to the DB. In fact, it's better to store non encoded values in the DB to avoid double encoding.

Fixed code:

Case 1
%>
<table>
  <tr>
    <td><strong>Name:</strong></td>
    <td><input type="text" 
               name="1_name" 
               value="<%= Server.HTMLEncode(Request.Form("1_name")) %>"></td>
  </tr>
  <tr>
    <td><strong>Email:</strong></td>
    <td><input type="text" 
               name="1_email" 
               value="<%= Server.HTMLEncode(Request.Form("1_email")) %>"></td>
  </tr>
</table>
<%

Also, make sure that Request.Form("page") is a number

intPreviousPage = TryCLng(Request.Form("page"))

Internal function of mine

function TryCLng( NumeroEnTexto )
    if isNumeric( NumeroEnTexto ) then
        TryCLng = clng(NumeroEnTexto)
    else
        TryCLng = 0
    end if
end function
Eduardo Molteni
Server.HTMLEncode doesn't seem to work. Since the page posts back to itself, after I "submit" the first page of data and lets say put some html like `<a href="asdf.com">asdf</a>` then look at the source after the page reloads, i see this: <input type="hidden" name="1_name" value="<a href="asdf.com">asdf</a>">Even while using `value="<%= Server.HTMLEncode(Request.Form("1_name")) %>"` it still renders the html in the hidden field.How can that be?
jethomas