Code Monkey home page Code Monkey logo

lua-resty-jwt's Introduction

Name

lua-resty-jwt - JWT for ngx_lua and LuaJIT

Build Status

Attention โ— the hmac lib used here is lua-resty-hmac, not the one in luarocks.

Installation

  • opm: opm get SkyLothar/lua-resty-jwt
  • luarocks: luarocks install lua-resty-jwt
  • Head to release page and download tar.gz

version

0.1.10

Table of Contents

Status

This library is under active development but is considered production ready.

Description

This library requires an nginx build with OpenSSL, the ngx_lua module, the LuaJIT 2.0, the lua-resty-hmac, and the lua-resty-string,

Synopsis

    # nginx.conf:

    lua_package_path "/path/to/lua-resty-jwt/lib/?.lua;;";

    server {
        default_type text/plain;
        location = /verify {
            content_by_lua '
                local cjson = require "cjson"
                local jwt = require "resty.jwt"

                local jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" ..
                    ".eyJmb28iOiJiYXIifQ" ..
                    ".VAoRL1IU0nOguxURF2ZcKR0SGKE1gCbqwyh8u2MLAyY"
                local jwt_obj = jwt:verify("lua-resty-jwt", jwt_token)
                ngx.say(cjson.encode(jwt_obj))
            ';
        }
        location = /sign {
            content_by_lua '
                local cjson = require "cjson"
                local jwt = require "resty.jwt"

                local jwt_token = jwt:sign(
                    "lua-resty-jwt",
                    {
                        header={typ="JWT", alg="HS256"},
                        payload={foo="bar"}
                    }
                )
                ngx.say(jwt_token)
            ';
        }
    }

Back to TOC

Methods

To load this library,

  1. you need to specify this library's path in ngx_lua's lua_package_path directive. For example, lua_package_path "/path/to/lua-resty-jwt/lib/?.lua;;";.
  2. you use require to load the library into a local Lua variable:
    local jwt = require "resty.jwt"

Back to TOC

sign

syntax: local jwt_token = jwt:sign(key, table_of_jwt)

sign a table_of_jwt to a jwt_token.

The alg argument specifies which hashing algorithm to use (HS256, HS512, RS256).

sample of table_of_jwt

{
    "header": {"typ": "JWT", "alg": "HS512"},
    "payload": {"foo": "bar"}
}

verify

syntax: local jwt_obj = jwt:verify(key, jwt_token [, claim_spec [, ...]])

verify a jwt_token and returns a jwt_obj table. key can be a pre-shared key (as a string), or a function which takes a single parameter (the value of kid from the header) and returns either the pre-shared key (as a string) for the kid or nil if the kid lookup failed. This call will fail if you try to specify a function for key and there is no kid existing in the header.

See Verification for details on the format of claim_spec parameters.

load & verify

syntax: local jwt_obj = jwt:load_jwt(jwt_token)
syntax: local verified = jwt:verify_jwt_obj(key, jwt_obj [, claim_spec [, ...]])

verify = load_jwt + verify_jwt_obj

load jwt, check for kid, then verify it with the correct key

sample of jwt_obj

{
    "raw_header": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9",
    "raw_payload: "eyJmb28iOiJiYXIifQ",
    "signature": "wrong-signature",
    "header": {"typ": "JWT", "alg": "HS256"},
    "payload": {"foo": "bar"},
    "verified": false,
    "valid": true,
    "reason": "signature mismatched: wrong-signature"
}

sign-jwe

syntax: local jwt_token = jwt:sign(key, table_of_jwt)

sign a table_of_jwt to a jwt_token.

The alg argument specifies which hashing algorithm to use for encrypting key (dir). The enc argument specifies which hashing algorithm to use for encrypting payload (A128CBC-HS256, A256CBC-HS512)

sample of table_of_jwt

{
    "header": {"typ": "JWE", "alg": "dir", "enc":"A128CBC-HS256"},
    "payload": {"foo": "bar"}
}

Back to TOC

Verification

Both the jwt:load and jwt:verify_jwt_obj functions take, as additional parameters, any number of optional claim_spec parameters. A claim_spec is simply a lua table of claims and validators. Each key in the claim_spec table corresponds to a matching key in the payload, and the validator is a function that will be called to determine if the claims are met.

The signature of a validator function is:

function(val, claim, jwt_json)

Where val is the value of the claim from the jwt_obj being tested (or nil if it doesn't exist in the object's payload), claim is the name of the claim that is being verified, and jwt_json is a json-serialized representation of the object that is being verified. If the function has no need of the claim or jwt_json, parameters, they may be left off.

A validator function returns either true or false. Any validator MAY raise an error, and the validation will be treated as a failure, and the error that was raised will be put into the reason field of the resulting object. If a validator returns nothing (i.e. nil), then the function is treated to have succeeded - under the assumption that it would have raised an error if it would have failed.

A special claim named __jwt can be used such that if a validator function exists for it, then the validator will be called with a deep clone of the entire parsed jwt object as the value of val. This is so that you can write verifications for an entire object that may depend on one or more claims.

Multiple claim_spec tables can be specified to the jwt:load and jwt:verify_jwt_obj - and they will be executed in order. There is no guarantee of the execution order of individual validators within a single claim_spec. If a claim_spec fails, then any following claim_specs will NOT be executed.

sample claim_spec

{
    sub = function(val) return string.match("^[a-z]+$", val) end,
    iss = function(val)
        for _, value in pairs({ "first", "second" }) do
            if value == val then return true end
        end
        return false
    end,
    __jwt = function(val, claim, jwt_json)
        if val.payload.foo == nil or val.payload.bar == nil then
            error("Need to specify either 'foo' or 'bar'")
        end
    end
}

JWT Validators

A library of helpful validator functions exists at resty.jwt-validators. You can use this library by including:

local validators = require "resty.jwt-validators"

The following functions are currently defined in the validator library. Those marked with "(opt)" means that the same function exists named opt_<name> which takes the same parameters. The "opt" version of the function will return true if the key does not exist in the payload of the jwt_object being verified, while the "non-opt" version of the function will return false if the key does not exist in the payload of the jwt_object being verified.

validators.chain(...)

Returns a validator that chains the given functions together, one after another - as long as they keep passing their checks.

validators.required(chain_function)

Returns a validator that returns false if a value doesn't exist. If the value exists and a chain_function is specified, then the value of chain_function(val, claim, jwt_json) will be returned, otherwise, true will be returned. This allows for specifying that a value is both required and it must match some additional check.

validators.require_one_of(claim_keys)

Returns a validator which errors with a message if NONE of the given claim keys exist. It is expected that this function is used against a full jwt object. The claim_keys must be a non-empty table of strings.

validators.check(check_val, check_function, name, check_type) (opt)

Returns a validator that checks if the result of calling the given check_function for the tested value and check_val returns true. The value of check_val and check_function cannot be nil. The optional name is used for error messages and defaults to "check_value". The optional check_type is used to make sure that the check type matches and defaults to type(check_val). The first parameter passed to check_function will never be nil. If the check_function raises an error, that will be appended to the error message.

validators.equals(check_val) (opt)

Returns a validator that checks if a value exactly equals (using ==) the given check_value. The value of check_val cannot be nil.

validators.matches(pattern) (opt)

Returns a validator that checks if a value matches the given pattern (using string.match). The value of pattern must be a string.

validators.any_of(check_values, check_function, name, check_type, table_type) (opt)

Returns a validator which calls the given check_function for each of the given check_values and the tested value. If any of these calls return true, then this function returns true. The value of check_values must be a non-empty table with all the same types, and the value of check_function must not be nil. The optional name is used for error messages and defaults to "check_values". The optional check_type is used to make sure that the check type matches and defaults to type(check_values[1]) - the table type.

validators.equals_any_of(check_values) (opt)

Returns a validator that checks if a value exactly equals any of the given check_values.

validators.matches_any_of(patterns) (opt)

Returns a validator that checks if a value matches any of the given patterns.

validators.contains_any_of(check_values,name) (opt)

Returns a validator that checks if a value of expected type string exists in any of the given check_values. The value of check_valuesmust be a non-empty table with all the same types. The optional name is used for error messages and defaults to check_values.

validators.greater_than(check_val) (opt)

Returns a validator that checks how a value compares (numerically, using >) to a given check_value. The value of check_val cannot be nil and must be a number.

validators.greater_than_or_equal(check_val) (opt)

Returns a validator that checks how a value compares (numerically, using >=) to a given check_value. The value of check_val cannot be nil and must be a number.

