Skip to content

Commit f538760

Browse files
authored
Merge pull request TeslaGov#12 from TeslaGov/joefitz/validate-authorization-header
Joefitz/validate authorization header
2 parents 382edac + 4ef1890 commit f538760

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ RUN wget https://github.com/benmcollins/libjwt/archive/v$LIBJWT_VERSION.zip && \
4848
ARG TESLA_REPO_NAME=ngx-http-auth-jwt-module
4949
# ARG TESLA_REPO_URL_PREFIX=joefitz/
5050
# ARG TESLA_REPO_FILE_PREFIX=joefitz-
51-
# ARG TESLA_REPO_FILENAME=match-rh-nginx110-version
51+
# ARG TESLA_REPO_FILENAME=validate-authorization-header
5252
ARG TESLA_REPO_URL_PREFIX=
5353
ARG TESLA_REPO_FILE_PREFIX=
5454
ARG TESLA_REPO_FILENAME=master

build.sh

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ RED='\033[01;31m'
1111
GREEN='\033[01;32m'
1212
NONE='\033[00m'
1313

14+
VALIDJWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwgImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwgInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwgImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwgImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.TvDD63ZOqFKgE-uxPDdP5aGIsbl5xPKz4fMul3Zlti4
15+
1416
TEST_INSECURE_EXPECT_200=`curl -X GET -o /dev/null --silent --head --write-out '%{http_code}\n' http://${MACHINE_IP}:8000`
1517
if [ "$TEST_INSECURE_EXPECT_200" -eq "200" ];then
1618
echo -e "${GREEN}Insecure test pass ${TEST_INSECURE_EXPECT_200}${NONE}";
@@ -25,9 +27,16 @@ else
2527
echo -e "${RED}Secure test without jwt fail ${TEST_SECURE_EXPECT_302}${NONE}";
2628
fi
2729

28-
TEST_SECURE_EXPECT_200=`curl -X GET -o /dev/null --silent --head --write-out '%{http_code}\n' http://${MACHINE_IP}:8000/secure/index.html -H 'cache-control: no-cache' --cookie "rampartjwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwgImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwgInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwgImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwgImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.TvDD63ZOqFKgE-uxPDdP5aGIsbl5xPKz4fMul3Zlti4;PassportKey=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwgImxhc3ROYW1lIjoid29ybGQiLCJlbWFpbEFkZHJlc3MiOiJoZWxsb3dvcmxkQGV4YW1wbGUuY29tIiwgInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3RoZXIiXSwgImlzcyI6Imlzc3VlciIsInBlcnNvbklkIjoiNzViYjNjYzctYjkzMy00NGYwLTkzYzYtMTQ3YjA4MmZhZGI1IiwgImV4cCI6MTkwODgzNTIwMCwiaWF0IjoxNDg4ODE5NjAwLCJ1c2VybmFtZSI6ImhlbGxvLndvcmxkIn0.TvDD63ZOqFKgE-uxPDdP5aGIsbl5xPKz4fMul3Zlti4"`
30+
TEST_SECURE_EXPECT_200=`curl -X GET -o /dev/null --silent --head --write-out '%{http_code}\n' http://${MACHINE_IP}:8000/secure/index.html -H 'cache-control: no-cache' --cookie "rampartjwt=${VALIDJWT}"`
2931
if [ "$TEST_SECURE_EXPECT_200" -eq "200" ];then
3032
echo -e "${GREEN}Secure test with jwt pass ${TEST_SECURE_EXPECT_200}${NONE}";
3133
else
3234
echo -e "${RED}Secure test with jwt fail ${TEST_SECURE_EXPECT_200}${NONE}";
3335
fi
36+
37+
TEST_SECURE_EXPECT_200=`curl -X GET -o /dev/null --silent --head --write-out '%{http_code}\n' http://${MACHINE_IP}:8000/secure/index.html -H 'cache-control: no-cache' --header "Authorization: Bearer ${VALIDJWT}" --cookie "rampartjwt=${VALIDJWT}"`
38+
if [ "$TEST_SECURE_EXPECT_200" -eq "200" ];then
39+
echo -e "${GREEN}Secure test with jwt and auth header pass ${TEST_SECURE_EXPECT_200}${NONE}";
40+
else
41+
echo -e "${RED}Secure test with jwt and auth header fail ${TEST_SECURE_EXPECT_200}${NONE}";
42+
fi

