Repository navigation
csrf, encoding: make luaossl optional at runtime - #820
Open
a-schaefers wants to merge 1 commit into
Open
a-schaefers wants to merge 1 commit into
a-schaefers wants to merge 1 commit into
Conversation
lapis.util.encoding and lapis.csrf required luaossl as soon as they were
loaded. Every app loads lapis.util.encoding (lapis.application ->
lapis.request -> lapis.session), so an OpenResty app could not start
without luaossl, even though OpenResty signs sessions with ngx.hmac_sha1
and ships a CSPRNG in resty.random. Every request failed with:
./lapis/util/encoding.lua:3: module 'openssl.hmac' not found
This is the runtime half of leafo/lapis issue 802, where luaossl fails
to compile with GCC 15+ (C23). Each module now picks a backend when it
loads, the way pgmoon does in pgmoon/crypto.moon. luaossl is first in
every chain, so nothing changes for installs that have it.
lapis.util.encoding
- HMACs are created with luaossl's openssl.hmac, falling back to
lua-resty-openssl's resty.openssl.hmac. Both expose new(key, digest)
and final(str).
- Inside OpenResty hmac_sha1 is still ngx.hmac_sha1, so the default
hmac_digest ("sha1") needs neither library there. hmac_digest =
"sha256" needs luaossl or lua-resty-openssl.
lapis.csrf
- The 32 random bytes behind the token cookie come from luaossl's
openssl.rand, falling back to resty.random.bytes(n, true), which calls
RAND_bytes in the OpenSSL that nginx links against.
- resty.random is only used inside a real nginx process, detected by
ngx.config. In plain LuaJIT it loads fine but crashes when called
("undefined symbol: RAND_bytes"), and the fake ngx of
lapis.spec.request has no ngx.config. This checks ngx.config rather
than lapis.nginx.is_simulate because specs also install fake ngx
tables that lack the simulate marker (stub_queries uses
{ null: nil }).
Errors
- Without a backend both modules still load. The function that needs
the backend raises an error that includes why luaossl failed to load,
so a broken luaossl install isn't hidden:
lapis.util.encoding: hmac_sha256 requires luaossl or
lua-resty-openssl, but luaossl failed to load: module
'openssl.hmac' not found: ...
- luaossl is probed once per module. On Lua 5.1 and LuaJIT, when a
module's loader raises an error, require leaves a sentinel in
package.loaded, and a second require only reports "loop or previous
error loading module" instead of the real reason.
Fixed from the prototype patch in the issue 802 plan: random_bytes
returned assert's message as a second value. ngx.encode_base64 rejects
it ("bad no_padding: boolean expected, got string", so CSRF tokens
failed on OpenResty without luaossl) and LuaSocket's mime.b64 appends
it to the key. random_bytes now returns only the bytes, and the specs
check the exact key.
Not changed
- luaossl stays a dependency in the rockspec. Outside OpenResty,
`lapis migrate` (pgmoon's password auth), sessions and CSRF still
need it.
- The simulated ngx.md5 in lapis.spec.request still loads luaossl
lazily when it's called.
Specs
- spec/helpers.moon: require_with_stubs loads a fresh copy of a module
with chosen modules replaced by tables, or made to fail as if not
installed, then restores package.loaded and package.preload.
- encoding: RFC 2202 and RFC 4231 HMAC test vectors, backend order, the
error without a backend, and nginx with and without lua-resty-openssl,
including hmac_digest = "sha256".
- csrf: backend order, strong bytes requested from resty.random, a
failing resty.random, no resty.random outside nginx or in a simulated
request, and validating tokens without a backend.
Docs: utilities.md now says which library backs the HMAC functions and
the CSRF random bytes.
Tested
- busted spec: 1026 successes, 5 pending on Lua 5.1; 1027 successes,
4 pending on LuaJIT 2.1 (OpenResty's branch) and Lua 5.4. Before this
change: 1013 successes, 5 pending on Lua 5.1.
- busted spec_cqueues: 13/13 on Lua 5.1 with lua-http 0.4.
- busted spec_openresty/server_spec.moon against Ubuntu 24.04's nginx
1.24 with ngx_lua 0.10.26, lua-resty-core and lua-resty-string 0.16:
10/10 with luaossl, and 10/10 with luaossl hidden from nginx. Without
this change the second run is 0/10.
- A throwaway app on that nginx with luaossl hidden: the app loads, sha1
sessions round trip, CSRF tokens come from resty.random (32 bytes,
padded base64) and are accepted, and hmac_sha256 raises the error
above. With lua-resty-openssl 1.9.0 added, hmac_digest = "sha256"
sessions round trip and hmac_sha256 matches RFC 4231. With luaossl
visible, luaossl is used and no resty module is loaded.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tEB9iu1sFMjM17UjVE6Tv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related to #802
lapis.util.encoding and lapis.csrf required luaossl as soon as they were loaded. Every app loads lapis.util.encoding (lapis.application -> lapis.request -> lapis.session), so an OpenResty app could not start without luaossl, even though OpenResty signs sessions with ngx.hmac_sha1 and ships a CSPRNG in resty.random. Every request failed with:
This is the runtime half of leafo/lapis issue 802, where luaossl fails to compile with GCC 15+ (C23). Each module now picks a backend when it loads, the way pgmoon does in pgmoon/crypto.moon. luaossl is first in every chain, so nothing changes for installs that have it.
lapis.util.encoding
lapis.csrf
Errors
Without a backend both modules still load. The function that needs the backend raises an error that includes why luaossl failed to load, so a broken luaossl install isn't hidden:
lapis.util.encoding: hmac_sha256 requires luaossl or
lua-resty-openssl, but luaossl failed to load: module
'openssl.hmac' not found: ...
luaossl is probed once per module. On Lua 5.1 and LuaJIT, when a module's loader raises an error, require leaves a sentinel in package.loaded, and a second require only reports "loop or previous error loading module" instead of the real reason.
Fixed from the prototype patch in the issue 802 plan: random_bytes returned assert's message as a second value. ngx.encode_base64 rejects it ("bad no_padding: boolean expected, got string", so CSRF tokens failed on OpenResty without luaossl) and LuaSocket's mime.b64 appends it to the key. random_bytes now returns only the bytes, and the specs check the exact key.
Not changed
lapis migrate(pgmoon's password auth), sessions and CSRF still need it.Specs
Docs: utilities.md now says which library backs the HMAC functions and the CSRF random bytes.
Tested