validators.less_than(check_val) (opt)

Returns a validator that checks how a value compares (numerically, using <) to a given check_value. The value of check_val cannot be nil and must be a number.

validators.less_than_or_equal(check_val) (opt)

Returns a validator that checks how a value compares (numerically, using <=) to a given check_value. The value of check_val cannot be nil and must be a number.

validators.is_not_before() (opt)

Returns a validator that checks if the current time is not before the tested value within the system's leeway. This means that:

val <= (system_clock() + system_leeway).

validators.is_not_expired() (opt)

Returns a validator that checks if the current time is not equal to or after the tested value within the system's leeway. This means that:

val > (system_clock() - system_leeway).

validators.is_at() (opt)

Returns a validator that checks if the current time is the same as the tested value within the system's leeway. This means that:

val >= (system_clock() - system_leeway) and val <= (system_clock() + system_leeway).

validators.set_system_leeway(leeway)

A function to set the leeway (in seconds) used for is_not_before and is_not_expired. The default is to use 0 seconds

validators.set_system_clock(clock)

A function to set the system clock used for is_not_before and is_not_expired. The default is to use ngx.now

sample claim_spec using validators

local validators = require "resty.jwt-validators"
local claim_spec = {
    sub = validators.opt_matches("^[a-z]+$),
    iss = validators.equals_any_of({ "first", "second" }),
    __jwt = validators.require_one_of({ "foo", "bar" })
}

Legacy/Timeframe options

In order to support code which used previous versions of this library, as well as to simplify specifying timeframe-based claim_specs, you may use in place of any single claim_spec parameter a table of validation_options. The parameter should be expressed as a key/value table. Each key of the table should be picked from the following list.

When using legacy validation_options, you MUST ONLY specify these options. That is, you cannot mix legacy validation_options with other claim_spec validators. In order to achieve that, you must specify multiple options to the jwt:load/jwt:verify_jwt_obj functions.

  • lifetime_grace_period: Define the leeway in seconds to account for clock skew between the server that generated the jwt and the server validating it. Value should be zero (0) or a positive integer.

    • When this validation option is specified, the process will ensure that the jwt contains at least one of the two nbf or exp claim and compare the current clock time against those boundaries. Would the jwt be deemed as expired or not valid yet, verification will fail.

    • When none of the nbf and exp claims can be found, verification will fail.

    • nbf and exp claims are expected to be expressed in the jwt as numerical values. Wouldn't that be the case, verification will fail.

    • Specifying this option is equivalent to calling:

      validators.set_system_leeway(leeway)
      

      and specifying as a claim_spec:

      {
        __jwt = validators.require_one_of({ "nbf", "exp" }),
        nbf = validators.opt_is_not_before(),
        exp = validators.opt_is_not_expired()
      }
      
  • require_nbf_claim: Express if the nbf claim is optional or not. Value should be a boolean.

    • When this validation option is set to true and no lifetime_grace_period has been specified, a zero (0) leeway is implied.

    • Specifying this option is equivalent to specifying as a claim_spec:

      {
        nbf = validators.is_not_before(),
      }
      
  • require_exp_claim: Express if the exp claim is optional or not. Value should be a boolean.

    • When this validation option is set to true and no lifetime_grace_period has been specified, a zero (0) leeway is implied.

    • Specifying this option is equivalent to specifying as a claim_spec:

      {
        exp = validators.is_not_expired(),
      }
      
  • valid_issuers: Whitelist the vetted issuers of the jwt. Value should be a array of strings.

    • When this validation option is specified, the process will compare the jwt iss claim against the list of valid issuers. Comparison is done in a case sensitive manner. Would the jwt issuer not be found in the whitelist, verification will fail.

    • iss claim is expected to be expressed in the jwt as a string. Wouldn't that be the case, verification will fail.

    • Specifying this option is equivalent to specifying as a claim_spec:

      {
        iss = validators.equals_any_of(valid_issuers),
      }
      

sample of validation_options usage

local jwt_obj = jwt:verify(key, jwt_token,
    {
        lifetime_grace_period = 120,
        require_exp_claim = true,
        valid_issuers = { "my-trusted-issuer", "my-other-trusteed-issuer" }
    }
)

Examples

Back to TOC

Installation

Using Luarocks

luarocks install lua-resty-jwt

It is recommended to use the latest ngx_openresty bundle directly.

Also, You need to configure the lua_package_path directive to add the path of your lua-resty-jwt source tree to ngx_lua's Lua module search path, as in

    # nginx.conf
    http {
        lua_package_path "/path/to/lua-resty-jwt/lib/?.lua;;";
        ...
    }

and then load the library in Lua:

    local jwt = require "resty.jwt"

Back to TOC

Testing With Docker

./ci script

Back to TOC

See Also

Back to TOC

lua-resty-jwt's People

Contributors

deadleg avatar extremesurfer avatar fermaem avatar fraburnham avatar kleht8 avatar leyan-pang avatar lucafavatella avatar mrrobby avatar ravenscar avatar rohitjoshi avatar saks avatar skylothar avatar toonetown avatar wuthefwasthat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lua-resty-jwt's Issues

Validation example throws exception due to validate function 3rd argument being a number and not object/table

Been playing around with guard examples and noticed the following:

Getting this exception:

2017/09/12 13:26:07 [error] 7#7: *5 lua entry thread aborted: runtime error: /usr/local/openresty/site/lualib/resty/jwt.lua:696: bad argument #1 to 'pairs' (table expected, got number)
stack traceback:
coroutine 0:
	[C]: in function 'pairs'
	/usr/local/openresty/site/lualib/resty/jwt.lua:696: in function 'validate_claims'
	/usr/local/openresty/site/lualib/resty/jwt.lua:728: in function 'verify'
	/usr/local/openresty/nginx/conf/lua/jwtguard.lua:13: in function </usr/local/openresty/nginx/conf/lua/jwtguard.lua:1>, client: ..., server: localhost, request: "GET /test/ HTTP/1.1", host: "..."

My token does not have any claim and seems like 3rd argument on below line causing this issue:
here

If 3rd argument removed, everything seems to work as expected.

Thanks for a great library!

required key length is wrong

from rfc

5.2.2.1. AES_CBC_HMAC_SHA2 Encryption
The number of octets in the input key K MUST be the sum of
MAC_KEY_LEN and ENC_KEY_LEN. The values of these parameters are
specified by the Authenticated Encryption algorithms in Sections
5.2.3 through 5.2.5.
5.2.3. AES_128_CBC_HMAC_SHA_256
The input key K is 32 octets long.
ENC_KEY_LEN is 16 octets.
MAC_KEY_LEN is 16 octets.
5.2.5. AES_256_CBC_HMAC_SHA_512 AES_256_CBC_HMAC_SHA_512 is based on AES_128_CBC_HMAC_SHA_256, but
with the following differences: The input key K is 64 octets long instead of 32.
ENC_KEY_LEN is 32 octets instead of 16.
MAC_KEY_LEN is 32 octets instead of 16.

but in code:

local function derive_keys(enc, secret_key)
  local key_size_bytes = 16
  if enc == str_const.A128CBC_HS256 then
    key_size_bytes = 16
  elseif enc == str_const.A256CBC_HS512 then
    key_size_bytes = 32
  end
  if not secret_key then
    secret_key =  resty_random.bytes(key_size_bytes,true)
  end
  if #secret_key ~= key_size_bytes then
    error({reason="The pre-shared content key must be ".. key_size_bytes})
  end
  local derived_key_size = key_size_bytes / 2
  mac_key = string_sub(secret_key, 1, derived_key_size)
  enc_key =string_sub(secret_key, derived_key_size)
  return secret_key, mac_key, enc_key
end

"jwt.lua": HS256 signature validation fails which reason as "internal error"

My env:

After debug i stuck up at hmac.lua at function _M.new(self, key, hash_algo). It seems it returns "nil' to the caller in JWT.lua.
I doubt something going worng with 'require "ffi"' in hmac.lua, though not sure.

Log snippet from nginx error.log:
2018/05/30 13:42:41 [warn] 11009#11009: *1 [lua] jwt.lua:109: my signsecret3, client: 127.0.0.1, server: , request: "POST /pps/v3/core/bookings/createEventBooking HTTP/1.1", host: "localhost:8080"
2018/05/30 13:42:41 [warn] 11009#11009: *1 [lua] hmac.lua:92: new(): inside hmac(in new)secret3, client: 127.0.0.1, server: , request: "POST /pps/v3/core/bookings/createEventBooking HTTP/1.1", host: "localhost:8080"
2018/05/30 13:42:41 [warn] 11009#11009: *1 [lua] nginx-jwt.lua:51: auth(): Invalid token: internal error, client: 127.0.0.1, server: , request: "POST /pps/v3/core/bookings/createEventBooking HTTP/1.1", host: "localhost:8080"

Any help appreciated?

Thanks,
Sanjay

Always get "internal error" for reason

What does it mean when the library returns "internal error" for the reason?

I'm testing with a secret of tacos and following JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjJjOGIzNThhLWM3MTctNGQyYi1hMWZmLWY2YzFmMjQxNWFjNCIsImlhdCI6MTQ0MTUxMzMyNCwiZXhwIjoxNDQxNTMxMzI0fQ.vs-xGa4SC6CuzHyb1bWywTyMEffi5MVILnVC3E-zz-g

Testing on jwt.io shows that the token is fine and the secret works. Here's my lua code (running in NGINX)

cjson = require "cjson"
jwt   = require "resty.jwt"

local parsed_jwt = jwt:verify("tacos", *insert JWT from above*)
ngx.say(cjson.encode(parsed_jwt))

And the output is

{
    "payload":{
        "exp":1441531324,
        "id":"2c8b358a-c717-4d2b-a1ff-f6c1f2415ac4",
        "iat":1441513324
    },
    "reason":"internal error",
    "raw_header":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9",
    "valid":true,
    "header":{"alg":"HS256","typ":"JWT"},
    "signature":"vs-xGa4SC6CuzHyb1bWywTyMEffi5MVILnVC3E-zz-g",
    "verified":false,
    "raw_payload":"eyJpZCI6IjJjOGIzNThhLWM3MTctNGQyYi1hMWZmLWY2YzFmMjQxNWFjNCIsImlhdCI6MTQ0MTUxMzMyNCwiZXhwIjoxNDQxNTMxMzI0fQ"
}

Any ideas? The payload is parsed correctly.

wrong tag - RS256

Hi,

I have the following JWT token (generated from keycloak ):

eyJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI5ZTVlYTkxYS05ZDk0LTQ2M2YtYjVlMi00Y2U0YWRjMzM2MzYiLCJleHAiOjE0NTgyOTQ0NzcsIm5iZiI6MCwiaWF0IjoxNDU4Mjk0MTc3LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvVGVzdCIsImF1ZCI6IlRlc3RDbGllbnQiLCJzdWIiOiIzYzFmMjBkOS1kNDgxLTQzN2QtOWZlOC03YzU0YzZhNDY1ZTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJUZXN0Q2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjlkZTJlNDYyLThmMGUtNGU3Yi05MmM2LTEzMzc5MDQ4OGM2NiIsImNsaWVudF9zZXNzaW9uIjoiNTI3MDkyODMtYzNmNy00ZGQxLWExMjYtOGRmNjBmNDRhNWU5IiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdCJ9.fujEnidGKs1F2RRG-MGKP-aQDa8DqzR3nQDpt-TQDQ1rBbj4xdI2UDFL1tgMNj-V--xf_bKujf0ROM5VUd-AOdR0n5dnmO8pgm02P-0Pc5Ycat2ADJoL3TlJJSE2SkZNPQ-MS3DN2d_bnJak3If3OwlzXH7puOVgxGnjNnWWTm_nZXi2Y1gdUOdMOzbUDR--e2-hDPwZG5PS4xUX_XbMMVhPAwYqsEfpPdYVXgcoF7czjWPFPqtaEXy0BvJashdsy-Tv56wusKrt38tWP5MA6qWxtXNP4Cho-RGdAI6jDgmsJKE-jDSME7vVy-5i6w1NI_hr_4zakOLzySfcsFgYRg

Public key:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArNMMislfx2MqXNLP5rFOZ5/K54qpI2JitGBoosOuxBGnNS8nbsqDW7VoXEDooR3KrZoJQurxKuvFQ/RnEWpNNd6wZnussIbmQzYKOBhzpu3ksOena7enfxAVTfc5WiuU1+NwP9AEbJJWSPskMkEINluk+aCL5y8d27q3Rooo9bvtcrR4f7RxvHnv1mzaXGP2b7W7AO2qMoiizmZWWKTxeea2RmGniNFHbI2i0SVTQWdW3KKo+iX98SC0HCw8KnFI6wbwABDnel7xLz+LRinw9+Jr9gQdAPFZIY1tKyKiqzGkCnz3N3DPdE3dHON8Mnsh1Lwv9FQurYdwB0vvfH5ZhQIDAQAB

Unfortunately when verifying the token with lua.jwt:
local jwt_obj = jwt:verify(cert, jwt_token)
The token is not verified and the reason is "Wrong tag" which I wasn't able to decipher what it means.

{"payload":{"nbf":0,"azp":"TestClient","client_session":"52709283-c3f7-4dd1-a126-8df60f44a5e9","iat":1458294177,"iss":"http://localhost:8080/auth/realms/Test","aud":"TestClient","preferred_username":"test","name":"","session_state":"9de2e462-8f0e-4e7b-92c6-133790488c66","sub":"3c1f20d9-d481-437d-9fe8-7c54c6a465e6","exp":1458294477,"resource_access":{"account":{"roles":["manage-account","view-profile"]}},"jti":"9e5ea91a-9d94-463f-b5e2-4ce4adc33636","allowed-origins":{},"typ":"Bearer"},"reason":"wrong tag","raw_header":"eyJhbGciOiJSUzI1NiJ9","valid":true,"header":{"alg":"RS256"},"signature":"fujEnidGKs1F2RRG-MGKP-aQDa8DqzR3nQDpt-TQDQ1rBbj4xdI2UDFL1tgMNj-V--xf_bKujf0ROM5VUd-AOdR0n5dnmO8pgm02P-0Pc5Ycat2ADJoL3TlJJSE2SkZNPQ-MS3DN2d_bnJak3If3OwlzXH7puOVgxGnjNnWWTm_nZXi2Y1gdUOdMOzbUDR--e2-hDPwZG5PS4xUX_XbMMVhPAwYqsEfpPdYVXgcoF7czjWPFPqtaEXy0BvJashdsy-Tv56wusKrt38tWP5MA6qWxtXNP4Cho-RGdAI6jDgmsJKE-jDSME7vVy-5i6w1NI_hr_4zakOLzySfcsFgYRg","verified":false,"raw_payload":"eyJqdGkiOiI5ZTVlYTkxYS05ZDk0LTQ2M2YtYjVlMi00Y2U0YWRjMzM2MzYiLCJleHAiOjE0NTgyOTQ0NzcsIm5iZiI6MCwiaWF0IjoxNDU4Mjk0MTc3LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvVGVzdCIsImF1ZCI6IlRlc3RDbGllbnQiLCJzdWIiOiIzYzFmMjBkOS1kNDgxLTQzN2QtOWZlOC03YzU0YzZhNDY1ZTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJUZXN0Q2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjlkZTJlNDYyLThmMGUtNGU3Yi05MmM2LTEzMzc5MDQ4OGM2NiIsImNsaWVudF9zZXNzaW9uIjoiNTI3MDkyODMtYzNmNy00ZGQxLWExMjYtOGRmNjBmNDRhNWU5IiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJ2aWV3LXByb2ZpbGUiXX19LCJuYW1lIjoiIiwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdCJ9"}

The same token and public key pass verification in java libraries.

Enforcing a stricter kind of validation

Would you be against a stricter kind of jwt validation?

Currently when the exp|nbf claim are not expressed as a number, they're silently ignored.

Two options:

  • Make it stricter by design and fail the verification of the jwt with a meaningful message when exp isn't a number
  • Allow an optional stricter kind of validation (through jwt:enforce_strict_validation(bool) for instance)

Plans for luarocks?

I am fairly new to Lua and coming from Ruby it is hard to live without package managers. Any chance you can add the rockspec and publish to luarocks?

Eg.

package = 'lua-resty-jwt'
version = '0.1.2-0'
source = {
  url = "git://github.com/SkyLothar/lua-resty-jwt",
  branch = "master"
}
description = {
  summary = 'JWT for ngx_lua and LuaJIT.',
  detailed = [[
    This library requires an nginx build 
    with OpenSSL, the ngx_lua module, 
    the LuaJIT 2.0, the lua-resty-hmac, 
    and the lua-resty-string,
  ]],
  homepage = 'https://github.com/SkyLothar/lua-resty-jwt',
  license = 'Apache License Version 2'
}
dependencies = {
  'lua >= 5.1',
  'lua-resty-hmac >= 1.0',
  'lua-resty-string >= 0.09',
}
build = {
  type = 'builtin',
  modules = {
    ['resty.jwt'] = 'lib/resty/jwt',
    ['resty.evp'] = 'lib/resty/evp'
  }
}

Question regarding expiration

Hi.

We've been playing with jwt and we've noticed that in your library, you only expire the token if "leeway" is not nil:

     if leeway ~= nil and not jwt_obj["reason"] then

Shouldn't the token be expired regardless of "leeway" being present or not, if "exp" is present in the payload of the token?

Thanks for your attention.

question about set and check expire time

How to set expire time when create a new jwt token?
And how to check a token is expired? Does below codes working?
local jwt_obj = jwt:verify("jwt-key", token,
{
lifetime_grace_period = 120,
require_exp_claim = true
}
)

Module 'resty.aes' not found:

Hi folks, I'm trying to import the lua-resty-jwt in a script but this error module 'resty.aes' not found: is displayed. I tried to find the lua-resty-aes package on luarocks but I could not.

My goal is to build a script to verify a JWT. My current script:

local jwt = require "resty.jwt"

print("Hello World")

Lua version: 5.3.5
OS: MacOS Sierra

New build 0.1.5

Any schedule for getting another release (0.1.5?) with the recent changes?

Confused about expected key format

I'm trying to verify a token that uses this jwk:

{
  kid = "20171030-00:00:00",
  alg = "RS256",
  n = "AIueZ4JvoqY1imSaLGLLjCAEe3n/1CdHlnjtRjSLAsK69/ucL3slei3ParNHhuCmhE16LoHwnBXRbjy+GTPjmfLH4HMqbsMrEjGF5FYLW/6JjHJFyUzkWkYh+W/I99FBRC+J2oXSgD0t4iblV4BMYVJljjigMzzTrQT0h2OtXonX2B2vTjwO+nC9oNVI2fY09eDtsyaJOynaduej8Mgo6w8if/q23R0+fQuPxVkY/tr3o6eeUARcVye74mqyPBtNH+Ear894w4ipgSyvl2NAFrGF79CPWypnN8OFPqMMvATXEDeh1VBSnEUqum8bvvjI6S2w32OFxoDQ9zXW5wTMQsU=",
  x5c = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi55ngm+ipjWKZJosYsuMIAR7ef/UJ0eWeO1GNIsCwrr3+5wveyV6Lc9qs0eG4KaETXougfCcFdFuPL4ZM+OZ8sfgcypuwysSMYXkVgtb/omMckXJTORaRiH5b8j30UFEL4nahdKAPS3iJuVXgExhUmWOOKAzPNOtBPSHY61eidfYHa9OPA76cL2g1UjZ9jT14O2zJok7Kdp256PwyCjrDyJ/+rbdHT59C4/FWRj+2vejp55QBFxXJ7viarI8G00f4Rqvz3jDiKmBLK+XY0AWsYXv0I9bKmc3w4U+owy8BNcQN6HVUFKcRSq6bxu++MjpLbDfY4XGgND3NdbnBMxCxQIDAQAB",
  kty = "RSA",
  e = "AQAB"
}

I couldn't find a good way to convert that to a pem in lua so I'm using a node script with the jwk-to-pem library. That gives me a key like this:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAi55ngm+ipjWKZJosYsuMIAR7ef/UJ0eWeO1GNIsCwrr3+5wveyV6
Lc9qs0eG4KaETXougfCcFdFuPL4ZM+OZ8sfgcypuwysSMYXkVgtb/omMckXJTORa
RiH5b8j30UFEL4nahdKAPS3iJuVXgExhUmWOOKAzPNOtBPSHY61eidfYHa9OPA76
cL2g1UjZ9jT14O2zJok7Kdp256PwyCjrDyJ/+rbdHT59C4/FWRj+2vejp55QBFxX
J7viarI8G00f4Rqvz3jDiKmBLK+XY0AWsYXv0I9bKmc3w4U+owy8BNcQN6HVUFKc
RSq6bxu++MjpLbDfY4XGgND3NdbnBMxCxQIDAQAB
-----END RSA PUBLIC KEY-----

I see this issue #7 that ends with a merged PR about supporting RSA public keys, but I'm still getting the "no start line" error when I call jwt:verify_jwt_obj.

nginx version: openresty/1.13.6.1

lua-resty-jwt
   0.2.0-0 (installed) - /usr/local/openresty/luajit/lib/luarocks/rocks

Tag releases

Hi - I was wondering what do you think about tagging the releases? It would make easier/more explicit to request a specific version when deploying/building lua-resty-jwt.

It would give you a release page within Github to easily refer to/download versions.

Custom Claim Containing Table Value

I've been working though auth0/nginx-jwt and following what you've done since with jwt-validators, in my own attempt to add one more bit of functionality. I wanted to propose an addition or see if I missed something in my own implementation.

Motivation: validating a claim containing an array/table of values from which one must match. For example, validating Foo as a role in a token containing something of the form:

{
  "roles" : [
     "Foo",
     "Bar"
   ],
   iss: "issuer",
   .
   .
},

I was able to accomplish this combining the validators.any_of with the nginx-jwt method for table_contains, looking something like:

access_by_lua_block {
     jwt = require("auth.nginx-jwt")
     local valid_roles = {"Foo","Bar"}
     .
     .
     local claim_spec = {
         .
         roles = validators.any_of(valid_roles, jwt.table_contains, "roles","table","string")
         .
     }
     .
     .
}

So, I'm proposing a method that uses table_contains in a similar form of equality_function and its friends, altogether allowing to expose a new validator:

validators.contains_any_of(check_values)(opt)

Thoughts?

How to properly cope with certificate renewal?

The lib provides a way to validates certs through jwt:set_trusted_certs_file("/path/to/may/cert.pem").

We're currently assessing the integrity of jwts by

  • first validating the public cert exposed by the x5u claim against a trusted root one (set through jwt:set_trusted_certs_file("/path/to/may/cert.pem"))
  • then checking the signature of the signed jwt

However, it is unclear for me how we should cope with a renewal of the trusted root cert. Should the old one and the new one be concatenated in the .pem file?

Is this project still active?

There are 3 PRs open for 6 months plus. We'd really like to use this project in production in our organisation but it difficult to do so comfortably if it's effectively abandoned.

Validate header claims

I'd like to be able to validate header claims with my claim spec as well -- this could look like:

local claimSpec = {
  header = {
    alg = validators.equals('HS256'),
    kid = validators.equals('MyKeyID'),
    custom = validators.equals('other')
  },
  payload = {
    exp = validators.required(validators.opt_is_not_expired()),
  }
}

openssl 1.1 compatibility ?

Hi,

debian will ship nginx with openssl 1.1 support, meaning... one won't be able to
run this module in next's debian stable. Any plans about that ?

luarocks install lua-resty-jwt fails

Luarocks installation fails.

In my ubuntu desktop machine:

$sudo luarocks install lua-resty-jwt

Warning: falling back to curl - install luasec to get native HTTPS support
Installing https://luarocks.org/lua-resty-jwt-0.1.10-0.src.rock

Error: Couldn't extract archive .: unrecognized filename extension

And in my Dockerfile (based on CentOS):

Step 6/11 : RUN luarocks install lua-resty-jwt
---> Running in dd206fdf553c
Warning: falling back to curl - install luasec to get native HTTPS support

Error: Unrecognized filename extension .
Installing https://luarocks.org/lua-resty-jwt-0.1.10-0.src.rock...
Using https://luarocks.org/lua-resty-jwt-0.1.10-0.src.rock... switching to 'build' mode
The command '/bin/sh -c luarocks install lua-resty-jwt' returned a non-zero code: 1

ES{256,384,512} Support

Related to #36.

openssl ecparam -name secp521r1 -genkey -out ES512.pem && openssl ec -in ES512.pem -pubout >> ES512.pem

Comment mention both x5c and x5u being defined when they are not

-- TODO When both x5c and x5u are defined, the implementation should
-- ensure their content match
-- cf. https://tools.ietf.org/html/rfc7517#section-4.6
jwt_obj[str_const.reason] = "Unsupported RS256 key model"
return nil

In the code above, the comment talks about not handling the case of both x5c and x5u are defined. However, it is possible to get that far down in the code path if both x5c and x5u are nil. Is that supposed to be a different case that should be handled?

According to RFC 7515, both x5c and x5u as header parameters are optional it seems...?

no start line - RS256

Greetings.

I'm not sure if it's something I'm completely borking up, but I can't see another way to do it.

We use your library to send JWT's from our frontends to our backends out of paranoia - this is working great, php has no trouble decoding these.

I am now trying to use your library to accept a JWT from a different provider (encoded with go-jwt)

This is the public key

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0UJybPz2/GQAGh/Ri3rvsv60h
fYB3JzoZpWzKBPPXeHC/w7rnOSdOjeqLT4evgfRXjsld9Rx5GYXARiEicTl1l9DU
+P9xk0eptU/Cawz003/XT5tPX+uhmFcC3zIa3oUb/25Kq+IPeq1AUTK9PcJsohFk
pcgtuZvphRLKvFA8uQIDAQAB
-----END PUBLIC KEY-----

This is the token

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NDQ2OTQxNzksImlhdCI6MTQ0NDY1MDk3OSwiaXAiOiIxNzIuMTYuMjAzLjEiLCJuYmYiOjE0NDQ2NTA5NzksInVzZXIiOiJzaGFubm9uLnd5bnRlciJ9.PLEe64xKFGpkwrX2OameIT6_xSpl5mW_BxFmI4sz20SS_jOaemWrwUBB4gMIUm9LwhxUWTAr5wdLMF30bDJLSfQamf2VeggGMzvjKMQdbJcOPzT6QPmuITAju7bn6WzLzEE5cThIXhOCEdNGKf1xDp9om9T8HVZ15nWpuwwvqwo

This is the output

{"payload":{"nbf":1444650979,"ip":"172.16.203.1","exp":1444694179,"user":"shannon.wynter","iat":1444650979},"reason":"no start line","raw_header":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9","valid":true,"header":{"alg":"RS256","typ":"JWT"},"signature":"PLEe64xKFGpkwrX2OameIT6_xSpl5mW_BxFmI4sz20SS_jOaemWrwUBB4gMIUm9LwhxUWTAr5wdLMF30bDJLSfQamf2VeggGMzvjKMQdbJcOPzT6QPmuITAju7bn6WzLzEE5cThIXhOCEdNGKf1xDp9om9T8HVZ15nWpuwwvqwo","verified":false,"raw_payload":"eyJleHAiOjE0NDQ2OTQxNzksImlhdCI6MTQ0NDY1MDk3OSwiaXAiOiIxNzIuMTYuMjAzLjEiLCJuYmYiOjE0NDQ2NTA5NzksInVzZXIiOiJzaGFubm9uLnd5bnRlciJ9"}```

And this is how I get that

local cookie_value = ngx.var.cookie_lda
if (cookie_value == nil) then
    ngx.exit(401)
else
    local public_key = [[-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0UJybPz2/GQAGh/Ri3rvsv60h
fYB3JzoZpWzKBPPXeHC/w7rnOSdOjeqLT4evgfRXjsld9Rx5GYXARiEicTl1l9DU
+P9xk0eptU/Cawz003/XT5tPX+uhmFcC3zIa3oUb/25Kq+IPeq1AUTK9PcJsohFk
pcgtuZvphRLKvFA8uQIDAQAB
-----END PUBLIC KEY-----]]

    local jwt_obj = jwt:verify(public_key, cookie_value)
    ngx.log(ngx.ERR, cjson.encode(jwt_obj))
end

The debug tool at jwt.io says it's a verified signature but this library won't verify it saying "no start line"

Any help?

Include hmac in distribution?

I don't know if it's possible (for licensing/other reasons), but it would be nice to include the stuff inside of "vendor/resty" in the packaged distribution when builds are made.

Again - not a super urgent issue - and it's something that can be grabbed separately, but it's kind of nice to have a distribution package that contains all the required files.

Generating a token in php

Does anyone have experience generating the token in php?

I encountered two surprises. No direct support for HS256 in php.
But if I have googled correctly, 'sha256' is the sam3 thing.

Results do not match what is generated by /sign ?

I can use curl to get a result --but that seems weird. Maybe it isn't?!

$payload=[
    'typ'=>"JWT",
    'alg'=>"HS256"
];
$jload = json_encode($payload);

// string hash_hmac ( string $algo , string $data , string $key [, bool $raw_output = false ] )

$key="lua-resty-jwt";

$hash = hash_hmac("sha256", $jload, $key, false);
echo "\nHASHMACF:\n$hash";

$hash = hash_hmac("sha256", $jload,$key, true);
echo "\nHASHMACT:\n$hash";

Appreciate any advice, thank you.
I had assumed I could generate the token in php along with the page.
But worried now I have misunderstood something essential.

Got 'everything is awesome~ :p' error

I met a verify error which reason is "interval error", then I modify this line to jwt_obj[str_const.reason] = cjson.decode(ret), then I got a reason "everything is awesome~ :p", is there some logic missed in your code?

Tokens always pass validation even with invalid checksums when using asymmetric key algorithms

Hello,

First of all, let me say that my knowledge of Lua can be measured in "days", so please be gentle :-)

I have been using the library for the last weeks, and I it seems that there is a critical bug in the validation of tokens when using asymmetric algorithms (RS256 or RS512).

In the following code in jwt.lua _M:verify_jwt_object:

...
    if alg == str_const.RS256 then
      local verified, err = verifier:verify(message, sig, evp.CONST.SHA256_DIGEST)
    elseif alg == str_const.RS512 then
      local verified, err = verifier:verify(message, sig, evp.CONST.SHA512_DIGEST)
    end
    if not verified then
      jwt_obj[str_const.reason] = err
    end
  else
    jwt_obj[str_const.reason] = "Unsupported algorithm " .. alg
  end

  if not jwt_obj[str_const.reason] then
    jwt_obj[str_const.verified] = true
    jwt_obj[str_const.reason] = str_const.everything_awesome
  end
  return jwt_obj
...

As far as I understand, this piece of code creates variables verified and err in local block scope, meaning that out of the if-then-else they will be nil, no matter what happens after the verification. Then the next piece of code will set the result verified to true depending on reason, which was assigned to nil from the err value.

The end result seems to be that, no matter which key is passed, when this code path is reached the verification will always succeed.

I did a quick fix on my code with something like the following and it seems to work (please excuse my poor Lua):

    -- a previous check makes sure that 'alg' can only be either 'str_const.RS256'
    -- or 'str_const.RS512'
    local evp_digest = nil
    if alg == str_const.RS256 then
      evp_digest = evp.CONST.SHA256_DIGEST
    else
      evp_digest = evp.CONST.SHA512_DIGEST
    end

    local verified, err = verifier:verify(message, sig, evp_digest)

    if not verified then
      jwt_obj[str_const.reason] = err
    end
  else 
    jwt_obj[str_const.reason] = "Unsupported algorithm " .. alg
  end

  if not jwt_obj[str_const.reason] then
    jwt_obj[str_const.verified] = true
    jwt_obj[str_const.reason] = str_const.everything_awesome
  end
  return jwt_obj

Am I missing anything here?

Add support for providing a function as the key

It would be helpful to be able to provide a function or a string as the key to the verify functions. Such a function would be passed the value of kid from the header as a single parameter, and would return a string to use as the key. This would simplify, for example, using a hash table or a Redis database for looking up keys based off of the kid parameter.

I actually have some code that does this, and am willing to put together a pull request if it would be something that is desired.

When to exect the release of v0.1.7?

I'd like to bundle my project with fixed implementation of jwe. Any ideas when will be a next release? Is there anything that blocks the next release?

Verification of claims

The way that the "auth" function of the code at https://github.com/auth0/nginx-jwt is pretty nice. It seems to me that this would be a nicer (and more flexible) alternative to the verification code that is currently being worked on.

Basically, you specify a table with claims you want to verify. Existence of a claim key requires the presence of it. The value of the claims can either be a string (in which case, it's matched against the string value in the payload), or a function which takes a single parameter (the value from the payload) and returns true or false.

It seems that the current validation of nbf and exp (and the grace period and issuers check) could be done using this mechanism instead, if it were available.

I actually have some code that does this, and am willing to put together a pull request if it would be something that is desired.

Question on JWT token validation.

I am trying to validate my access_token using https://github.com/pingidentity/lua-resty-openidc#sample-configuration-for-oauth-20-jwt-token-validation. Somehow I wasn't able to succeed. My goal is to protect my apis using this approach. Here is my configuration. Am I missing something here. I am getting 2 issues here. Can someone tell me where I am going wrong. I was able to verify the signature using http://jwt.io

openidc.lua calls https://github.com/pingidentity/lua-resty-openidc/blob/master/lib/resty/openidc.lua#L723

  1. With the below configuration, I am getting openidc_discover(): accessing discovery url (https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) failed: 20: unable to get local issuer certificate, client:

  2. When I added secret (public key string from my secret.pem file) I am getting "reason":"Verification failed","raw_header":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6InowMzl6ZHNGdWl6cEJmQlZLMVRuMjVRSFlPMCIs

location /api {
      access_by_lua '
          local opts = {
            -- The jwks endpoint must provide a x5c entry
   discovery = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"
          }
          -- call bearer_jwt_verify for OAuth 2.0 JWT validation
          local res, err = require("resty.openidc").bearer_jwt_verify(opts)
..
..
    }
  }
}