src/ngx_http_auth_jwt_module.c

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ static char * ngx_http_auth_jwt_merge_loc_conf(ngx_conf_t *cf, void *parent, voi
2323
static int hex_char_to_binary( char ch, char* ret );
2424
static int hex_to_binary( const char* str, u_char* buf, int len );
2525
static char * ngx_str_t_to_char_ptr(ngx_pool_t *pool, ngx_str_t str);
26+
static ngx_table_elt_t* search_headers_in(ngx_http_request_t *r, u_char *name, size_t len);
2627

2728
static ngx_command_t ngx_http_auth_jwt_commands[] = {
2829

@@ -84,9 +85,12 @@ ngx_module_t ngx_http_auth_jwt_module = {
8485

8586
static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
8687
{
87-
ngx_int_t n;
88+
static const int BEARER_LEN = 7; // strlen("Bearer ");
89+
8890
ngx_str_t jwtCookieName = ngx_string("rampartjwt");
8991
ngx_str_t passportKeyCookieName = ngx_string("PassportKey");
92+
ngx_str_t authorizationHeaderName = ngx_string("Authorization");
93+
ngx_int_t n;
9094
ngx_str_t jwtCookieVal;
9195
char* jwtCookieValChrPtr;
9296
char* return_url;
@@ -97,6 +101,7 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
97101
jwt_alg_t alg;
98102
time_t exp;
99103
time_t now;
104+
ngx_table_elt_t *authorizationHeader;
100105

101106
jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module);
102107

@@ -109,6 +114,7 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
109114
// jwtcf->auth_jwt_key.data,
110115
// jwtcf->auth_jwt_enabled);
111116

117+
112118
// get the cookie
113119
// TODO: the cookie name could be passed in dynamicallly
114120
n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &jwtCookieName, &jwtCookieVal);
@@ -163,6 +169,25 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
163169
goto redirect;
164170
}
165171

172+
// if an Authorization header exists, it must match the cookie
173+
authorizationHeader = search_headers_in(r, authorizationHeaderName.data, authorizationHeaderName.len);
174+
if (authorizationHeader != NULL)
175+
{
176+
// compare lengths first
177+
if (authorizationHeader->value.len != jwtCookieVal.len + BEARER_LEN)
178+
{
179+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Authorization and Cookie do not match lengths");
180+
goto redirect;
181+
}
182+
183+
// compare content
184+
if (0 != strncmp((const char *)(authorizationHeader->value.data + BEARER_LEN), (const char *)jwtCookieVal.data, jwtCookieVal.len))
185+
{
186+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Authorization and Cookie do not match content");
187+
goto redirect;
188+
}
189+
}
190+
166191
return NGX_OK;
167192

168193
redirect:
@@ -361,4 +386,48 @@ static char* ngx_str_t_to_char_ptr(ngx_pool_t *pool, ngx_str_t str)
361386
return char_ptr;
362387
}
363388

389+
static ngx_table_elt_t* search_headers_in(ngx_http_request_t *r, u_char *name, size_t len)
390+
{
391+
ngx_list_part_t *part;
392+
ngx_table_elt_t *h;
393+
ngx_uint_t i;
394+
395+
// Get the first part of the list. There is usual only one part.
396+
part = &r->headers_in.headers.part;
397+
h = part->elts;
398+
399+
// Headers list array may consist of more than one part, so loop through all of it
400+
for (i = 0; /* void */ ; i++)
401+
{
402+
if (i >= part->nelts)
403+
{
404+
if (part->next == NULL)
405+
{
406+
/* The last part, search is done. */
407+
break;
408+
}
409+
410+
part = part->next;
411+
h = part->elts;
412+
i = 0;
413+
}
414+
415+
//Just compare the lengths and then the names case insensitively.
416+
if (len != h[i].key.len || ngx_strcasecmp(name, h[i].key.data) != 0)
417+
{
418+
/* This header doesn't match. */
419+
continue;
420+
}
421+
422+
/*
423+
* Ta-da, we got one!
424+
* Note, we've stopped the search at the first matched header
425+
* while more then one header may match.
426+
*/
427+
return &h[i];
428+
}
429+
430+
/* No headers was found */
431+
return NULL;
432+
}
364433

0 commit comments

Comments
 (0)