Escape Username before using it in any search filter
This commit is contained in:
		
							parent
							
								
									ef8d313042
								
							
						
					
					
						commit
						c0a43f4800
					
				|  | @ -2,9 +2,16 @@ | ||||||
| ''''[ -z $LOG ] && export LOG=/dev/stdout # ''' | ''''[ -z $LOG ] && export LOG=/dev/stdout # ''' | ||||||
| ''''which python  >/dev/null && exec python  -u "$0" "$@" >> $LOG 2>&1 # ''' | ''''which python  >/dev/null && exec python  -u "$0" "$@" >> $LOG 2>&1 # ''' | ||||||
| 
 | 
 | ||||||
| # Copyright (C) 2014-2015 Nginx, Inc. | # Copyright (C) 2014-2022 Nginx, Inc. | ||||||
|  | 
 | ||||||
|  | import sys | ||||||
|  | import os | ||||||
|  | import signal | ||||||
|  | import base64 | ||||||
|  | import ldap | ||||||
|  | from ldap.filter import escape_filter_chars | ||||||
|  | import argparse | ||||||
| 
 | 
 | ||||||
| import sys, os, signal, base64, ldap, argparse |  | ||||||
| if sys.version_info.major == 2: | if sys.version_info.major == 2: | ||||||
|     from Cookie import BaseCookie |     from Cookie import BaseCookie | ||||||
|     from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler |     from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler | ||||||
|  | @ -23,6 +30,7 @@ if not hasattr(__builtins__, "basestring"): basestring = (str, bytes) | ||||||
| # ----------------------------------------------------------------------------- | # ----------------------------------------------------------------------------- | ||||||
| # Requests are processed in separate thread | # Requests are processed in separate thread | ||||||
| import threading | import threading | ||||||
|  | 
 | ||||||
| if sys.version_info.major == 2: | if sys.version_info.major == 2: | ||||||
|     from SocketServer import ThreadingMixIn |     from SocketServer import ThreadingMixIn | ||||||
| elif sys.version_info.major == 3: | elif sys.version_info.major == 3: | ||||||
|  | @ -89,8 +97,8 @@ class AuthHandler(BaseHTTPRequestHandler): | ||||||
|             self.auth_failed(ctx) |             self.auth_failed(ctx) | ||||||
|             return True |             return True | ||||||
|         |         | ||||||
|         ctx['user'] = user |  | ||||||
|         ctx['pass'] = passwd |         ctx['pass'] = passwd | ||||||
|  |         ctx['user'] = ldap.filter.escape_filter_chars(user) | ||||||
| 
 | 
 | ||||||
|         # Continue request processing |         # Continue request processing | ||||||
|         return False |         return False | ||||||
|  | @ -228,7 +236,7 @@ class LDAPAuthHandler(AuthHandler): | ||||||
|             ldap_obj.bind_s(ctx['binddn'], ctx['bindpasswd'], ldap.AUTH_SIMPLE) |             ldap_obj.bind_s(ctx['binddn'], ctx['bindpasswd'], ldap.AUTH_SIMPLE) | ||||||
| 
 | 
 | ||||||
|             ctx['action'] = 'preparing search filter' |             ctx['action'] = 'preparing search filter' | ||||||
|             searchfilter = ctx['template'] % { 'username': ctx['user'] } |             searchfilter = ctx['template'] % {'username': ctx['user']} | ||||||
| 
 | 
 | ||||||