I am sending Curl request with authorization token to test if my /api is secured or not like below
curl -i http://myserver.com/api/index.html -H 'Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6InowMzl6ZHNGdWl6cEJmQlZLMVRuMjVRSFlPMCIs' @

Push the library on OPM ?

Hello,

Currently, I'm using your library for an another project, I would like to import it from OPM.

Could you push your library on it please ?

I also created a ticket on lua-resty-hmac to ask them to push their project on OPM.

Thank you.

Causes for "invalid jwt string" ?

Thanks to all.

I am new to both nginx and lua, so this is probably not an issue.
Just not sure where to turn now.
I am trying to extend the example by adding a user id.
Am I attempting something impossible, or do I need more help with syntax (nginx or lua??)
Many variations tried. The following seems closest...

I get a result for the gentoken location:

curl  http://127.0.0.1/gentoken/123
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIkdXNlciJ9.1fb3i8IsaAXL-syRWQzhiGruOUruCMzUxBxHtig3e8c

But I'm running out of ideas on the "invalid jwt string" response:

curl  http://127.0.0.1/verifytoken/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIkdXNlciJ9.1fb3i8IsaAXL-syRWQzhiGruOUruCMzUxBxHtig3e8c
{"verified":false,"reason":"invalid jwt string"}
        location  ~ ^/gentoken/(?<user>.+)$ {
            add_header X-USER "$user";

            content_by_lua '
                local cjson = require "cjson"
                local jwt = require "resty.jwt"

                local jwt_token = jwt:sign(
                    "lua-resty-jwt",
                    {
                        header={typ="JWT", alg="HS256"},
                        payload={uid="$user"}
                    }
                )
                ngx.say(jwt_token)
            ';
        }
    location ~ ^/verifytoken/(?<tok>.+)$ {
            content_by_lua '
                local cjson = require "cjson"
                local jwt = require "resty.jwt"
                local jwt_obj = jwt:verify("lua-resty-jwt", "$tok")

                ngx.say(cjson.encode(jwt_obj))
            ';
        }

Memory leak jwt:verify with X509 certificate

I'm experiencing memory leak when using X509. Ran through Valgrind and most of the leak points to X509_get_pubkey. Not sure if this a OpenSSL issue or something else.

Specs:
Centos 7 - 64Bit
nginx version: openresty/1.11.2.3
openssl-1.0.1e-60.el7_3.1.x86_64
SkyLothar/lua-resty-jwt 0.1.10

valgrind -v --log-file=memcheck.log --tool=memcheck --leak-check=full openresty-valgrind -c /srv/openresty-apigw/conf/nginx.conf -p /srv/openresty-apigw

memcheck.log

==16375== Memcheck, a memory error detector ==16375== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==16375== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==16375== Command: openresty-valgrind -c /srv/openresty-apigw/conf/nginx.conf -p /srv/openresty-apigw ==16375== Parent PID: 16374 ==16375== --16375-- --16375-- Valgrind options: --16375-- -v --16375-- --log-file=memcheck.log --16375-- --tool=memcheck --16375-- --leak-check=full --16375-- Contents of /proc/version: --16375-- Linux version 3.10.0-514.16.1.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) ) #1 SMP Wed Apr 12 15:04:24 UTC 2017 --16375-- --16375-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-rdtscp-sse3-avx --16375-- Page sizes: currently 4096, max supported 4096 --16375-- Valgrind library directory: /usr/lib64/valgrind --16375-- Reading syms from /usr/local/openresty-valgrind/nginx/sbin/nginx --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/lib64/ld-2.17.so --16375-- Reading syms from /usr/lib64/valgrind/memcheck-amd64-linux --16375-- object doesn't have a symbol table --16375-- object doesn't have a dynamic symbol table --16375-- Scheduler: using generic scheduler lock implementation. --16375-- Reading suppressions file: /usr/lib64/valgrind/default.supp ==16375== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-16375-by-root-on-vm1 ==16375== embedded gdbserver: writing to /tmp/vgdb-pipe-to-vgdb-from-16375-by-root-on-vm1 ==16375== embedded gdbserver: shared mem /tmp/vgdb-pipe-shared-mem-vgdb-16375-by-root-on-vm1 ==16375== ==16375== TO CONTROL THIS PROCESS USING vgdb (which you probably ==16375== don't want to do, unless you know exactly what you're doing, ==16375== or are doing some strange experiment): ==16375== /usr/lib64/valgrind/../../bin/vgdb --pid=16375 ...command... ==16375== ==16375== TO DEBUG THIS PROCESS USING GDB: start GDB like this ==16375== /path/to/gdb openresty-valgrind ==16375== and then give GDB the following command ==16375== target remote | /usr/lib64/valgrind/../../bin/vgdb --pid=16375 ==16375== --pid is optional if only one valgrind process is running ==16375== --16375-- REDIR: 0x4018e90 (ld-linux-x86-64.so.2:strlen) redirected to 0x38056d91 (???) --16375-- REDIR: 0x4018c60 (ld-linux-x86-64.so.2:index) redirected to 0x38056dab (???) --16375-- Reading syms from /usr/lib64/valgrind/vgpreload_core-amd64-linux.so --16375-- Reading syms from /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so ==16375== WARNING: new redirection conflicts with existing -- ignoring it --16375-- old: 0x04018e90 (strlen ) R-> (0000.0) 0x38056d91 ??? --16375-- new: 0x04018e90 (strlen ) R-> (2007.0) 0x04c2aa90 strlen --16375-- REDIR: 0x4018e10 (ld-linux-x86-64.so.2:strcmp) redirected to 0x4c2bbe0 (strcmp) --16375-- REDIR: 0x4019a00 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x4c2ec20 (mempcpy) --16375-- Reading syms from /usr/lib64/libdl-2.17.so --16375-- Reading syms from /usr/lib64/libpthread-2.17.so --16375-- Reading syms from /usr/lib64/libcrypt-2.17.so --16375-- Reading syms from /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0 --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/lib64/libm-2.17.so --16375-- Reading syms from /usr/local/openresty/pcre/lib/libpcre.so.1.2.8 --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/local/openresty-debug/openssl/lib/libssl.so.1.0.0 --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0 --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/local/openresty/zlib/lib/libz.so.1.2.11 --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/lib64/libGeoIP.so.1.5.0 --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/lib64/libc-2.17.so --16375-- Reading syms from /usr/lib64/libfreebl3.so --16375-- object doesn't have a symbol table --16375-- Reading syms from /usr/lib64/libgcc_s-4.8.5-20150702.so.1 --16375-- object doesn't have a symbol table --16375-- REDIR: 0x6884ec0 (libc.so.6:strcasecmp) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6881c40 (libc.so.6:strnlen) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6887190 (libc.so.6:strncasecmp) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x68846a0 (libc.so.6:memset) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6884650 (libc.so.6:memcpy@GLIBC_2.2.5) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x68800c0 (libc.so.6:strcmp) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6881b10 (libc.so.6:strlen) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6889860 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6881b60 (libc.so.6:__GI_strlen) redirected to 0x4c2a9f0 (__GI_strlen) --16375-- REDIR: 0x6883630 (libc.so.6:__GI_strrchr) redirected to 0x4c2a450 (__GI_strrchr) --16375-- REDIR: 0xffffffffff600400 (???:???) redirected to 0x38056d7d (???) --16375-- REDIR: 0x687af70 (libc.so.6:malloc) redirected to 0x4c27b5c (malloc) --16375-- REDIR: 0x68898d0 (libc.so.6:__GI_memcpy) redirected to 0x4c2c580 (__GI_memcpy) --16375-- REDIR: 0x6881d60 (libc.so.6:__GI_strncmp) redirected to 0x4c2b220 (__GI_strncmp) --16375-- REDIR: 0x6884d60 (libc.so.6:__GI_stpcpy) redirected to 0x4c2d8d0 (__GI_stpcpy) --16375-- REDIR: 0x6884870 (libc.so.6:__GI_mempcpy) redirected to 0x4c2e950 (__GI_mempcpy) --16375-- REDIR: 0x695ee60 (libc.so.6:__strlen_sse2_pminub) redirected to 0x4c2a9d0 (strlen) --16375-- REDIR: 0x6944220 (libc.so.6:__memcpy_ssse3_back) redirected to 0x4c2bfa0 (memcpy@@GLIBC_2.14) --16375-- REDIR: 0xffffffffff600000 (???:???) redirected to 0x38056d73 (???) --16375-- REDIR: 0x687b370 (libc.so.6:free) redirected to 0x4c28c56 (free) --16375-- REDIR: 0x689a540 (libc.so.6:__GI_strstr) redirected to 0x4c2eeb0 (__strstr_sse2) --16375-- REDIR: 0x68840c0 (libc.so.6:__GI_memcmp) redirected to 0x4c2d4d0 (__GI_memcmp) --16375-- REDIR: 0x6884700 (libc.so.6:__GI_memset) redirected to 0x4c2de80 (memset) --16375-- REDIR: 0x6884080 (libc.so.6:bcmp) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x6959790 (libc.so.6:__memcmp_sse4_1) redirected to 0x4c2d610 (__memcmp_sse4_1) --16375-- REDIR: 0x6883d30 (libc.so.6:memchr) redirected to 0x4c2bc80 (memchr) --16375-- REDIR: 0x692e570 (libc.so.6:__strcmp_sse42) redirected to 0x4c2bb90 (__strcmp_sse42) --16375-- REDIR: 0x687b450 (libc.so.6:realloc) redirected to 0x4c29aae (realloc) --16375-- REDIR: 0x6937f80 (libc.so.6:__strncasecmp_avx) redirected to 0x4c2b450 (strncasecmp) --16375-- REDIR: 0x68835b0 (libc.so.6:strncpy) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x688f930 (libc.so.6:__strncpy_sse2_unaligned) redirected to 0x4c2b060 (__strncpy_sse2_unaligned) --16375-- REDIR: 0x68844c0 (libc.so.6:__GI_memmove) redirected to 0x4c2e190 (__GI_memmove) --16375-- REDIR: 0x6880040 (libc.so.6:__GI_strchr) redirected to 0x4c2a580 (__GI_strchr) --16375-- REDIR: 0x688b150 (libc.so.6:strchrnul) redirected to 0x4c2e740 (strchrnul) --16375-- REDIR: 0x6880100 (libc.so.6:__GI_strcmp) redirected to 0x4c2baf0 (__GI_strcmp) --16375-- REDIR: 0x688efa0 (libc.so.6:__GI_strncpy) redirected to 0x4c2adc0 (__GI_strncpy) --16375-- REDIR: 0x687b960 (libc.so.6:calloc) redirected to 0x4c298df (calloc) --16375-- Reading syms from /usr/lib64/libnss_files-2.17.so --16375-- REDIR: 0x6881590 (libc.so.6:__GI_strcpy) redirected to 0x4c2ab90 (__GI_strcpy) --16375-- REDIR: 0x688af10 (libc.so.6:rawmemchr) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x688af40 (libc.so.6:__GI___rawmemchr) redirected to 0x4c2e7a0 (__GI___rawmemchr) --16375-- REDIR: 0x6936910 (libc.so.6:__strcasecmp_avx) redirected to 0x4c2b370 (strcasecmp) --16375-- REDIR: 0x6881d20 (libc.so.6:strncmp) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x692f320 (libc.so.6:__strncmp_sse42) redirected to 0x4c2b300 (__strncmp_sse42) --16375-- REDIR: 0x68836d0 (libc.so.6:strpbrk) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x69366d0 (libc.so.6:__strpbrk_sse42) redirected to 0x4c2efd0 (strpbrk) --16375-- REDIR: 0x6880000 (libc.so.6:index) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x692e4c0 (libc.so.6:__strchr_sse42) redirected to 0x4c2a640 (index) --16375-- REDIR: 0x69497c0 (libc.so.6:__memmove_ssse3_back) redirected to 0x4c2bd40 (memcpy@GLIBC_2.2.5) --16375-- REDIR: 0x689ab00 (libc.so.6:strstr) redirected to 0x4a227b0 (_vgnU_ifunc_wrapper) --16375-- REDIR: 0x69304a0 (libc.so.6:__strstr_sse42) redirected to 0x4c2ef40 (__strstr_sse42) ==16375== Syscall param epoll_ctl(event) points to uninitialised byte(s) ==16375== at 0x68F2CBA: epoll_ctl (in /usr/lib64/libc-2.17.so) ==16375== by 0x4605E5: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x460129: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x450A2B: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x45D431: ngx_single_process_cycle (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x41CA43: main (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== Address 0xfff0000a4 is on thread 1's stack ==16375== --16375-- memcheck GC: 1000 nodes, 998 survivors (99.8%) --16375-- memcheck GC: 1414 new table size (stepup) --16375-- memcheck GC: 1414 nodes, 1414 survivors (100.0%) --16375-- memcheck GC: 1999 new table size (stepup) --16375-- memcheck GC: 1999 nodes, 1999 survivors (100.0%) --16375-- memcheck GC: 2827 new table size (stepup) --16375-- REDIR: 0x4019b50 (ld-linux-x86-64.so.2:stpcpy) redirected to 0x4c2dc50 (stpcpy) --16375-- Reading syms from /usr/local/openresty-valgrind/lualib/cjson.so --16375-- object doesn't have a symbol table --16375-- Discarding syms at 0x7fea1e0-0x7fec70c in /usr/local/openresty-valgrind/lualib/cjson.so due to munmap() --16375-- Discarding syms at 0x73d71d0-0x73de3e4 in /usr/lib64/libnss_files-2.17.so due to munmap() ==16375== ==16375== HEAP SUMMARY: ==16375== in use at exit: 24,697,697 bytes in 155,572 blocks ==16375== total heap usage: 9,573,377 allocs, 9,417,805 frees, 937,530,735 bytes allocated ==16375== ==16375== Searching for pointers to 155,572 not-freed blocks ==16375== Checked 11,364,680 bytes ==16375== ==16375== 112 bytes in 2 blocks are possibly lost in loss record 70 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60A3C8B: EVP_PKEY_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60B5F49: X509_PUBKEY_get (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60D8987: X509_get_pubkey (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x40417D2: ??? ==16375== by 0x7134D97: ??? ==16375== by 0x54941FA: ??? (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x54BAAFA: lua_resume (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x5623BF: ngx_http_lua_run_thread (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x56AA48: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== ==16375== 552 bytes in 23 blocks are possibly lost in loss record 96 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F133: BN_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60B6AE6: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60B6C4A: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C1027: asn1_ex_c2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C0F78: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60BF665: ASN1_item_ex_d2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C0A55: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C062A: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C00CC: ASN1_item_ex_d2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== ==16375== 1,664 bytes in 16 blocks are possibly lost in loss record 125 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x604C319: BN_MONT_CTX_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x604C7D5: BN_MONT_CTX_set_locked (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606A943: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x6073E93: RSA_public_decrypt (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606CE4E: int_rsa_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606D26B: RSA_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x6072CF4: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60AB529: EVP_PKEY_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60ACC06: EVP_DigestVerifyFinal (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== ==16375== 1,848 bytes in 11 blocks are possibly lost in loss record 127 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606C0BA: RSA_new_method (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606BFAE: RSA_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606FE81: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60BD45B: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60BD1A7: ASN1_item_ex_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60BFE6B: ASN1_item_ex_d2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60BF43E: ASN1_item_d2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x6070088: d2i_RSAPublicKey (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60702F0: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== ==16375== 6,960 bytes in 38 blocks are possibly lost in loss record 141 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F2FB: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F634: bn_expand2 (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602FF9B: BN_bin2bn (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60B6C75: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C1027: asn1_ex_c2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C0F78: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60BF665: ASN1_item_ex_d2i (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C0A55: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60C062A: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== ==16375== 7,168 bytes in 28 blocks are possibly lost in loss record 142 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F2FB: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F634: bn_expand2 (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F8A1: BN_copy (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x604C493: BN_MONT_CTX_set (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x604C801: BN_MONT_CTX_set_locked (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606A943: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x6073E93: RSA_public_decrypt (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606CE4E: int_rsa_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606D26B: RSA_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== ==16375== 22,360 bytes in 43 blocks are possibly lost in loss record 145 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F2FB: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x602F634: bn_expand2 (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60305A7: BN_set_bit (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x604C64E: BN_MONT_CTX_set (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x604C801: BN_MONT_CTX_set_locked (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606A943: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x6073E93: RSA_public_decrypt (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606CE4E: int_rsa_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x606D26B: RSA_verify (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== ==16375== 98,304 bytes in 1 blocks are possibly lost in loss record 146 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x457883: ngx_alloc (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x450DC4: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x45D431: ngx_single_process_cycle (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x41CA43: main (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== ==16375== 98,304 bytes in 1 blocks are possibly lost in loss record 147 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x457883: ngx_alloc (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x450EA1: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x45D431: ngx_single_process_cycle (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x41CA43: main (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== ==16375== 245,760 bytes in 1 blocks are possibly lost in loss record 148 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x457883: ngx_alloc (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x450D54: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x45D431: ngx_single_process_cycle (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x41CA43: main (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== ==16375== 9,466,168 (372,792 direct, 9,093,376 indirect) bytes in 6,657 blocks are definitely lost in loss record 155 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60A3C8B: EVP_PKEY_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60B5F49: X509_PUBKEY_get (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60D8987: X509_get_pubkey (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x54962EC: ??? (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x553428D: ??? (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x5559D39: ??? (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x54941FA: ??? (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x54BAAFA: lua_resume (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x5623BF: ngx_http_lua_run_thread (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== ==16375== 14,644,616 (576,968 direct, 14,067,648 indirect) bytes in 10,303 blocks are definitely lost in loss record 156 of 156 ==16375== at 0x4C27BE3: malloc (vg_replace_malloc.c:299) ==16375== by 0x5FE8044: ??? (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x5FE86FB: CRYPTO_malloc (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60A3C8B: EVP_PKEY_new (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60B5F49: X509_PUBKEY_get (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x60D8987: X509_get_pubkey (in /usr/local/openresty-debug/openssl/lib/libcrypto.so.1.0.0) ==16375== by 0x40417D2: ??? ==16375== by 0x7134D97: ??? ==16375== by 0x54941FA: ??? (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x54BAAFA: lua_resume (in /usr/local/openresty-valgrind/luajit/lib/libluajit-5.1.so.2.1.0) ==16375== by 0x5623BF: ngx_http_lua_run_thread (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x56AA48: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== ==16375== LEAK SUMMARY: ==16375== definitely lost: 949,760 bytes in 16,960 blocks ==16375== indirectly lost: 23,161,024 bytes in 135,524 blocks ==16375== possibly lost: 483,032 bytes in 164 blocks ==16375== still reachable: 103,881 bytes in 2,924 blocks ==16375== suppressed: 0 bytes in 0 blocks ==16375== Reachable blocks (those to which a pointer was found) are not shown. ==16375== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==16375== ==16375== Use --track-origins=yes to see where uninitialised values come from ==16375== ERROR SUMMARY: 13 errors from 13 contexts (suppressed: 0 from 0) ==16375== ==16375== 1 errors in context 1 of 13: ==16375== Syscall param epoll_ctl(event) points to uninitialised byte(s) ==16375== at 0x68F2CBA: epoll_ctl (in /usr/lib64/libc-2.17.so) ==16375== by 0x4605E5: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x460129: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x450A2B: ??? (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x45D431: ngx_single_process_cycle (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== by 0x41CA43: main (in /usr/local/openresty-valgrind/nginx/sbin/nginx) ==16375== Address 0xfff0000a4 is on thread 1's stack ==16375== ==16375== ERROR SUMMARY: 13 errors from 13 contexts (suppressed: 0 from 0)

