security::validated_host_header (public)
security::validated_host_header
Defined in packages/acs-tcl/tcl/security-procs.tcl
- Returns:
- validated host header field or empty
- Author:
- Gustaf Neumann Protect against faked or invalid host header fields. Host header attacks can lead to web-cache poisoning and password reset attacks <for more details, see e.g. http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html>
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
Source code: # # Check, if we have a host header field # set host [ns_set iget [ns_conn headers] Host] if {$host eq ""} { return "" } # # Domain names are case insensitive. So convert it to lower to # avoid surprises. # set host [string tolower $host] # # Check, if we have validated it before, or it belongs to the # predefined accepted host header fields. # set key ::acs::validated($host) if {[info exists $key]} { return $host } if {![string match *//* $host]} { set splithost [ns_conn protocol]://$host } else { set splithost $host } if {![util::split_location $splithost .proto hostName hostPort]} { return "" } # # Remove trailing dot, as this is allowed in fully qualified DNS # names (see e.g. §3.2.2 of RFC 3976). # set hostName [string trimright $hostName .] # # Check, if the provided host is the same as the configured host # name for the current driver or one of its IP addresses. Should # be true in most cases. # set driverInfo [util_driver_info] set driverHostName [dict get $driverInfo hostname] # # The port is currently ignored for determining the validated host # header field. # # Validation is OK, when the provided host-header content is # either the same as configured hostname in the driver # configuration or one of its IP addresses. # set validationOk 0 if {$hostName eq $driverHostName} { set validationOk 1 } else { try { ns_addrbyhost -all $driverHostName } on error {errorMsg} { # # Name resolution of the hostname configured for this # driver failed, we cannot validate incoming IP addresses. # ns_log error "security::validated_host_header: configuration error:" "name resolution for configured hostname '$driverHostName'" "of driver '[ad_conn driver]' failed" } on ok {result} { set validationOk [expr {$hostName in $result}] } } # # Check, if the provided host is the same in [ns_conn location] # (will be used as default, but we do not want a warning in such # cases). # if {$validationOk == 0 && [util::split_location [ns_conn location] proto locationHost locationPort]} { set validationOk [expr {$hostName eq $locationHost}] } # # Check, if the provided host is the same as in the configured # SystemURL. # if {$validationOk == 0 && [util::split_location [ad_url] .proto systemHost systemPort]} { set validationOk [expr {$hostName eq $systemHost && ($hostPort eq $systemPort || $hostPort eq "") }] } if {$validationOk == 0 && [ns_info name] eq "NaviServer"} { # # Check against the virtual server configuration of NaviServer. # set s [ns_info server] set driverInfo [security::configured_driver_info] set drivers [lmap d $driverInfo {dict get $d driver}] foreach driver $drivers { # # Check global "servers" configuration for virtual servers for the driver # set ns [ns_configsection ns/module/$driver/servers] if {$ns ne ""} { # # We have a global "servers" configuration for the driver # set names [lmap {key value} [ns_set array $ns] { if {$key ne $s} continue set value }] if {$host in $names} { ns_log notice "security::validated_host_header: found $host" "in global virtual server configuration for $driver" set validationOk 1 break } } } } if {$validationOk == 0} { # # Check against host node map. Here we need as well protection # against invalid utf-8 characters. # if {![security::provided_host_valid $hostName]} { return "" } set validationOk [db_0or1row host_header_field_mapped {select 1 from host_node_map where host = :hostName}] } if {$validationOk == 0} { # # This is not an attempt, where someone tries to lure us to a # different host via redirect. "localhost" is always safe. # set validationOk [expr {$hostName eq "localhost"}] } # # When any of the validation attempts above were successful, we # are done. We keep the logic for successful lookups # centralized. Performance of the individual tests are not # critical, since the lookups are cache per thread. # if {$validationOk} { set $key 1 return $host } # # We could/should check as well against a white-list of additional # hostnames (maybe via ::acs::validated, or via config file, or # via additional package parameter). Probably the best way is to # get alternate (alias) names from the driver section of the # current driver [ns_conn driver] (maybe check global and local). # #ns_set array [ns_configsection ns/module/nssock/servers] # # Now we give up set info "" foreach {k v} [ns_set array [ns_conn headers]] { append info "\n $k:\t$v" } append info "\n[ns_conn method] [ns_conn url]" ns_log warning "ignore untrusted host header field: '$host'$info" #ad_log warning "ignore untrusted host header field: '$host'" return ""Generic XQL file: packages/acs-tcl/tcl/security-procs.xql
PostgreSQL XQL file: packages/acs-tcl/tcl/security-procs-postgresql.xql
Oracle XQL file: packages/acs-tcl/tcl/security-procs-oracle.xql