Bug 65504

Summary: Defaulting RequireAny globally violates best security practices
Product: Apache httpd-2 Reporter: Philip Prindeville <philipp>
Component: mod_authz_coreAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: NEW ---    
Severity: critical CC: philipp
Priority: P2    
Version: 2.4.48   
Target Milestone: ---   
Hardware: All   
OS: All   

Description Philip Prindeville 2021-08-17 21:38:34 UTC
Recently I was configuring a proxy URL for EJBCA on AWS, and has configured:

<Location /ejbca/adminweb>
    Require ssl
    Require expr "\
          %{SSL_CLIENT_I_DN} == 'O=ec2-n.n.n.n.us-east-1.compute.amazonaws.com,OU=ami-aaaaaaaaa,CN=ManagementCA,UID=r-uuuuuuuu' \
            && %{SSL_CLIENT_V_REMAIN} -gt 0 \
        "
    SSLVerifyClient require
</Location>

thinking that both Require statements had to be true.  Nope, turns out that's not the case, even though (1) it's completely counter-intuitive, and (2) it violates a couple of security Best Practices that "everything not explicitly permitted is implicitly defined" and "defense-in-depth".

So I thought I was battening things down by requiring that SSL be in use, and that any certs be issued from the server's own self-signed-cert, and that the cert not be expired.  In fact, only one of those two needs to be true (SSL is in use, OR the client's TLS certificate issuer DN match the server's subject DN and not be expired).

Completely counter-intuitive, and somewhat nonsensical.  If SSL *isn't* in use, then the "Require expr ..." will *never* be true.

Looking at https://httpd.apache.org/docs/2.4/howto/auth.html#beyond we have:

> By default all Require directives are handled as though contained within a <RequireAny> container directive. In other words, if any of the specified authorization methods succeed, then authorization is granted.
and in https://httpd.apache.org/docs/2.4/mod/mod_authz_core.html#requiredirectives we have:

> When multiple Require directives are used in a single configuration section and are not contained in another authorization directive like <RequireAll>, they are implicitly contained within a <RequireAny> directive. Thus the first one to authorize a user authorizes the entire request, and subsequent Require directives are ignored.
Again, completely counterintuitive and not following security Best Practices.

I recommend that the default for global scoping should be implicitly having the entire configuration being treated as if encompassed in <RequireAll>...</RequireAll>, or else there be a global directive one could apply server-config wide.