RSA512 support?

Is it planned? or can you explain how to add support for RSA512?

id_token 'RS256' signature verification failed, client: xxx.xxx.170.207

I am able to validate the token at jwt.io. Signature validation is also true using my public key.

2018/01/15 06:13:11 [debug] 256#256: *78 [lua] openidc.lua:737: openidc_load_jwt_and_verify_crypto(): jwt: {"signature":"Jb9T5QlRDIJMdhcC_jQ9bZlCKuRBEWe9BJd70g0sw1UJbznKwXb46eGsaGgAF1aorsZaEQqefAxkL5rfgp9dPVETyAQrToGD74QloStLq_15oeCLa0wAeJvHGb4x_pEGNqAjLYHu04MaguQfVZsVulM80kxBLCQ7ILJvii06HuU","reason":"too long",

"reason":"too long" - what could be the reason it repeatedly shows "too long" and verification fails on nginx ?

Reference - zmartzone/lua-resty-openidc#135

Add handling of intermediate certs in x5u

I'm thinking about working on this topic.

Currently we're relying on Cert.new() and Cert.verify_trust() that have been initially contributed by @dhiltgen

According to the RFC "The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the first certificate.This MAY be followed by additional certificates, with each subsequent certificate being the one used to certify the previous one."

The use case I'm willing to deal with would be as follows

  • the x5u url would return something like

    -----BEGIN CERTIFICATE-----
    CERT_1: Leaf cert
    -----END CERTIFICATE
    -----BEGIN CERTIFICATE-----
    CERT_2: Intermediate cert
    -----END CERTIFICATE
    -----BEGIN CERTIFICATE-----
    CERT_3: Self signed root cert
    -----END CERTIFICATE
    
  • The trusted cert location store would contain the self signed root cert as well (CERT_3).