|             self.log_message(('searching on server "%s" with base dn ' + \ |             self.log_message(('searching on server "%s" with base dn ' + \ | ||||||
|                               '"%s" with filter "%s"') % |                               '"%s" with filter "%s"') % | ||||||
|  |  | ||||||
|  | @ -105,6 +105,15 @@ http { | ||||||
|             proxy_pass http://backend/; |             proxy_pass http://backend/; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         location /query-injection { | ||||||
|  |             auth_request /auth-query-injection; | ||||||
|  | 	     | ||||||
|  | 	    error_page 401 =200 /login; | ||||||
|  | 	     | ||||||
|  | 	    proxy_pass http://backend/; | ||||||
|  |         | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         location /login { |         location /login { | ||||||
|             proxy_pass http://backend/login; |             proxy_pass http://backend/login; | ||||||
| 
 | 
 | ||||||
|  | @ -221,6 +230,24 @@ http { | ||||||
|             proxy_set_header Cookie nginxauth=$cookie_nginxauth; |             proxy_set_header Cookie nginxauth=$cookie_nginxauth; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         location = /auth-query-injection { | ||||||
|  |             internal; | ||||||
|  | 
 | ||||||
|  |             proxy_pass http://127.0.0.1:8888; | ||||||
|  | 
 | ||||||
|  |             proxy_pass_request_body off; | ||||||
|  |             proxy_set_header Content-Length ""; | ||||||
|  | 
 | ||||||
|  |             proxy_set_header X-Ldap-URL      "ldap://127.0.0.1:8083"; | ||||||
|  |             proxy_set_header X-Ldap-BaseDN   "ou=Users,dc=test,dc=local"; | ||||||
|  |             proxy_set_header X-Ldap-BindDN   "cn=root,dc=test,dc=local"; | ||||||
|  |             proxy_set_header X-Ldap-BindPass "secret"; | ||||||
|  |             | ||||||
|  |             proxy_set_header X-CookieName "nginxauth"; | ||||||
|  |             proxy_set_header Cookie nginxauth=$cookie_nginxauth; | ||||||
|  | 	     | ||||||
|  |             proxy_set_header X-Ldap-Template '(|(&(memberOf=superadmin)(cn=%(username)s))(&(memberOf=admin)(cn=%(username)s)))'; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -321,7 +348,7 @@ EOF | ||||||
| $t->write_file_expand("initial.ldif", <<'EOF'); | $t->write_file_expand("initial.ldif", <<'EOF'); | ||||||
| dn: dc=test,dc=local | dn: dc=test,dc=local | ||||||
| dc: test | dc: test | ||||||
| description: BlaBlaBla | description: Test-OU | ||||||
| objectClass: dcObject | objectClass: dcObject | ||||||
| objectClass: organization | objectClass: organization | ||||||
| o: Example, Inc. | o: Example, Inc. | ||||||
|  | @ -333,7 +360,7 @@ objectclass: organizationalunit | ||||||
| 
 | 
 | ||||||
| dn: cn=user1,ou=Users,dc=test,dc=local | dn: cn=user1,ou=Users,dc=test,dc=local | ||||||
| objectclass: inetOrgPerson | objectclass: inetOrgPerson | ||||||
| cn: User number one | cn: User1 | ||||||
| sn: u1 | sn: u1 | ||||||
| uid: user1 | uid: user1 | ||||||
| userpassword: user1secret | userpassword: user1secret | ||||||
|  | @ -343,7 +370,7 @@ ou: Users | ||||||
| 
 | 
 | ||||||
| dn: cn=user2,ou=Users,dc=test,dc=local | dn: cn=user2,ou=Users,dc=test,dc=local | ||||||
| objectclass: inetOrgPerson | objectclass: inetOrgPerson | ||||||
| cn: User number one | cn: User2 | ||||||
| sn: u2 | sn: u2 | ||||||
| uid: user2 | uid: user2 | ||||||
| userpassword: user2secret | userpassword: user2secret | ||||||
|  | @ -353,7 +380,7 @@ ou: Users | ||||||
| 
 | 
 | ||||||
| dn: cn=user3,ou=Users,dc=test,dc=local | dn: cn=user3,ou=Users,dc=test,dc=local | ||||||
| objectclass: inetOrgPerson | objectclass: inetOrgPerson | ||||||
| cn: User number one | cn: User3 | ||||||
| sn: u3 | sn: u3 | ||||||
| uid: user3 | uid: user3 | ||||||
| userpassword: user3secret | userpassword: user3secret | ||||||
|  | @ -378,13 +405,13 @@ objectclass: organizationalunit | ||||||
| 
 | 
 | ||||||
| dn: ou=more,ou=Users,dc=test,dc=local | dn: ou=more,ou=Users,dc=test,dc=local | ||||||
| dc: test | dc: test | ||||||
| description: BlaBlaBla | description: Test-OU | ||||||
| objectClass: dcObject | objectClass: dcObject | ||||||
| objectClass: organizationalUnit | objectClass: organizationalUnit | ||||||
| 
 | 
 | ||||||
| dn: cn=user4, ou=more, ou=Users,dc=test,dc=local | dn: cn=user4, ou=more, ou=Users,dc=test,dc=local | ||||||
| objectclass: inetOrgPerson | objectclass: inetOrgPerson | ||||||
| cn: User number one | cn: User4 | ||||||
| sn: u4 | sn: u4 | ||||||
| uid: user4 | uid: user4 | ||||||
| userpassword: user4secret | userpassword: user4secret | ||||||
|  | @ -441,7 +468,7 @@ $t->run_daemon('/bin/sh', "$d/auth_daemon.sh"); | ||||||
| $t->waitforsocket('127.0.0.1:' . port(8888)) | $t->waitforsocket('127.0.0.1:' . port(8888)) | ||||||
| 	or die "Can't start auth daemon"; | 	or die "Can't start auth daemon"; | ||||||
| 
 | 
 | ||||||
| $t->plan(21); | $t->plan(22); | ||||||
| 
 | 
 | ||||||
| $t->run(); | $t->run(); | ||||||
| 
 | 
 | ||||||
|  | @ -500,10 +527,17 @@ like(http_get_auth('/ref1', 'user4', 'user4secret'), qr!LOGIN PAGE!, | ||||||
| 	'server2 user via referral on server1'); | 	'server2 user via referral on server1'); | ||||||
| 
 | 
 | ||||||
| # unknown user on referred server, result is empty dn | # unknown user on referred server, result is empty dn | ||||||
| like(http_get_auth('/ref1', 'userx', 'blah'), qr!LOGIN PAGE!, | like(http_get_auth('/ref1', 'unknow_user', 'unknowpassword'), qr!LOGIN PAGE!, | ||||||
| 	'unknown user with referral on server1'); | 	'unknown user with referral on server1'); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # LDAP Query Injection result in 401 | ||||||
|  | like(http_get_auth('/query-injection', 'user1))(|(cn=user1', 'user1secret'), qr!LOGIN PAGE!, | ||||||
|  | 	'Injection Attempt in Username will be escaped and blocked.'); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ############################################################################### | ############################################################################### | ||||||
| 
 | 
 | ||||||
| sub http_get_auth { | sub http_get_auth { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue