12

I have put the following in the web.xml of my application to attempt to disallow PUT, DELETE, etc.:

 <security-constraint>
 <web-resource-collection>
  <web-resource-name>restricted methods</web-resource-name>
  <url-pattern>/*</url-pattern>
  <http-method>DELETE</http-method>
  <http-method>PUT</http-method>
  <http-method>SEARCH</http-method>
  <http-method>COPY</http-method>
  <http-method>MOVE</http-method>
  <http-method>PROPFIND</http-method>
  <http-method>PROPPATCH</http-method>
  <http-method>MKCOL</http-method>
  <http-method>LOCK</http-method>
  <http-method>UNLOCK</http-method>
  <http-method>delete</http-method>
  <http-method>put</http-method>
  <http-method>search</http-method>
  <http-method>copy</http-method>
  <http-method>move</http-method>
  <http-method>propfind</http-method>
  <http-method>proppatch</http-method>
  <http-method>mkcol</http-method>
  <http-method>lock</http-method>
  <http-method>unlock</http-method>
 </web-resource-collection>
 <auth-constraint />
 </security-constraint>

Ok, so now:

If I do a request with method of DELETE I get a 403 back.

If I do a request with method of delete I get a 403 back.

BUT

If I do a request with method of DeLeTe I get OK!

How can I make it disallow these case-insensitive?

Edit: I'm testing it with a C# program:

    private void button1_Click(object sender, EventArgs e)
    {
        textBox1.Text = "making request";
        System.Threading.Thread.Sleep(400);
        WebRequest req = WebRequest.Create("http://serverurl/Application/cache_test.jsp");
        req.Method = txtMethod.Text;
        try
        {
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

            textBox1.Text = "Status: " + resp.StatusCode;

            if (resp.StatusCode == System.Net.HttpStatusCode.OK)
            {
                WebHeaderCollection header = resp.Headers;
                using (System.IO.StreamReader reader = new System.IO.StreamReader(resp.GetResponseStream(), ASCIIEncoding.ASCII))
                {
                    //string responseText = reader.ReadToEnd();
                    textBox1.Text += "\r\n" + reader.ReadToEnd();
                }
            }
        }
        catch (Exception ex)
        {
            textBox1.Text = ex.Message;
        }
    }

txtMethod.Text is a text box where I'm typing the method name. When there is a 403 an exception is thrown which is caught in the catch block.

The cache_test.jsp contains:

<%
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma","no-cache");

out.print("Method used was: "+request.getMethod());
%>
developerwjk
  • 253
  • 1
  • 3
  • 8
  • How do you test it ? – Xavier Lucas Jan 26 '15 at 23:20
  • @XavierLucas, Added that to the question – developerwjk Jan 26 '15 at 23:31
  • 1
    Your test program is flawed. `HttpWebRequest` will [case-insensitively](http://referencesource.microsoft.com/#System/net/System/Net/Internal.cs,1944) [recognise and convert](http://referencesource.microsoft.com/#System/net/System/Net/HttpWebRequest.cs,2443) standard HTTP methods to uppercase. Additionally, it is [documented](https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.method.aspx) as only allowing standard HTTP methods. The best option is to use a raw TCP stream (e.g. in netcat, or PuTTY raw, or telnet, etc.). – Bob Jan 27 '15 at 23:54
  • 1
    @Bob, I did it in .NET 2.0 in Visual C# 2005 Express and didn't run into any of those issues. It is sending exactly what I type. So they must have changed that in a later version. – developerwjk Jan 28 '15 at 20:21
  • 1
    @Bob, Lol. Microsoft's documentation is wrong/misleading. For the .NET 2.0 version they also say "The Method property can be set to any of the HTTP 1.1 protocol verbs: GET, HEAD, POST, PUT, DELETE, TRACE, or OPTIONS." But it doesn't restrict to that in any way in practice. – developerwjk Jan 28 '15 at 20:23

2 Answers2

14

Regardless of Tomcat's incorrect behaviour with regards to the HTTP standard, you should be using a whitelist to allow specific methods rather than a blacklist.

For example, the following whitelist will block all methods except the case-sensitive GET and HEAD.

<security-constraint>
    <web-resource-collection>
        <web-resource-name>restricted methods</web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method-omission>GET</http-method-omission>
        <http-method-omission>HEAD</http-method-omission>
    </web-resource-collection>
    <auth-constraint />
</security-constraint>

(Note: requires Tomcat 7+. Those using older versions will have to investigate other solutions, e.g. a servlet filter.)

Ref

Bob
  • 1,536
  • 12
  • 17
  • When I do that with POST also included, I go to a page on the site (just clicking a link or bookmark to it) and it gives me a 405. – developerwjk Jan 27 '15 at 17:47
  • Actually it gives me HTTP Status 403 - Access to the requested resource has been denied – developerwjk Jan 27 '15 at 17:51
  • I've tried it in the server's web.xml, and it ignored the omissions and just blocked everything. Took that out. Tried it in the application's web.xml, and again, its just blocking every method and ignoring the omissions. – developerwjk Jan 27 '15 at 18:02
  • Also tried exactly as above but taking out `` and then it just allows everything. – developerwjk Jan 27 '15 at 18:10
  • Thought maybe it could be that this server was still on Tomcat 5, but no, just tested it on another server that's Tomcat 6. Same results. – developerwjk Jan 27 '15 at 18:21
  • 2
    @developerwjk `http-method-omission` was first defined in in Servlet API 3.0, which is implemented by Tomcat 7+: http://tomcat.apache.org/whichversion.html. Unfortunately, this means this won't work in Tomcat 6 and older (note: 5 is EOL already). You can try the other proposed solution in the [linked SO question](http://stackoverflow.com/questions/8069640/whitelist-security-constraint-in-web-xml) that recommends setting two separate `security-constraint`s - I could not confirm that one works in 7, so did not include it in this answer. – Bob Jan 27 '15 at 22:22
  • A note on the accepted answer by Bob showing the *security-constraint*: the *auth-constraint* tag makes it a BLACK-LIST, not a white-list. – DocOc Jun 07 '18 at 02:24
  • @DocOc The `auth-constraint` applies to everything, due to the global wildcard `url-pattern`, *except* the `http-method-omission`s. You *might* call it a "blacklist excluding some values" which is quite literally exactly the same thing as a "whitelist for some values". But "blacklist excluding" is a double-negative, which is generally confusing, so this is really just a "whitelist for". The implementation might use an inverted blacklist, but the goal, and the result, is a whitelist. Therefore, it is a whitelist, and I am calling it as such. – Bob Jun 07 '18 at 07:48
13

Well, after quick testing over some random servers holding Server: Apache-Coyotte header signature in their HTTP replies, it seems you are right as sending get / HTTP/1.1\r\nHost: <target_IP>\r\n\r\n with a simple netcat connection worked every time while a 400 HTTP code should have been received.

For instance :

$ { echo -en "get / HTTP/1.1\r\nHost: <target_IP>:8080\r\n\r\n" ; } | nc <target_IP> 8080

01:14:58.095547 IP 192.168.1.3.57245 > <target_IP>.8080: Flags [P.], seq 1:42, ack 1, win 115, options [nop,nop,TS val 4294788321 ecr 0], length 41
E..]C.@.@..Y......p.....A..v.......s.......
..D.....get / HTTP/1.1
Host: <target_IP>:8080

[...]

01:14:58.447946 IP <target_IP>.8080 > 192.168.1.3.57245: Flags [.], seq 1:1409, ack 43, win 65494, options [nop,nop,TS val 7981294 ecr 4294787971], length 1408
E...f...i.....p.............A..............
.y....C.HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Tue, 27 Jan 2015 00:15:14 GMT

I must say I'm a bit shocked here and I would not be surprised to see that behaviour extended to all HTTP/1.1 methods in such case.

You should fill a bug report on their bug tracking tool and send a mail to the appropriate mailing list because that's one ugly violation of RFC 2616 (see below) with bad consequences.

5.1.1 Method

  The Method  token indicates the method to be performed on the
  resource identified by the Request-URI. The method is case-sensitive.

      Method         = "OPTIONS"                ; Section 9.2
                     | "GET"                    ; Section 9.3
                     | "HEAD"                   ; Section 9.4
                     | "POST"                   ; Section 9.5
                     | "PUT"                    ; Section 9.6
                     | "DELETE"                 ; Section 9.7
                     | "TRACE"                  ; Section 9.8
                     | "CONNECT"                ; Section 9.9
                     | extension-method
      extension-method = token
Xavier Lucas
  • 12,815
  • 2
  • 44
  • 50
  • 3
    Note: RFC 2616 is now replaced by RFC 7230-7235. [RFC 7230 § 3.1.1](http://tools.ietf.org/html/rfc7230#section-3.1.1): "The request method is case-sensitive." [RFC 7231 § 4](http://tools.ietf.org/html/rfc7231#section-4): "By convention, standardized methods are defined in all-uppercase US-ASCII letters.", followed by the same list in your answer. – Bob Jan 27 '15 at 04:00
  • 1
    The response status code should actually be 405 Method Not Allowed. – Lie Ryan Jan 27 '15 at 11:43
  • 3
    @LieRyan No because that would mean the method token fits the RFC while the server doesn't allow it to be used on this resource. RFC 2616 § 10.4.1 : [400 Bad Request] *The request could not be understood by the server due to malformed syntax.* RFC 2616 § 10.4.6 [405 Method Not Allowed] *The **method** specified in the Request-Line is not allowed for the resource identified by the Request-URI.* The token `get` is not an HTTP **method** in any way (see the excerpt of RFC 2616 § 5.1.1 above) – Xavier Lucas Jan 27 '15 at 12:04
  • @XavierLucas: using lower case method isn't a syntax error, check the [RFC2616 Section 5](http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html). In the ABNF, `extension-method` have syntax `token` which includes all alphanumeric characters and some symbols, not just the methods specifically listed in the RFC. Almost every part of HTTP is extensible, as long as both client and server agree how they are too be extended, including defining your own lower case methods. The request line "get / HTTP/1.1" is syntactically fine, it just violates the RFC in that method name should be case sensitive. – Lie Ryan Jan 27 '15 at 12:22
  • @LieRyan The `extension-method` is here to leave the door open to next RFCs, that's not here to get your own methods added out of RFC's scope and pretend that you are running HTTP/1.1 compliant services. So a 400 should be returned because no such method have appeared yet in latest RFC thus that's, today, an invalid token. If the token was valid regarding the current method list and implemented server-side but disallowed then a 405 should be returned. A 501 should be returned in case the method is valid but not implemented server-side. – Xavier Lucas Jan 27 '15 at 12:54
  • @XavierLucas: no, it's not just for future RFC. extension-method is there to allow application authors to extend HTTP for their limited purposes in closed systems, this has always been allowed as long as both clients and servers can agree on the extensions. This is the mechanism used by WebDAV, for example, to define its own custom methods. WebDAV is RFCed but it's also an example of how to compatibly extend HTTP for closed systems. Also note how RFC 7230 only specifies that `method = token`, not listing the method names at all. Using custom methods is not syntax error. – Lie Ryan Jan 27 '15 at 12:56
  • @LieRyan No it was not specified 16 years ago while thinking at how other things like SSDP, WEBSOCKET, WEBDAV etc would work years after. These new mechanisms based themselves on HTTP/1.1 partially or fully years after. Continue in a chatroom instead of spamming this question please. – Xavier Lucas Jan 27 '15 at 13:10