I've started to study (a bit) the OpenSSL API and think I should be able to send something allowing this. However, that will be my first encounter with OpenSSL and I'd be very grateful for some help.

Would the following sequence of calls do the trick?

  • X509_STORE_load_locations(file_containing_CERT_3)
  • sk_X509_push(CERT_3)
  • sk_X509_push(CERT_2)
  • X509_STORE_CTX_set_chain()
  • X509_STORE_CTX_set_cert()
  • X509_verify_cert()

Besides, I've got the feeling that the evp.lua Cert API would have to be changed (in order to cope with chains of certs), and I'd prefer to potentially have some feedback regarding how it should shaped (once again, no real experience dealing with chain of certs, so I'm not sure I'd properly think of all the potential use cases and corner cases).

On the other hand, would anyone be already cooking some code in order to make this happen, I'd be happy to beta test it ๐Ÿ˜‰

Thoughts?

Standard claims (such as exp) not validated by default

This was a bummer to me. By default, the following library usage is insecure at not RFC-compliant:

local jwt_obj = jwt:verify(SECRET_KEY, token)

For example, this happily returns jwt_obj.verified being true, even if the token is expired.

I understand that application developers must always be responsible when using security-relevant libraries. But not validating claims by default and not explicitly warning about this in the docs in practice leads to many vulnerable applications.

claim_spec by default really is empty, so that validate_claims() by default just jumps to return true:

for _, claim_spec in ipairs(claim_specs) do

The JWT RFC https://tools.ietf.org/html/rfc7519 specifies the behavior of certain claims such as the exp claim, and I really would have assumed that lua-resty-jwt (like all of the other JWT libraries I have looked at) would validate those claims by default.

What we IMO need to have, at least: a big big warning in the README that calling local jwt_obj = jwt:verify(SECRET_KEY, token) alone leads to a validation process that is

  1. insecure (as of e.g. the missing exp validation)
  2. not RFC-compliant

What would be better: have an example invocation in the docs that leads to RFC-compliant token verification (i.e. a verification that processes all standard claims in the way as specified in RFC 7519).

What would be best, I think: jwt:verify(key, jwt_token) should be secure/RFC-compliant. As simple as that. :)

Release version 0.1.3

Are there plans to release version 0.1.3 (which has the validation options in it), or are there other features waiting before a release happens?

RS256

Getting segmentation fault log on signing a new JWT token using RSA private key and certificate

Eliminate header? Can this be an option?

RE https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

Apologies if this has already been covered. I am not expert enough with LUA to read the code and be sure I have understood.

If I understand the issue, I am not immediately vulnerable, because my usage is internal (I have a secret key, do not use third-parties and public keys). Cf comment from Josรฉ Romaniello

But still, my preference would be to remove the header all together. Make for a shorter transmission, and remove any clue about which algorithm was used. Cf comment from Federico Rampazzo

I do not yet have the lua mojo to attempt this. Nor do I ask for it.

Thank you for sharing this ! I am excited to be discovering the world of lua+nginx.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.